@@ -20,7 +20,8 @@ import StdNames._
2020import reporting ._
2121import dotty .tools .dotc .util .SourceFile
2222import util .Spans ._
23- import scala .collection .mutable .ListBuffer
23+
24+ import scala .collection .mutable .{ListBuffer , LinkedHashMap }
2425
2526object JavaParsers {
2627
@@ -96,8 +97,12 @@ object JavaParsers {
9697 def javaLangDot (name : Name ): Tree =
9798 Select (javaDot(nme.lang), name)
9899
100+ /** Tree representing `java.lang.Object` */
99101 def javaLangObject (): Tree = javaLangDot(tpnme.Object )
100102
103+ /** Tree representing `java.lang.Record` */
104+ def javaLangRecord (): Tree = javaLangDot(tpnme.Record )
105+
101106 def arrayOf (tpt : Tree ): AppliedTypeTree =
102107 AppliedTypeTree (scalaDot(tpnme.Array ), List (tpt))
103108
@@ -555,6 +560,14 @@ object JavaParsers {
555560
556561 def definesInterface (token : Int ): Boolean = token == INTERFACE || token == AT
557562
563+ /** If the next token is the identifier "record", convert it into the RECORD token.
564+ * This makes it easier to handle records in various parts of the code,
565+ * in particular when a `parentToken` is passed to some functions.
566+ */
567+ def adaptRecordIdentifier (): Unit =
568+ if in.token == IDENTIFIER && in.name == jnme.RECORDid then
569+ in.token = RECORD
570+
558571 def termDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = {
559572 val inInterface = definesInterface(parentToken)
560573 val tparams = if (in.token == LT ) typeParams(Flags .JavaDefined | Flags .Param ) else List ()
@@ -581,6 +594,16 @@ object JavaParsers {
581594 TypeTree (), methodBody()).withMods(mods)
582595 }
583596 }
597+ } else if (in.token == LBRACE && rtptName != nme.EMPTY && parentToken == RECORD ) {
598+ /*
599+ record RecordName(T param1, ...) {
600+ RecordName { // <- here
601+ // methodBody
602+ }
603+ }
604+ */
605+ methodBody()
606+ Nil
584607 }
585608 else {
586609 var mods1 = mods
@@ -717,12 +740,11 @@ object JavaParsers {
717740 ValDef (name, tpt2, if (mods.is(Flags .Param )) EmptyTree else unimplementedExpr).withMods(mods1)
718741 }
719742
720- def memberDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = in.token match {
721- case CLASS | ENUM | INTERFACE | AT =>
722- typeDecl(start, if ( definesInterface(parentToken)) mods | Flags .JavaStatic else mods)
743+ def memberDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = in.token match
744+ case CLASS | ENUM | RECORD | INTERFACE | AT =>
745+ typeDecl(start, if definesInterface(parentToken) then mods | Flags .JavaStatic else mods)
723746 case _ =>
724747 termDecl(start, mods, parentToken, parentTParams)
725- }
726748
727749 def makeCompanionObject (cdef : TypeDef , statics : List [Tree ]): Tree =
728750 atSpan(cdef.span) {
@@ -804,6 +826,51 @@ object JavaParsers {
804826 addCompanionObject(statics, cls)
805827 }
806828
829+ def recordDecl (start : Offset , mods : Modifiers ): List [Tree ] =
830+ accept(RECORD )
831+ val nameOffset = in.offset
832+ val name = identForType()
833+ val tparams = typeParams()
834+ val header = formalParams()
835+ val superclass = javaLangRecord() // records always extend java.lang.Record
836+ val interfaces = interfacesOpt() // records may implement interfaces
837+ val (statics, body) = typeBody(RECORD , name, tparams)
838+
839+ // We need to generate accessors for every param, if no method with the same name is already defined
840+
841+ var fieldsByName = header.map(v => (v.name, (v.tpt, v.mods.annotations))).to(LinkedHashMap )
842+
843+ for case DefDef (name, paramss, _, _) <- body
844+ if paramss.isEmpty && fieldsByName.contains(name)
845+ do
846+ fieldsByName -= name
847+ end for
848+
849+ val accessors =
850+ (for (name, (tpt, annots)) <- fieldsByName yield
851+ DefDef (name, Nil , tpt, unimplementedExpr)
852+ .withMods(Modifiers (Flags .JavaDefined | Flags .Method | Flags .Synthetic ))
853+ ).toList
854+
855+ // generate the canonical constructor
856+ val canonicalConstructor =
857+ DefDef (nme.CONSTRUCTOR , joinParams(tparams, List (header)), TypeTree (), EmptyTree )
858+ .withMods(Modifiers (Flags .JavaDefined | Flags .Synthetic , mods.privateWithin))
859+
860+ // return the trees
861+ val recordTypeDef = atSpan(start, nameOffset) {
862+ TypeDef (name,
863+ makeTemplate(
864+ parents = superclass :: interfaces,
865+ stats = canonicalConstructor :: accessors ::: body,
866+ tparams = tparams,
867+ true
868+ )
869+ ).withMods(mods)
870+ }
871+ addCompanionObject(statics, recordTypeDef)
872+ end recordDecl
873+
807874 def interfaceDecl (start : Offset , mods : Modifiers ): List [Tree ] = {
808875 accept(INTERFACE )
809876 val nameOffset = in.offset
@@ -846,7 +913,8 @@ object JavaParsers {
846913 else if (in.token == SEMI )
847914 in.nextToken()
848915 else {
849- if (in.token == ENUM || definesInterface(in.token)) mods |= Flags .JavaStatic
916+ adaptRecordIdentifier()
917+ if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) mods |= Flags .JavaStatic
850918 val decls = memberDecl(start, mods, parentToken, parentTParams)
851919 (if (mods.is(Flags .JavaStatic ) || inInterface && ! (decls exists (_.isInstanceOf [DefDef ])))
852920 statics
@@ -947,13 +1015,13 @@ object JavaParsers {
9471015 }
9481016 }
9491017
950- def typeDecl (start : Offset , mods : Modifiers ): List [Tree ] = in.token match {
1018+ def typeDecl (start : Offset , mods : Modifiers ): List [Tree ] = in.token match
9511019 case ENUM => enumDecl(start, mods)
9521020 case INTERFACE => interfaceDecl(start, mods)
9531021 case AT => annotationDecl(start, mods)
9541022 case CLASS => classDecl(start, mods)
1023+ case RECORD => recordDecl(start, mods)
9551024 case _ => in.nextToken(); syntaxError(em " illegal start of type declaration " , skipIt = true ); List (errorTypeTree)
956- }
9571025
9581026 def tryConstant : Option [Constant ] = {
9591027 val negate = in.token match {
@@ -1004,6 +1072,7 @@ object JavaParsers {
10041072 if (in.token != EOF ) {
10051073 val start = in.offset
10061074 val mods = modifiers(inInterface = false )
1075+ adaptRecordIdentifier() // needed for typeDecl
10071076 buf ++= typeDecl(start, mods)
10081077 }
10091078 }
0 commit comments