@@ -118,6 +118,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
118
118
119
119
def javaLangObject (): Tree = javaLangDot(tpnme.Object )
120
120
121
+ def javaLangRecord (): Tree = javaLangDot(tpnme.Record )
122
+
121
123
def arrayOf (tpt : Tree ) =
122
124
AppliedTypeTree (scalaDot(tpnme.Array ), List (tpt))
123
125
@@ -564,6 +566,16 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
564
566
565
567
def definesInterface (token : Int ) = token == INTERFACE || token == AT
566
568
569
+ /** If the next token is the identifier "record", convert it into a proper
570
+ * token. Technically, "record" is just a restricted identifier. However,
571
+ * once we've figured out that it is in a position where it identifies a
572
+ * "record" class, it is much more convenient to promote it to a token.
573
+ */
574
+ def adaptRecordIdentifier (): Unit = {
575
+ if (in.token == IDENTIFIER && in.name.toString == " record" )
576
+ in.token = RECORD
577
+ }
578
+
567
579
def termDecl (mods : Modifiers , parentToken : Int ): List [Tree ] = {
568
580
val inInterface = definesInterface(parentToken)
569
581
val tparams = if (in.token == LT ) typeParams() else List ()
@@ -587,6 +599,10 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
587
599
DefDef (mods, nme.CONSTRUCTOR , tparams, List (vparams), TypeTree (), methodBody())
588
600
}
589
601
}
602
+ } else if (in.token == LBRACE && parentToken == RECORD ) {
603
+ // compact constructor
604
+ methodBody()
605
+ List .empty
590
606
} else {
591
607
var mods1 = mods
592
608
if (mods hasFlag Flags .ABSTRACT ) mods1 = mods &~ Flags .ABSTRACT | Flags .DEFERRED
@@ -721,11 +737,14 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
721
737
}
722
738
}
723
739
724
- def memberDecl (mods : Modifiers , parentToken : Int ): List [Tree ] = in.token match {
725
- case CLASS | ENUM | INTERFACE | AT =>
726
- typeDecl(if (definesInterface(parentToken)) mods | Flags .STATIC else mods)
727
- case _ =>
728
- termDecl(mods, parentToken)
740
+ def memberDecl (mods : Modifiers , parentToken : Int ): List [Tree ] = {
741
+ adaptRecordIdentifier()
742
+ in.token match {
743
+ case CLASS | ENUM | RECORD | INTERFACE | AT =>
744
+ typeDecl(if (definesInterface(parentToken)) mods | Flags .STATIC else mods)
745
+ case _ =>
746
+ termDecl(mods, parentToken)
747
+ }
729
748
}
730
749
731
750
def makeCompanionObject (cdef : ClassDef , statics : List [Tree ]): Tree =
@@ -808,6 +827,61 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
808
827
})
809
828
}
810
829
830
+ def recordDecl (mods : Modifiers ): List [Tree ] = {
831
+ accept(RECORD )
832
+ val pos = in.currentPos
833
+ val name = identForType()
834
+ val tparams = typeParams()
835
+ val header = formalParams()
836
+ val superclass = javaLangRecord()
837
+ val interfaces = interfacesOpt()
838
+ val (statics, body) = typeBody(RECORD , name)
839
+
840
+ // Records generate a canonical constructor and accessors, unless they are manually specified
841
+ var generateCanonicalCtor = true
842
+ var generateAccessors = header
843
+ .view
844
+ .map { case ValDef (_, name, tpt, _) => name -> tpt }
845
+ .toMap
846
+ for (DefDef (_, name, List (), List (params), tpt, _) <- body) {
847
+ if (name == nme.CONSTRUCTOR && params.size == header.size) {
848
+ val ctorParamsAreCanonical = params.lazyZip(header).forall {
849
+ case (ValDef (_, _, tpt1, _), ValDef (_, _, tpt2, _)) => tpt1 equalsStructure tpt2
850
+ case _ => false
851
+ }
852
+ if (ctorParamsAreCanonical) generateCanonicalCtor = false
853
+ } else if (generateAccessors.contains(name) && params.isEmpty) {
854
+ generateAccessors -= name
855
+ }
856
+ }
857
+
858
+ // Generate canonical constructor and accessors, if not already manually specified
859
+ val accessors = generateAccessors
860
+ .map { case (name, tpt) =>
861
+ DefDef (Modifiers (Flags .JAVA ), name, List (), List (), tpt, blankExpr)
862
+ }
863
+ .toList
864
+ val canonicalCtor = Option .when(generateCanonicalCtor) {
865
+ DefDef (
866
+ Modifiers (Flags .JAVA ),
867
+ nme.CONSTRUCTOR ,
868
+ List (),
869
+ List (header),
870
+ TypeTree (),
871
+ blankExpr
872
+ )
873
+ }
874
+
875
+ addCompanionObject(statics, atPos(pos) {
876
+ ClassDef (
877
+ mods | Flags .FINAL ,
878
+ name,
879
+ tparams,
880
+ makeTemplate(superclass :: interfaces, canonicalCtor.toList ++ accessors ++ body)
881
+ )
882
+ })
883
+ }
884
+
811
885
def interfaceDecl (mods : Modifiers ): List [Tree ] = {
812
886
accept(INTERFACE )
813
887
val pos = in.currentPos
@@ -847,7 +921,10 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
847
921
} else if (in.token == SEMI ) {
848
922
in.nextToken()
849
923
} else {
850
- if (in.token == ENUM || definesInterface(in.token)) mods |= Flags .STATIC
924
+
925
+ // See "14.3. Local Class and Interface Declarations"
926
+ if (in.token == ENUM || in.token == RECORD || definesInterface(in.token))
927
+ mods |= Flags .STATIC
851
928
val decls = joinComment(memberDecl(mods, parentToken))
852
929
853
930
@ tailrec
@@ -956,12 +1033,16 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
956
1033
(res, hasClassBody)
957
1034
}
958
1035
959
- def typeDecl (mods : Modifiers ): List [Tree ] = in.token match {
960
- case ENUM => joinComment(enumDecl(mods))
961
- case INTERFACE => joinComment(interfaceDecl(mods))
962
- case AT => annotationDecl(mods)
963
- case CLASS => joinComment(classDecl(mods))
964
- case _ => in.nextToken(); syntaxError(" illegal start of type declaration" , skipIt = true ); List (errorTypeTree)
1036
+ def typeDecl (mods : Modifiers ): List [Tree ] = {
1037
+ adaptRecordIdentifier()
1038
+ in.token match {
1039
+ case ENUM => joinComment(enumDecl(mods))
1040
+ case INTERFACE => joinComment(interfaceDecl(mods))
1041
+ case AT => annotationDecl(mods)
1042
+ case CLASS => joinComment(classDecl(mods))
1043
+ case RECORD => joinComment(recordDecl(mods))
1044
+ case _ => in.nextToken(); syntaxError(" illegal start of type declaration" , skipIt = true ); List (errorTypeTree)
1045
+ }
965
1046
}
966
1047
967
1048
def tryLiteral (negate : Boolean = false ): Option [Constant ] = {
0 commit comments