diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index a544b3948e5..54226155cf8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -3697,7 +3697,7 @@ public class Lower extends TreeTranslator { vardefinit).setType(tree.var.type); indexDef.sym = tree.var.sym; JCBlock body = make.Block(0, List.of(indexDef, tree.body)); - body.endpos = TreeInfo.endPos(tree.body); + body.bracePos = TreeInfo.endPos(tree.body); result = translate(make. ForLoop(List.of(init), cond, @@ -4158,7 +4158,7 @@ public class Lower extends TreeTranslator { stmtList.append(switch2); JCBlock res = make.Block(0L, stmtList.toList()); - res.endpos = TreeInfo.endPos(tree); + res.bracePos = TreeInfo.endPos(tree); return res; } else { JCSwitchExpression switch2 = make.SwitchExpression(make.Ident(dollar_tmp), lb.toList()); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index 725c7f8063b..14c5420c7c0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -500,7 +500,7 @@ public class Gen extends JCTree.Visitor { c.members().enter(clinit); List clinitStats = clinitCode.toList(); JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats); - block.endpos = TreeInfo.endPos(clinitStats.last()); + block.bracePos = TreeInfo.endPos(clinitStats.last()); methodDefs.append(make.MethodDef(clinit, block)); if (!clinitTAs.isEmpty()) @@ -553,8 +553,8 @@ public class Gen extends JCTree.Visitor { // Find the super() invocation and append the given initializer code. TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initCode.prepend(supercall))); - if (md.body.endpos == Position.NOPOS) - md.body.endpos = TreeInfo.endPos(md.body.stats.last()); + if (md.body.bracePos == Position.NOPOS) + md.body.bracePos = TreeInfo.endPos(md.body.stats.last()); md.sym.appendUniqueTypeAttributes(initTAs); } @@ -1121,7 +1121,7 @@ public class Gen extends JCTree.Visitor { genStats(tree.stats, localEnv); // End the scope of all block-local variables in variable info. if (!env.tree.hasTag(METHODDEF)) { - code.statBegin(tree.endpos); + code.statBegin(tree.bracePos); code.endScopes(limit); code.pendingStatPos = Position.NOPOS; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 1383baa57b8..7ebcd0c844b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -231,8 +231,8 @@ public class JavacParser implements Parser { protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) { return keepEndPositions - ? new SimpleEndPosTable(this) - : new EmptyEndPosTable(this); + ? new SimpleEndPosTable() + : new MinimalEndPosTable(); } protected DocCommentTable newDocCommentTable(boolean keepDocComments, ParserFactory fac) { @@ -633,12 +633,14 @@ public class JavacParser implements Parser { * * @param tree The tree to be used as index in the hashtable * @param dc The doc comment to associate with the tree, or null. + * @return {@code tree} */ - protected void attach(JCTree tree, Comment dc) { + protected T attach(T tree, Comment dc) { if (keepDocComments && dc != null) { docComments.putComment(tree, dc); } reportDanglingComments(tree, dc); + return tree; } /** Reports all dangling comments associated with the @@ -702,16 +704,35 @@ public class JavacParser implements Parser { endPosTable.setErrorEndPos(errPos); } - protected void storeEnd(JCTree tree, int endpos) { - endPosTable.storeEnd(tree, endpos); + /** + * Store ending position for a tree, the value of which is the greater of + * last error position in {@link #endPosTable} and the given ending position. + * @param tree tree node + * @param endpos the ending position to associate with {@code tree} + * @return {@code tree} + */ + protected T storeEnd(T tree, int endpos) { + return endPosTable.storeEnd(tree, endpos); } - protected T to(T t) { - return endPosTable.to(t); + /** + * Store current token's ending position for a tree, the value of which + * will be the greater of last error position in {@link #endPosTable} + * and the ending position of the current token. + * @param tree tree node + */ + protected T to(T tree) { + return storeEnd(tree, token.endPos); } - protected T toP(T t) { - return endPosTable.toP(t); + /** + * Store current token's ending position for a tree, the value of which + * will be the greater of last error position in {@link #endPosTable} + * and the ending position of the previous token. + * @param tree tree node + */ + protected T toP(T tree) { + return storeEnd(tree, S.prevToken().endPos); } /** Get the start position for a tree node. The start position is @@ -1741,7 +1762,7 @@ public class JavacParser implements Parser { case RBRACE: case EOF: JCSwitchExpression e = to(F.at(switchPos).SwitchExpression(selector, cases.toList())); - e.endpos = token.pos; + e.bracePos = token.pos; accept(RBRACE); return e; default: @@ -2819,9 +2840,9 @@ public class JavacParser implements Parser { syntaxError(token.pos, Errors.Orphaned(token.kind)); switchBlockStatementGroups(); } - // the Block node has a field "endpos" for first char of last token, which is + // the Block node has a field "bracePos" for first char of last token, which is // usually but not necessarily the last char of the last token. - t.endpos = token.pos; + t.bracePos = token.pos; accept(RBRACE); return toP(t); } @@ -3142,7 +3163,7 @@ public class JavacParser implements Parser { accept(LBRACE); List cases = switchBlockStatementGroups(); JCSwitch t = to(F.at(pos).Switch(selector, cases)); - t.endpos = token.endPos; + t.bracePos = token.endPos; accept(RBRACE); return t; } @@ -3658,9 +3679,7 @@ public class JavacParser implements Parser { } else { throw new AssertionError("Unhandled annotation kind: " + kind); } - - storeEnd(ann, S.prevToken().endPos); - return ann; + return toP(ann); } List annotationFieldValuesOpt() { @@ -3835,9 +3854,8 @@ public class JavacParser implements Parser { } } JCVariableDecl result = toP(F.at(pos).VarDef(mods, name, type, init, declaredUsingVar)); - attach(result, dc); result.startPos = startPos; - return result; + return attach(result, dc); } Name restrictedTypeName(JCExpression e, boolean shouldWarn) { @@ -4131,7 +4149,7 @@ public class JavacParser implements Parser { firstTypeDecl = false; } } - List topLevelDefs = isImplicitClass ? constructImplicitClass(defs.toList()) : defs.toList(); + List topLevelDefs = isImplicitClass ? constructImplicitClass(defs.toList(), S.prevToken().endPos) : defs.toList(); JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(topLevelDefs); if (!consumedToplevelDoc) attach(toplevel, firstToken.docComment()); @@ -4141,13 +4159,12 @@ public class JavacParser implements Parser { toplevel.docComments = docComments; if (keepLineMap) toplevel.lineMap = S.getLineMap(); - this.endPosTable.setParser(null); // remove reference to parser toplevel.endPositions = this.endPosTable; return toplevel; } // Restructure top level to be an implicitly declared class. - private List constructImplicitClass(List origDefs) { + private List constructImplicitClass(List origDefs, int endPos) { ListBuffer topDefs = new ListBuffer<>(); ListBuffer defs = new ListBuffer<>(); @@ -4177,6 +4194,7 @@ public class JavacParser implements Parser { JCClassDecl implicit = F.at(primaryPos).ClassDef( implicitMods, name, List.nil(), null, List.nil(), List.nil(), defs.toList()); + storeEnd(implicit, endPos); topDefs.append(implicit); return topDefs.toList(); } @@ -4192,11 +4210,12 @@ public class JavacParser implements Parser { accept(LBRACE); directives = moduleDirectiveList(); accept(RBRACE); + int endPos = S.prevToken().endPos; accept(EOF); - JCModuleDecl result = toP(F.at(pos).ModuleDef(mods, kind, name, directives)); - attach(result, dc); - return result; + JCModuleDecl result = F.at(pos).ModuleDef(mods, kind, name, directives); + storeEnd(result, endPos); + return attach(result, dc); } List moduleDirectiveList() { @@ -4386,8 +4405,7 @@ public class JavacParser implements Parser { List defs = classInterfaceOrRecordBody(name, false, false); JCClassDecl result = toP(F.at(pos).ClassDef( mods, name, typarams, extending, implementing, permitting, defs)); - attach(result, dc); - return result; + return attach(result, dc); } protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) { @@ -4434,8 +4452,7 @@ public class JavacParser implements Parser { defs = defs.prepend(field); } JCClassDecl result = toP(F.at(pos).ClassDef(mods, name, typarams, null, implementing, defs)); - attach(result, dc); - return result; + return attach(result, dc); } Name typeName() { @@ -4474,8 +4491,7 @@ public class JavacParser implements Parser { defs = classInterfaceOrRecordBody(name, true, false); JCClassDecl result = toP(F.at(pos).ClassDef( mods, name, typarams, null, extending, permitting, defs)); - attach(result, dc); - return result; + return attach(result, dc); } List permitsClause(JCModifiers mods, String classOrInterface) { @@ -4522,8 +4538,7 @@ public class JavacParser implements Parser { JCClassDecl result = toP(F.at(pos). ClassDef(mods, name, List.nil(), null, implementing, defs)); - attach(result, dc); - return result; + return attach(result, dc); } /** EnumBody = "{" { EnumeratorDeclarationList } [","] @@ -4664,8 +4679,7 @@ public class JavacParser implements Parser { storeEnd(create, S.prevToken().endPos); ident = F.at(identPos).Ident(enumName); JCTree result = toP(F.at(pos).VarDef(mods, name, ident, create)); - attach(result, dc); - return result; + return attach(result, dc); } /** TypeList = Type {"," Type} @@ -5097,8 +5111,7 @@ public class JavacParser implements Parser { toP(F.at(pos).MethodDef(mods, name, type, typarams, receiverParam, params, thrown, body, defaultValue)); - attach(result, dc); - return result; + return attach(result, dc); } finally { this.receiverParam = prevReceiverParam; } @@ -5395,8 +5408,7 @@ public class JavacParser implements Parser { return mostInnerTypeToReturn; } else { mostInnerArrayType.elemtype = mostInnerTypeToReturn; - storeEnd(type, origEndPos); - return type; + return storeEnd(type, origEndPos); } } @@ -5622,121 +5634,69 @@ public class JavacParser implements Parser { } } - /* - * a functional source tree and end position mappings + /** + * A straightforward {@link EndPosTable} implementation. */ protected static class SimpleEndPosTable extends AbstractEndPosTable { - private final IntHashTable endPosMap; + private final IntHashTable endPosMap = new IntHashTable(); - SimpleEndPosTable(JavacParser parser) { - super(parser); - endPosMap = new IntHashTable(); - } - - public void storeEnd(JCTree tree, int endpos) { - endPosMap.put(tree, errorEndPos > endpos ? errorEndPos : endpos); - } - - protected T to(T t) { - storeEnd(t, parser.token.endPos); - return t; - } - - protected T toP(T t) { - storeEnd(t, parser.S.prevToken().endPos); - return t; + @Override + public T storeEnd(T tree, int endpos) { + endPosMap.put(tree, Math.max(endpos, errorEndPos)); + return tree; } + @Override public int getEndPos(JCTree tree) { int value = endPosMap.get(tree); // As long as Position.NOPOS==-1, this just returns value. return (value == -1) ? Position.NOPOS : value; } + @Override public int replaceTree(JCTree oldTree, JCTree newTree) { int pos = endPosMap.remove(oldTree); - if (pos != -1) { + if (pos != -1 && newTree != null) { storeEnd(newTree, pos); - return pos; } - return Position.NOPOS; + return pos; } } - /* - * a default skeletal implementation without any mapping overhead. + /** + * A minimal implementation that only stores what's required. */ - protected static class EmptyEndPosTable extends AbstractEndPosTable { + protected static class MinimalEndPosTable extends SimpleEndPosTable { - EmptyEndPosTable(JavacParser parser) { - super(parser); + @Override + public T storeEnd(T tree, int endpos) { + switch (tree.getTag()) { + case MODULEDEF: + case PACKAGEDEF: + case CLASSDEF: + case METHODDEF: + case VARDEF: + break; + default: + return tree; + } + return super.storeEnd(tree, endpos); } - - public void storeEnd(JCTree tree, int endpos) { /* empty */ } - - protected T to(T t) { - return t; - } - - protected T toP(T t) { - return t; - } - - public int getEndPos(JCTree tree) { - return Position.NOPOS; - } - - public int replaceTree(JCTree oldTree, JCTree newTree) { - return Position.NOPOS; - } - } protected abstract static class AbstractEndPosTable implements EndPosTable { - /** - * The current parser. - */ - protected JavacParser parser; /** * Store the last error position. */ public int errorEndPos = Position.NOPOS; - public AbstractEndPosTable(JavacParser parser) { - this.parser = parser; - } - - /** - * Store current token's ending position for a tree, the value of which - * will be the greater of last error position and the ending position of - * the current token. - * @param t The tree. - */ - protected abstract T to(T t); - - /** - * Store current token's ending position for a tree, the value of which - * will be the greater of last error position and the ending position of - * the previous token. - * @param t The tree. - */ - protected abstract T toP(T t); - - /** - * Set the error position during the parsing phases, the value of which - * will be set only if it is greater than the last stored error position. - * @param errPos The error position - */ + @Override public void setErrorEndPos(int errPos) { if (errPos > errorEndPos) { errorEndPos = errPos; } } - - public void setParser(JavacParser parser) { - this.parser = parser; - } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java index d68ba838ba1..83fe402c0a7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, 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 @@ -25,8 +25,21 @@ package com.sun.tools.javac.tree; +import com.sun.tools.javac.util.Position; + /** * Specifies the methods to access a mappings of syntax trees to end positions. + * + *

+ * Implementations must store end positions for at least these node types: + *

    + *
  • {@link JCTree.JCModuleDecl} + *
  • {@link JCTree.JCPackageDecl} + *
  • {@link JCTree.JCClassDecl} + *
  • {@link JCTree.JCMethodDecl} + *
  • {@link JCTree.JCVariableDecl} + *
+ * *

This is NOT part of any supported API. * If you write code that depends on this, you do so at your own * risk. This code and its internal interfaces are subject to change @@ -40,23 +53,31 @@ public interface EndPosTable { * @param tree JCTree * @return position of the source tree or Positions.NOPOS for non-existent mapping */ - public int getEndPos(JCTree tree); + int getEndPos(JCTree tree); /** * Store ending position for a tree, the value of which is the greater of * last error position and the given ending position. * @param tree The tree. * @param endpos The ending position to associate with the tree. + * @return the {@code tree} */ - public abstract void storeEnd(JCTree tree, int endpos); + T storeEnd(T tree, int endpos); + + /** + * Set the error position during the parsing phases, the value of which + * will be set only if it is greater than the last stored error position. + * @param errPos The error position + */ + void setErrorEndPos(int errPos); /** * Give an old tree and a new tree, the old tree will be replaced with * the new tree, the position of the new tree will be that of the old * tree. * @param oldtree a JCTree to be replaced - * @param newtree a JCTree to be replaced with + * @param newtree a JCTree to be replaced with, or null to just remove {@code oldtree} * @return position of the old tree or Positions.NOPOS for non-existent mapping */ - public int replaceTree(JCTree oldtree, JCTree newtree); + int replaceTree(JCTree oldtree, JCTree newtree); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index de86b7e2c57..debc10c9ff8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, 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 @@ -1121,7 +1121,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { /** statements */ public List stats; /** Position of closing brace, optional. */ - public int endpos = Position.NOPOS; + public int bracePos = Position.NOPOS; /** If this block contains record pattern, it is necessary to catch * exceptions from the deconstructors and wrap them. * The {@code patternMatchingCatch} keeps the list of the deconstructor @@ -1330,7 +1330,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public JCExpression selector; public List cases; /** Position of closing brace, optional. */ - public int endpos = Position.NOPOS; + public int bracePos = Position.NOPOS; public boolean hasUnconditionalPattern; public boolean isExhaustive; public boolean patternSwitch; @@ -1430,7 +1430,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public JCExpression selector; public List cases; /** Position of closing brace, optional. */ - public int endpos = Position.NOPOS; + public int bracePos = Position.NOPOS; public boolean hasUnconditionalPattern; public boolean isExhaustive; public boolean patternSwitch; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 5e3b043fb11..ae946c5b6d6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, 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 @@ -500,12 +500,12 @@ public class TreeInfo { return tree.pos; } - /** The end position of given tree, if it is a block with - * defined endpos. + /** The closing brace position of given tree, if it is a block with + * defined bracePos. */ public static int endPos(JCTree tree) { - if (tree.hasTag(BLOCK) && ((JCBlock) tree).endpos != Position.NOPOS) - return ((JCBlock) tree).endpos; + if (tree.hasTag(BLOCK) && ((JCBlock) tree).bracePos != Position.NOPOS) + return ((JCBlock) tree).bracePos; else if (tree.hasTag(SYNCHRONIZED)) return endPos(((JCSynchronized) tree).body); else if (tree.hasTag(TRY)) { @@ -513,11 +513,11 @@ public class TreeInfo { return endPos((t.finalizer != null) ? t.finalizer : (t.catchers.nonEmpty() ? t.catchers.last().body : t.body)); } else if (tree.hasTag(SWITCH) && - ((JCSwitch) tree).endpos != Position.NOPOS) { - return ((JCSwitch) tree).endpos; + ((JCSwitch) tree).bracePos != Position.NOPOS) { + return ((JCSwitch) tree).bracePos; } else if (tree.hasTag(SWITCH_EXPRESSION) && - ((JCSwitchExpression) tree).endpos != Position.NOPOS) { - return ((JCSwitchExpression) tree).endpos; + ((JCSwitchExpression) tree).bracePos != Position.NOPOS) { + return ((JCSwitchExpression) tree).bracePos; } else return tree.pos; } @@ -646,11 +646,6 @@ public class TreeInfo { if (tree == null) return Position.NOPOS; - if (endPosTable == null) { - // fall back on limited info in the tree - return endPos(tree); - } - int mapPos = endPosTable.getEndPos(tree); if (mapPos != Position.NOPOS) return mapPos; @@ -731,8 +726,8 @@ public class TreeInfo { /** A DiagnosticPosition with the preferred position set to the - * end position of given tree, if it is a block with - * defined endpos. + * closing brace position of given tree, if it is a block with + * defined closing brace position. */ public static DiagnosticPosition diagEndPos(final JCTree tree) { final int endPos = TreeInfo.endPos(tree); diff --git a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java index 2968bc3e173..0219fa0eaf8 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -141,7 +141,6 @@ class ReplParser extends JavacParser { storeEnd(toplevel, S.prevToken().endPos); } toplevel.lineMap = S.getLineMap(); - this.endPosTable.setParser(null); // remove reference to parser toplevel.endPositions = this.endPosTable; return toplevel; } diff --git a/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java b/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java index ff3f2cf3200..95a9ef2dd53 100644 --- a/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java +++ b/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, 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 @@ -151,7 +151,7 @@ public class MissingLNTEntryForFinalizerTest { com.sun.tools.javac.code.Type result = super.attribStat(tree, env); if (tree.hasTag(TRY)) { JCTry tryTree = (JCTry)tree; - lineNumber = env.toplevel.lineMap.getLineNumber(tryTree.finalizer.endpos); + lineNumber = env.toplevel.lineMap.getLineNumber(tryTree.finalizer.bracePos); } return result; } diff --git a/test/langtools/tools/javac/parser/DeclarationEndPositions.java b/test/langtools/tools/javac/parser/DeclarationEndPositions.java new file mode 100644 index 00000000000..473d6bc2712 --- /dev/null +++ b/test/langtools/tools/javac/parser/DeclarationEndPositions.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025, 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 8350212 + * @summary Verify ending source positions are calculated for declarations supporting SuppressWarnings + * @modules jdk.compiler/com.sun.tools.javac.tree + * @run main DeclarationEndPositions + */ + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TreeScanner; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.TreeInfo; + +import java.io.IOException; +import java.net.URI; +import java.util.List; + +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +public class DeclarationEndPositions { + + public static void checkEndPosition(Class nodeType, String input, String marker) throws IOException { + + // Create source + var source = new SimpleJavaFileObject(URI.create("file://T.java"), JavaFileObject.Kind.SOURCE) { + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return input; + } + }; + + // Parse source + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, List.of(), List.of(), List.of(source)); + Iterable units = ((JavacTask)task).parse(); + + // Find node and check end position + JCTree.JCCompilationUnit unit = (JCTree.JCCompilationUnit)units.iterator().next(); + unit.accept(new TreeScanner() { + @Override + public Void scan(Tree node, Void aVoid) { + if (nodeType.isInstance(node)) { + JCTree tree = (JCTree)node; + int actual = TreeInfo.getEndPos(tree, unit.endPositions); + int expected = marker.indexOf('^') + 1; + if (actual != expected) { + throw new AssertionError(String.format( + "wrong end pos %d != %d for \"%s\" @ %d", actual, expected, input, tree.pos)); + } + } + return super.scan(node, aVoid); + } + }, null); + } + + public static void main(String... args) throws Exception { + + // JCModuleDecl + checkEndPosition(JCModuleDecl.class, + "/* comment */ module fred { /* comment */ } /* comment */", + " ^ "); + + // JCPackageDecl + checkEndPosition(JCPackageDecl.class, + "/* comment */ package fred; /* comment */", + " ^ "); + + // JCClassDecl + checkEndPosition(JCClassDecl.class, + "/* comment */ class Fred { /* comment */ } /* comment */", + " ^ "); + + // JCMethodDecl + checkEndPosition(JCMethodDecl.class, + "/* comment */ class Fred { void m() { /* comment */ } } /* comment */", + " ^ "); + + // JCVariableDecl + checkEndPosition(JCVariableDecl.class, + "/* comment */ class Fred { int x; } /* comment */", + " ^ "); + checkEndPosition(JCVariableDecl.class, + "/* comment */ class Fred { int x = 123; } /* comment */", + " ^ "); + checkEndPosition(JCVariableDecl.class, + "/* comment */ class A { try {} catch (Error err) {} } /* comment */", + " ^ "); + } +} diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index 6a2850fc8dd..9c6ad617132 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -2298,10 +2298,9 @@ public class JavacParserTest extends TestCase { @Test //JDK-8310326 void testUnnamedClassPositions() throws IOException { - String code = """ - void main() { - } - """; + // 0 1 2 + // 012345678901234567890 + String code = "void main() { }"; DiagnosticCollector coll = new DiagnosticCollector<>(); JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null, @@ -2313,7 +2312,7 @@ public class JavacParserTest extends TestCase { @Override public Void visitClass(ClassTree node, Void p) { assertEquals("Wrong start position", 0, sp.getStartPosition(cut, node)); - assertEquals("Wrong end position", -1, sp.getEndPosition(cut, node)); + assertEquals("Wrong end position", 15, sp.getEndPosition(cut, node)); assertEquals("Wrong modifiers start position", -1, sp.getStartPosition(cut, node.getModifiers())); assertEquals("Wrong modifiers end position", -1, sp.getEndPosition(cut, node.getModifiers())); return super.visitClass(node, p); diff --git a/test/langtools/tools/javac/parser/extend/TrialParser.java b/test/langtools/tools/javac/parser/extend/TrialParser.java index 539676a90fb..c9a858fbc47 100644 --- a/test/langtools/tools/javac/parser/extend/TrialParser.java +++ b/test/langtools/tools/javac/parser/extend/TrialParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, 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 @@ -139,7 +139,6 @@ class TrialParser extends JavacParser { storeEnd(toplevel, S.prevToken().endPos); } toplevel.lineMap = S.getLineMap(); - this.endPosTable.setParser(null); // remove reference to parser toplevel.endPositions = this.endPosTable; return toplevel; }