8350212: Track source end positions of declarations that support @SuppressWarnings

Co-authored-by: Jan Lahoda <jlahoda@openjdk.org>
Reviewed-by: mcimadamore
This commit is contained in:
Archie Cobbs 2025-06-05 21:57:33 +00:00
parent 15178aa298
commit c793de989f
11 changed files with 252 additions and 160 deletions

View File

@ -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());

View File

@ -500,7 +500,7 @@ public class Gen extends JCTree.Visitor {
c.members().enter(clinit);
List<JCStatement> 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;
}

View File

@ -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 extends JCTree> 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 extends JCTree> T storeEnd(T tree, int endpos) {
return endPosTable.storeEnd(tree, endpos);
}
protected <T extends JCTree> 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 extends JCTree> T to(T tree) {
return storeEnd(tree, token.endPos);
}
protected <T extends JCTree> 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 extends JCTree> 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<JCCase> 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<JCExpression> 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<JCTree> topLevelDefs = isImplicitClass ? constructImplicitClass(defs.toList()) : defs.toList();
List<JCTree> 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<JCTree> constructImplicitClass(List<JCTree> origDefs) {
private List<JCTree> constructImplicitClass(List<JCTree> origDefs, int endPos) {
ListBuffer<JCTree> topDefs = new ListBuffer<>();
ListBuffer<JCTree> 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<JCDirective> moduleDirectiveList() {
@ -4386,8 +4405,7 @@ public class JavacParser implements Parser {
List<JCTree> 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<JCExpression> 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 extends JCTree> T to(T t) {
storeEnd(t, parser.token.endPos);
return t;
}
protected <T extends JCTree> T toP(T t) {
storeEnd(t, parser.S.prevToken().endPos);
return t;
@Override
public <T extends JCTree> 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 extends JCTree> 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 extends JCTree> T to(T t) {
return t;
}
protected <T extends JCTree> 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 extends JCTree> 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 extends JCTree> 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;
}
}
}

View File

@ -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.
*
* <p>
* Implementations <b>must</b> store end positions for at least these node types:
* <ul>
* <li>{@link JCTree.JCModuleDecl}
* <li>{@link JCTree.JCPackageDecl}
* <li>{@link JCTree.JCClassDecl}
* <li>{@link JCTree.JCMethodDecl}
* <li>{@link JCTree.JCVariableDecl}
* </ul>
*
* <p><b>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 extends JCTree> 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);
}

View File

@ -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<JCStatement> 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<JCCase> 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<JCCase> 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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<? extends JCTree> 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<? extends CompilationUnitTree> units = ((JavacTask)task).parse();
// Find node and check end position
JCTree.JCCompilationUnit unit = (JCTree.JCCompilationUnit)units.iterator().next();
unit.accept(new TreeScanner<Void, Void>() {
@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 */",
" ^ ");
}
}

View File

@ -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<JavaFileObject> 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);

View File

@ -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;
}