Track source end positions of declarations that support @SuppressWarnings.

This commit is contained in:
Archie L. Cobbs 2025-02-17 20:38:43 -06:00
parent ba28119642
commit 4fcc61269b
5 changed files with 210 additions and 23 deletions

View File

@ -701,6 +701,27 @@ public class JavacParser implements Parser {
protected void storeEnd(JCTree tree, int endpos) {
endPosTable.storeEnd(tree, endpos);
// Module, package, class, method, and variable declarations remember their end positions
switch (tree.getTag()) {
case MODULEDEF:
((JCModuleDecl)tree).endPos = endpos;
break;
case PACKAGEDEF:
((JCPackageDecl)tree).endPos = endpos;
break;
case CLASSDEF:
((JCClassDecl)tree).endPos = endpos;
break;
case METHODDEF:
((JCMethodDecl)tree).endPos = endpos;
break;
case VARDEF:
((JCVariableDecl)tree).endPos = endpos;
break;
default:
break;
}
}
protected <T extends JCTree> T to(T t) {
@ -2761,6 +2782,7 @@ public class JavacParser implements Parser {
List<JCTree> defs = classInterfaceOrRecordBody(names.empty, false, false);
JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
body = toP(F.at(pos).AnonymousClassDef(mods, defs));
storeEnd(body, S.prevToken().endPos);
}
return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
}
@ -4018,6 +4040,7 @@ public class JavacParser implements Parser {
JCExpression pid = qualident(false);
accept(SEMI);
JCPackageDecl pd = toP(F.at(packagePos).PackageDecl(annotations, pid));
storeEnd(pd, S.prevToken().endPos);
attach(pd, firstToken.docComment());
consumedToplevelDoc = true;
defs.append(pd);
@ -4127,7 +4150,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());
@ -4143,7 +4166,7 @@ public class JavacParser implements Parser {
}
// 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<>();
@ -4173,6 +4196,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();
}
@ -4188,9 +4212,11 @@ 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));
storeEnd(result, endPos);
attach(result, dc);
return result;
}
@ -4393,6 +4419,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));
storeEnd(result, S.prevToken().endPos);
attach(result, dc);
return result;
}
@ -4416,6 +4443,7 @@ public class JavacParser implements Parser {
saveDanglingDocComments(dc);
List<JCTree> defs = classInterfaceOrRecordBody(name, false, true);
int endPos = S.prevToken().endPos;
java.util.List<JCVariableDecl> fields = new ArrayList<>();
for (JCVariableDecl field : headerFields) {
fields.add(field);
@ -4441,6 +4469,7 @@ public class JavacParser implements Parser {
defs = defs.prepend(field);
}
JCClassDecl result = toP(F.at(pos).ClassDef(mods, name, typarams, null, implementing, defs));
storeEnd(result, endPos);
attach(result, dc);
return result;
}
@ -4481,6 +4510,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));
storeEnd(result, S.prevToken().endPos);
attach(result, dc);
return result;
}
@ -4529,6 +4559,7 @@ public class JavacParser implements Parser {
JCClassDecl result = toP(F.at(pos).
ClassDef(mods, name, List.nil(),
null, implementing, defs));
storeEnd(result, S.prevToken().endPos);
attach(result, dc);
return result;
}
@ -4667,10 +4698,12 @@ public class JavacParser implements Parser {
createPos = identPos;
JCIdent ident = F.at(identPos).Ident(enumName);
JCNewClass create = F.at(createPos).NewClass(null, typeArgs, ident, args, body);
int endPos = S.prevToken().endPos;
if (createPos != identPos)
storeEnd(create, S.prevToken().endPos);
storeEnd(create, endPos);
ident = F.at(identPos).Ident(enumName);
JCTree result = toP(F.at(pos).VarDef(mods, name, ident, create));
storeEnd(result, endPos);
attach(result, dc);
return result;
}
@ -5100,6 +5133,7 @@ public class JavacParser implements Parser {
toP(F.at(pos).MethodDef(mods, name, type, typarams,
receiverParam, params, thrown,
body, defaultValue));
storeEnd(result, S.prevToken().endPos);
attach(result, dc);
return result;
} finally {

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
@ -641,6 +641,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/** The tree representing the package clause. */
public JCExpression pid;
public PackageSymbol packge;
/** Position of closing semicolon, optional. */
public int endPos = Position.NOPOS;
public JCPackageDecl(List<JCAnnotation> annotations, JCExpression pid) {
this.annotations = annotations;
this.pid = pid;
@ -837,6 +839,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public List<JCTree> defs;
/** the symbol */
public ClassSymbol sym;
/** position of closing brace, optional. */
public int endPos = Position.NOPOS;
protected JCClassDecl(JCModifiers mods,
Name name,
List<JCTypeParameter> typarams,
@ -931,6 +935,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public MethodSymbol sym;
/** does this method completes normally */
public boolean completesNormally;
/** position of closing brace or semicolon, optional. */
public int endPos = Position.NOPOS;
protected JCMethodDecl(JCModifiers mods,
Name name,
@ -1016,6 +1022,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public VarSymbol sym;
/** explicit start pos */
public int startPos = Position.NOPOS;
/** position of closing semicolon, optional. */
public int endPos = Position.NOPOS;
/** declared using `var` */
private boolean declaredUsingVar;
@ -3126,6 +3134,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public JCExpression qualId;
public List<JCDirective> directives;
public ModuleSymbol sym;
/** position of closing brace, optional. */
public int endPos = Position.NOPOS;
protected JCModuleDecl(JCModifiers mods, ModuleKind kind,
JCExpression qualId, List<JCDirective> directives) {

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,26 +500,47 @@ public class TreeInfo {
return tree.pos;
}
/** The end position of given tree, if it is a block with
* defined endpos.
/** The end position of the given tree, if defined.
*/
public static int endPos(JCTree tree) {
if (tree.hasTag(BLOCK) && ((JCBlock) tree).endpos != Position.NOPOS)
return ((JCBlock) tree).endpos;
else if (tree.hasTag(SYNCHRONIZED))
int endPos;
switch (tree.getTag()) {
case BLOCK:
endPos = ((JCBlock) tree).endpos;
break;
case SYNCHRONIZED:
return endPos(((JCSynchronized) tree).body);
else if (tree.hasTag(TRY)) {
case TRY:
JCTry t = (JCTry) tree;
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;
} else if (tree.hasTag(SWITCH_EXPRESSION) &&
((JCSwitchExpression) tree).endpos != Position.NOPOS) {
return ((JCSwitchExpression) tree).endpos;
} else
case SWITCH:
endPos = ((JCSwitch) tree).endpos;
break;
case SWITCH_EXPRESSION:
endPos = ((JCSwitchExpression) tree).endpos;
break;
case MODULEDEF:
endPos = ((JCModuleDecl) tree).endPos;
break;
case PACKAGEDEF:
endPos = ((JCPackageDecl) tree).endPos;
break;
case CLASSDEF:
endPos = ((JCClassDecl) tree).endPos;
break;
case METHODDEF:
endPos = ((JCMethodDecl) tree).endPos;
break;
case VARDEF:
endPos = ((JCVariableDecl) tree).endPos;
break;
default:
return tree.pos;
}
if (endPos != Position.NOPOS)
return endPos;
return tree.pos;
}

View File

@ -0,0 +1,123 @@
/*
* 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.endPos(tree);
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,
// 0 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890123456789
"/* comment */ module fred { /* comment */ } /* comment */",
" ^ ");
// JCPackageDecl
checkEndPosition(JCPackageDecl.class,
// 0 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890123456789
"/* comment */ package fred; /* comment */",
" ^ ");
// JCClassDecl
checkEndPosition(JCClassDecl.class,
// 0 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890123456789
"/* comment */ class Fred { /* comment */ } /* comment */",
" ^ ");
// JCMethodDecl
checkEndPosition(JCMethodDecl.class,
// 0 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890123456789
"/* comment */ class Fred { void m() { /* comment */ } } /* comment */",
" ^ ");
// JCVariableDecl
checkEndPosition(JCVariableDecl.class,
// 0 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890123456789
"/* comment */ class Fred { int x = 123; } /* 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, List.of("--enable-preview", "--source", System.getProperty("java.specification.version")),
@ -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);