Skip to content

Commit e9b364a

Browse files
committed
Java parser: add support for unnamed classes (JEP-445)
Fixes #18584
1 parent c0eae68 commit e9b364a

File tree

8 files changed

+149
-2
lines changed

8 files changed

+149
-2
lines changed

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,27 @@ object JavaParsers {
826826
addCompanionObject(statics, cls)
827827
}
828828

829+
def unnamedClassDecl(priorTypes: List[Tree], start: Offset): List[Tree] = {
830+
val name = source.name.replaceAll("\\.java$", "").nn.toTypeName
831+
val (statics, body) = typeBodyDecls(CLASS, name, Nil)
832+
833+
val (priorStatics, priorBody) = priorTypes.partition {
834+
case t: TypeDef => t.mods.is(Flags.JavaStatic)
835+
case _: ModuleDef => true
836+
case _ => false
837+
}
838+
839+
val cls = atSpan(start, 0) {
840+
TypeDef(name, makeTemplate(
841+
parents = Nil,
842+
stats = priorBody ::: body,
843+
tparams = Nil,
844+
needsDummyConstr = true)
845+
).withMods(Modifiers(Flags.Private | Flags.Final))
846+
}
847+
addCompanionObject(priorStatics ::: statics, cls)
848+
}
849+
829850
def recordDecl(start: Offset, mods: Modifiers): List[Tree] =
830851
accept(RECORD)
831852
val nameOffset = in.offset
@@ -1067,16 +1088,37 @@ object JavaParsers {
10671088
val buf = new ListBuffer[Tree]
10681089
while (in.token == IMPORT)
10691090
buf ++= importDecl()
1091+
1092+
val afterImports = in.offset
1093+
val typesBuf = new ListBuffer[Tree]
1094+
10701095
while (in.token != EOF && in.token != RBRACE) {
10711096
while (in.token == SEMI) in.nextToken()
10721097
if (in.token != EOF) {
10731098
val start = in.offset
10741099
val mods = modifiers(inInterface = false)
10751100
adaptRecordIdentifier() // needed for typeDecl
1076-
buf ++= typeDecl(start, mods)
1101+
1102+
in.token match {
1103+
case ENUM | INTERFACE | AT | CLASS | RECORD => typesBuf ++= typeDecl(start, mods)
1104+
case _ =>
1105+
if (thisPackageName == tpnme.EMPTY_PACKAGE) {
1106+
// upon encountering non-types directly at a compilation unit level in an unnamed package,
1107+
// the entire compilation unit is treated as a JEP-445 unnamed class
1108+
//TODO support @annotated members of unnamed class
1109+
val cls = unnamedClassDecl(priorTypes = typesBuf.toList, start = afterImports)
1110+
typesBuf.clear()
1111+
typesBuf ++= cls
1112+
} else {
1113+
in.nextToken()
1114+
syntaxError(em"illegal start of type declaration", skipIt = true)
1115+
List(errorTypeTree)
1116+
}
1117+
}
10771118
}
10781119
}
1079-
val unit = atSpan(start) { PackageDef(pkg, buf.toList) }
1120+
1121+
val unit = atSpan(start) { PackageDef(pkg, (buf ++ typesBuf).toList) }
10801122
accept(EOF)
10811123
unit match
10821124
case PackageDef(Ident(nme.EMPTY_PACKAGE), Nil) => EmptyTree

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class CompilationTests {
5050
if scala.util.Properties.isJavaAtLeast("16") then
5151
tests ::= compileFilesInDir("tests/pos-java16+", defaultOptions.and("-Ysafe-init"))
5252

53+
if scala.util.Properties.isJavaAtLeast("21") then
54+
tests ::= compileFilesInDir("tests/pos-java21+", defaultOptions.withJavacOnlyOptions("--enable-preview", "--release", "21"))
55+
5356
aggregateTests(tests*).checkCompile()
5457
}
5558

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package dotty.tools.dotc.parsing
2+
3+
import dotty.tools.DottyTest
4+
import dotty.tools.dotc.ast.Trees.{Ident, PackageDef, TypeDef}
5+
import dotty.tools.dotc.ast.untpd.ModuleDef
6+
import dotty.tools.dotc.core.Contexts.{Context, ContextBase}
7+
import dotty.tools.dotc.core.StdNames.tpnme
8+
import dotty.tools.dotc.printing.{PlainPrinter, Printer}
9+
import dotty.tools.dotc.util.SourceFile
10+
import dotty.tools.io.PlainFile
11+
import org.junit.Assert.fail
12+
import org.junit.Test
13+
14+
class JavaJep445ParserTest extends DottyTest {
15+
16+
@Test def `parses unnamed class`: Unit = {
17+
val code =
18+
s"""
19+
|// hello
20+
|
21+
|import some.pkg.*;
22+
|
23+
|private volatile int x = 0;
24+
|@Magic
25+
|protected String s = "s";
26+
|
27+
|void main() {}
28+
|""".stripMargin
29+
30+
val parser =
31+
JavaParsers.JavaParser(SourceFile.virtual("MyUnnamed.java", code))
32+
val tree = parser.parse()
33+
34+
println(tree.show)
35+
36+
fail("TODO")
37+
}
38+
39+
@Test def `treats leading top-level types as nested types of unnamed class`: Unit = {
40+
val code =
41+
s"""
42+
|// hello
43+
|
44+
|import some.pkg.*;
45+
|
46+
|interface Inner {}
47+
|
48+
|static class InnerStatic {}
49+
|
50+
|void main() {}
51+
|""".stripMargin
52+
53+
val parser =
54+
JavaParsers.JavaParser(SourceFile.virtual("MyUnnamed.java", code))
55+
val tree = parser.parse()
56+
57+
println(tree.show)
58+
59+
fail("TODO")
60+
}
61+
62+
@Test def `treats leading top-level annotated vars as members of unnamed class`: Unit = {
63+
val code =
64+
s"""
65+
|
66+
|import some.pkg.*;
67+
|
68+
|@MyAnnotation
69+
|int x = 0;
70+
|
71+
|void main() {}
72+
|""".stripMargin
73+
74+
val parser =
75+
JavaParsers.JavaParser(SourceFile.virtual("MyUnnamed.java", code))
76+
val tree = parser.parse()
77+
78+
println(tree.show)
79+
80+
fail("TODO")
81+
}
82+
}

tests/pos-java21+/jep445/B.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class B
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void main() {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
@MyAnnotation
3+
int myInt = 10;
4+
5+
void main() {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
private volatile int myInt = 10;
2+
3+
String hello() {
4+
return "hello";
5+
}
6+
7+
interface Inner {}
8+
9+
void main() {}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
class InnerOfUnnamed {}
3+
4+
void main() {}

0 commit comments

Comments
 (0)