Skip to content

Commit 43af7b5

Browse files
committed
8371309: Diagnostic.getEndPosition can throw an NPE with typical broken code
Reviewed-by: vromero
1 parent 3f47e57 commit 43af7b5

File tree

3 files changed

+231
-2
lines changed

3 files changed

+231
-2
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ public JCTree getTree() {
505505

506506
// for default DiagnosticPosition
507507
public int getStartPosition() {
508-
return TreeInfo.getStartPos(this);
508+
return noNoPos(TreeInfo.getStartPos(this));
509509
}
510510

511511
// for default DiagnosticPosition
@@ -515,7 +515,14 @@ public int getPreferredPosition() {
515515

516516
// for default DiagnosticPosition
517517
public int getEndPosition(EndPosTable endPosTable) {
518-
return TreeInfo.getEndPos(this, endPosTable);
518+
return noNoPos(TreeInfo.getEndPos(this, endPosTable));
519+
}
520+
521+
private int noNoPos(int position) {
522+
if (position == JCDiagnostic.NOPOS) {
523+
return pos;
524+
}
525+
return position;
519526
}
520527

521528
/**

src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,11 @@ public static int getEndPos(JCTree tree, EndPosTable endPosTable) {
651651
if (tree == null)
652652
return Position.NOPOS;
653653

654+
if (endPosTable == null) {
655+
// fall back on limited info in the tree
656+
return endPos(tree);
657+
}
658+
654659
int mapPos = endPosTable.getEndPos(tree);
655660
if (mapPos != Position.NOPOS)
656661
return mapPos;
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8371309
27+
* @summary Verify that Diagnostic.getEndPosition works reasonably
28+
* @library /tools/lib
29+
* @modules jdk.compiler/com.sun.tools.javac.api
30+
* jdk.compiler/com.sun.tools.javac.main
31+
* @run junit DiagnosticGetEndPosition
32+
*/
33+
import java.net.URI;
34+
import java.nio.file.Path;
35+
import java.nio.file.Paths;
36+
import java.util.List;
37+
import javax.tools.JavaCompiler;
38+
import javax.tools.JavaFileObject;
39+
import javax.tools.SimpleJavaFileObject;
40+
import javax.tools.ToolProvider;
41+
42+
import com.sun.source.util.JavacTask;
43+
44+
import org.junit.jupiter.api.Test;
45+
46+
import toolbox.ToolBox;
47+
48+
import static org.junit.jupiter.api.Assertions.*;
49+
50+
public class DiagnosticGetEndPosition {
51+
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
52+
final ToolBox tb = new ToolBox();
53+
54+
@Test
55+
public void testGetEndPositionDuringParsing() {
56+
JavaFileObject testFile =
57+
SimpleJavaFileObject.forSource(URI.create("mem:///Test.java"),
58+
"""
59+
public class Test extends {}
60+
""");
61+
compiler.getTask(
62+
null,
63+
null,
64+
diagnostic -> assertEquals(26, diagnostic.getEndPosition()),
65+
null,
66+
null,
67+
List.of(testFile)
68+
).call();
69+
}
70+
71+
@Test
72+
public void testNoErrorsFromInternalParses() throws Exception {
73+
Path base = Paths.get(".");
74+
Path src = base.resolve("src");
75+
tb.writeJavaFiles(src,
76+
"""
77+
module m {
78+
exports test;
79+
}
80+
""",
81+
"""
82+
package test;
83+
public class Test extends {}
84+
""");
85+
86+
try (var fm = compiler.getStandardFileManager(null, null, null)) {
87+
compiler.getTask(
88+
null,
89+
null,
90+
d -> fail(d.toString()),
91+
List.of("-sourcepath", src.toString()),
92+
null,
93+
fm.getJavaFileObjects(src.resolve("module-info.java"))
94+
).call();
95+
}
96+
}
97+
98+
@Test
99+
public void testGetEndPositionWorkForImplicitParse() throws Exception {
100+
Path base = Paths.get(".");
101+
Path src = base.resolve("src");
102+
String implCode = """
103+
package test;
104+
public class Impl {
105+
public static final int C = 1 / 0;
106+
}
107+
""";
108+
tb.writeJavaFiles(src,
109+
implCode,
110+
"""
111+
package test;
112+
public class Test {
113+
Impl i; //force parsing of Impl
114+
}
115+
""");
116+
117+
try (var fm = compiler.getStandardFileManager(null, null, null)) {
118+
compiler.getTask(
119+
null,
120+
null,
121+
d -> assertEquals("", //ideally would be "0", but the positions are not fully set yet
122+
implCode.substring((int) d.getStartPosition(),
123+
(int) d.getEndPosition())),
124+
List.of("-sourcepath", src.toString(), "-Xlint:divzero"),
125+
null,
126+
fm.getJavaFileObjects(src.resolve("test").resolve("Test.java"))
127+
).call();
128+
}
129+
}
130+
131+
@Test
132+
public void testWronglyNamedClass() throws Exception {
133+
Path base = Paths.get(".");
134+
Path src = base.resolve("src");
135+
tb.writeFile(src.resolve("test").resolve("WronglyNamed.java"),
136+
"""
137+
package test;
138+
class SomeOtherName {}
139+
""");
140+
tb.writeJavaFiles(src,
141+
"""
142+
package test;
143+
public class Test {
144+
WronglyNamed l; //parse WronglyNamed.java
145+
}
146+
""");
147+
148+
try (var fm = compiler.getStandardFileManager(null, null, null)) {
149+
compiler.getTask(
150+
null,
151+
null,
152+
null,
153+
List.of("-sourcepath", src.toString()),
154+
null,
155+
fm.getJavaFileObjects(tb.findJavaFiles(src))
156+
).call();
157+
}
158+
}
159+
160+
@Test
161+
public void testWronglyNamedPackageInfo() throws Exception {
162+
Path base = Paths.get(".");
163+
Path src = base.resolve("src");
164+
tb.writeFile(src.resolve("test").resolve("package-info.java"),
165+
"""
166+
package wrongpackage;
167+
""");
168+
tb.writeJavaFiles(src,
169+
"""
170+
package test;
171+
public class Test {
172+
}
173+
""");
174+
175+
try (var fm = compiler.getStandardFileManager(null, null, null)) {
176+
JavacTask task = (JavacTask) compiler.getTask(
177+
null,
178+
null,
179+
null,
180+
List.of("-sourcepath", src.toString()),
181+
null,
182+
fm.getJavaFileObjects(src.resolve("test").resolve("Test.java"))
183+
);
184+
task.analyze();
185+
task.getElements().getPackageElement("test").getAnnotationMirrors();
186+
}
187+
}
188+
189+
@Test
190+
public void testGetEndPositionSyntheticTree() throws Exception {
191+
Path base = Paths.get(".");
192+
Path src = base.resolve("src");
193+
String testCode = """
194+
package test;
195+
public class Test extends Base {
196+
}
197+
class Base {
198+
Base(int i) {}
199+
}
200+
""";
201+
202+
tb.writeJavaFiles(src, testCode);
203+
204+
try (var fm = compiler.getStandardFileManager(null, null, null)) {
205+
compiler.getTask(
206+
null,
207+
null,
208+
d -> assertEquals("",
209+
testCode.substring((int) d.getStartPosition(),
210+
(int) d.getEndPosition())),
211+
List.of("-sourcepath", src.toString(), "-Xlint:divzero"),
212+
null,
213+
fm.getJavaFileObjects(src.resolve("test").resolve("Test.java"))
214+
).call();
215+
}
216+
}
217+
}

0 commit comments

Comments
 (0)