scala.tools.nsc.javac.JavaParsers.scala Maven / Gradle / Ivy
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
//todo: allow infix type patterns
package scala.tools.nsc
package javac
import symtab.Flags
import JavaTokens._
import scala.annotation._
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.language.implicitConversions
import scala.reflect.internal.util.{CodeAction, ListOfNil, Position}
import scala.tools.nsc.Reporting.WarningCategory
import scala.util.chaining._
trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
val global : Global
import global._
import definitions._
case class JavaOpInfo(operand: Tree, operator: Name, pos: Int)
class JavaUnitParser(val unit: global.CompilationUnit) extends JavaParser {
val in = new JavaUnitScanner(unit)
def freshName(prefix: String): Name = freshTermName(prefix)
def freshTermName(prefix: String): TermName = unit.freshTermName(prefix)
def freshTypeName(prefix: String): TypeName = unit.freshTypeName(prefix)
def deprecationWarning(off: Int, msg: String, since: String, actions: List[CodeAction]) = runReporting.deprecationWarning(off, msg, since, site = "", origin = "", actions)
implicit def i2p(offset : Int) : Position = Position.offset(unit.source, offset)
def warning(pos : Int, msg : String) : Unit = runReporting.warning(pos, msg, WarningCategory.JavaSource, site = "")
def syntaxError(pos: Int, msg: String) : Unit = reporter.error(pos, msg)
}
abstract class JavaParser extends ParserCommon {
val in: JavaScanner
def unit: CompilationUnit
def freshName(prefix : String): Name
protected implicit def i2p(offset : Int) : Position
private implicit def p2i(pos : Position): Int = if (pos.isDefined) pos.point else -1
/** The simple name of the package of the currently parsed file */
private var thisPackageName: TypeName = tpnme.EMPTY
/** this is the general parse method
*/
def parse(): Tree = {
val t = compilationUnit()
accept(EOF)
t
}
// -------- error handling ---------------------------------------
private var lastErrorPos : Int = -1
protected def skip(): Unit = {
var nparens = 0
var nbraces = 0
while (true) {
in.token match {
case EOF =>
return
case SEMI =>
if (nparens == 0 && nbraces == 0) return
case RPAREN =>
nparens -= 1
case RBRACE =>
if (nbraces == 0) return
nbraces -= 1
case LPAREN =>
nparens += 1
case LBRACE =>
nbraces += 1
case _ =>
}
in.nextToken()
}
}
def warning(pos : Int, msg : String) : Unit
def syntaxError(pos: Int, msg: String) : Unit
def syntaxError(msg: String, skipIt: Boolean): Unit = {
syntaxError(in.currentPos, msg, skipIt)
}
def syntaxError(pos: Int, msg: String, skipIt: Boolean): Unit = {
if (pos > lastErrorPos) {
syntaxError(pos, msg)
// no more errors on this token.
lastErrorPos = in.currentPos
}
if (skipIt)
skip()
}
def errorTypeTree = TypeTree().setType(ErrorType) setPos in.currentPos
// --------- tree building -----------------------------
import gen.{ rootId, scalaDot }
def javaDot(name: Name): Tree =
Select(rootId(nme.java), name)
def javaLangDot(name: Name): Tree =
Select(javaDot(nme.lang), name)
def javaLangObject(): Tree = javaLangDot(tpnme.Object)
def javaLangRecord(): Tree = javaLangDot(tpnme.Record)
def arrayOf(tpt: Tree) =
AppliedTypeTree(scalaDot(tpnme.Array), List(tpt))
def blankExpr = EmptyTree
def makePackaging(pkg: RefTree, stats: List[Tree]): PackageDef =
atPos(pkg.pos) { PackageDef(pkg, stats) }
def makeTemplate(parents: List[Tree], stats: List[Tree]) =
Template(parents, noSelfType, if (treeInfo.firstConstructor(stats) == EmptyTree)
makeConstructor(Nil) :: stats else stats)
def makeSyntheticParam(count: Int, tpt: Tree): ValDef =
makeParam(nme.syntheticParamName(count), tpt)
def makeParam(name: String, tpt: Tree): ValDef =
makeParam(TermName(name), tpt)
def makeParam(name: TermName, tpt: Tree): ValDef =
ValDef(Modifiers(Flags.JAVA | Flags.PARAM), name, tpt, EmptyTree)
def makeConstructor(formals: List[Tree]) = {
val vparams = mapWithIndex(formals)((p, i) => makeSyntheticParam(i + 1, p))
DefDef(Modifiers(Flags.JAVA), nme.CONSTRUCTOR, List(), List(vparams), TypeTree(), blankExpr)
}
/** A hook for joining the comment associated with a definition.
* Overridden by scaladoc.
*/
def joinComment(trees: => List[Tree]): List[Tree] = trees
// ------------- general parsing ---------------------------
/** skip parent or brace enclosed sequence of things */
def skipAhead(): Unit = {
var nparens = 0
var nbraces = 0
do {
in.token match {
case LPAREN =>
nparens += 1
case LBRACE =>
nbraces += 1
case _ =>
}
in.nextToken()
in.token match {
case RPAREN =>
nparens -= 1
case RBRACE =>
nbraces -= 1
case _ =>
}
} while (in.token != EOF && (nparens > 0 || nbraces > 0))
}
def skipTo(tokens: Int*): Unit = {
while (!(tokens contains in.token) && in.token != EOF) {
if (in.token == LBRACE) { skipAhead(); accept(RBRACE) }
else if (in.token == LPAREN) { skipAhead(); accept(RPAREN) }
else in.nextToken()
}
}
/** Consume one token of the specified type, or
* signal an error if it is not there.
*/
def accept(token: Int): Int = {
val pos = in.currentPos
if (in.token != token) {
val posToReport = in.currentPos
val msg =
JavaScannerConfiguration.token2string(token) + " expected but " +
JavaScannerConfiguration.token2string(in.token) + " found."
syntaxError(posToReport, msg, skipIt = true)
}
if (in.token == token) in.nextToken()
pos
}
def acceptClosingAngle(): Unit = {
val closers: PartialFunction[Int, Int] = {
case GTGTGTEQ => GTGTEQ
case GTGTGT => GTGT
case GTGTEQ => GTEQ
case GTGT => GT
case GTEQ => EQUALS
}
if (closers isDefinedAt in.token) in.token = closers(in.token)
else accept(GT)
}
def identForType(): TypeName = ident().toTypeName
def ident(): Name =
if (in.token == IDENTIFIER) {
val name = in.name
in.nextToken()
name
} else {
accept(IDENTIFIER)
nme.ERROR
}
def repsep[T <: Tree](p: () => T, sep: Int): List[T] = {
val buf = ListBuffer[T](p())
while (in.token == sep) {
in.nextToken()
buf += p()
}
buf.toList
}
/** Convert (qual)ident to type identifier
*/
def convertToTypeId(tree: Tree): Tree = gen.convertToTypeName(tree) match {
case Some(t) => t setPos tree.pos
case _ => tree match {
case AppliedTypeTree(_, _) | ExistentialTypeTree(_, _) | SelectFromTypeTree(_, _) =>
tree
case _ =>
syntaxError(tree.pos, "identifier expected", skipIt = false)
errorTypeTree
}
}
// -------------------- specific parsing routines ------------------
def qualId(orClassLiteral: Boolean = false): Tree = {
var t: Tree = atPos(in.currentPos) { Ident(ident()) }
var done = false
while (!done && in.token == DOT) {
in.nextToken()
t = atPos(in.currentPos) {
if (orClassLiteral && in.token == CLASS) {
in.nextToken()
done = true
val tpeArg = convertToTypeId(t)
TypeApply(Select(gen.mkAttributedRef(definitions.PredefModule), nme.classOf), tpeArg :: Nil)
} else {
Select(t, ident())
}
}
}
t
}
@tailrec
final def optArrayBrackets(tpt: Tree): Tree =
if (in.token == LBRACKET) {
val tpt1 = atPos(in.pos) { arrayOf(tpt) }
in.nextToken()
accept(RBRACKET)
optArrayBrackets(tpt1)
} else tpt
def basicType(): Tree =
atPos(in.pos) {
in.token match {
case BYTE => in.nextToken(); TypeTree(ByteTpe)
case SHORT => in.nextToken(); TypeTree(ShortTpe)
case CHAR => in.nextToken(); TypeTree(CharTpe)
case INT => in.nextToken(); TypeTree(IntTpe)
case LONG => in.nextToken(); TypeTree(LongTpe)
case FLOAT => in.nextToken(); TypeTree(FloatTpe)
case DOUBLE => in.nextToken(); TypeTree(DoubleTpe)
case BOOLEAN => in.nextToken(); TypeTree(BooleanTpe)
case _ => syntaxError("illegal start of type", skipIt = true); errorTypeTree
}
}
def typ(): Tree = {
annotations() // TODO: fix scala/bug#9883 (JSR 308)
optArrayBrackets {
if (in.token == FINAL) in.nextToken()
if (in.token == IDENTIFIER) {
var t = typeArgs(atPos(in.currentPos)(Ident(ident())))
// typeSelect generates Select nodes if the lhs is an Ident or Select,
// SelectFromTypeTree otherwise. See #3567.
// Select nodes can be later
// converted in the typechecker to SelectFromTypeTree if the class
// turns out to be an instance inner class instead of a static inner class.
def typeSelect(t: Tree, name: Name) = t match {
case Ident(_) | Select(_, _) => Select(t, name)
case _ => SelectFromTypeTree(t, name.toTypeName)
}
if (in.token == DOT)
t.updateAttachment(RootSelection)
while (in.token == DOT) {
in.nextToken()
t = typeArgs(atPos(in.currentPos)(typeSelect(t, ident())))
}
convertToTypeId(t)
} else {
basicType()
}
}
}
def typeArgs(t: Tree): Tree = {
val wildcards = new ListBuffer[TypeDef]
def typeArg(): Tree =
if (in.token == QMARK) {
val pos = in.currentPos
in.nextToken()
val hi = if (in.token == EXTENDS) { in.nextToken() ; typ() } else Ident(definitions.ObjectClass)
val lo = if (in.token == SUPER) { in.nextToken() ; typ() } else EmptyTree
val tdef = atPos(pos) {
TypeDef(
Modifiers(Flags.JAVA | Flags.DEFERRED),
newTypeName("_$"+ (wildcards.length + 1)),
List(),
TypeBoundsTree(lo, hi))
}
wildcards += tdef
atPos(pos) { Ident(tdef.name) }
} else {
typ()
}
if (in.token == LT) {
in.nextToken()
val t1 = convertToTypeId(t)
val args = repsep(() => typeArg(), COMMA)
acceptClosingAngle()
atPos(t1.pos) {
val t2: Tree = AppliedTypeTree(t1, args)
if (wildcards.isEmpty) t2
else ExistentialTypeTree(t2, wildcards.toList)
}
} else t
}
def annotations(): List[Tree] = {
val annots = new ListBuffer[Tree]
while (in.token == AT) {
in.nextToken()
val annot = annotation()
if (annot.nonEmpty) annots += annot
}
annots.toList
}
/**
* Annotation ::= NormalAnnotation
* | MarkerAnnotation
* | SingleElementAnnotation
*
* NormalAnnotation ::= `@` TypeName `(` [ElementValuePairList] `)`
* ElementValuePairList ::= ElementValuePair {`,` ElementValuePair}
* ElementValuePair ::= Identifier = ElementValue
* ElementValue ::= ConditionalExpressionSubset
* | ElementValueArrayInitializer
* | Annotation
*
* // We only support a subset of the Java syntax that can form constant expressions.
* // https://docs.oracle.com/javase/specs/jls/se14/html/jls-15.html#jls-15.29
* //
* // Luckily, we can just parse matching `(` and `)` to find our way to the end of the argument list.
* // and drop the arguments until we implement full support for Java constant expressions
* //
* ConditionalExpressionSubset := Literal
* | Identifier
* | QualifiedName
* | ClassLiteral
*
* ElementValueArrayInitializer ::= `{` [ElementValueList] [`,`] `}`
* ElementValueList ::= ElementValue {`,` ElementValue}
*/
def annotation(): Tree = {
object LiteralK { def unapply(@unused token: Token) = tryLiteral() }
def elementValue(): Tree = in.token match {
case LiteralK(k) => in.nextToken(); atPos(in.currentPos)(Literal(k))
case IDENTIFIER => qualId(orClassLiteral = true)
case LBRACE => accept(LBRACE); elementArray()
case AT => accept(AT); annotation()
case _ => in.nextToken(); EmptyTree
}
def elementArray(): Tree = atPos(in.pos) {
val ts = new ListBuffer[Tree]
while (in.token != RBRACE) {
ts += elementValue()
if (in.token == COMMA) in.nextToken() // done this way trailing commas are supported
}
val ok = !ts.contains(EmptyTree)
in.token match {
case RBRACE if ok => accept(RBRACE); Apply(ArrayModule_overloadedApply, ts.toList: _*)
case _ => skipTo(RBRACE); EmptyTree
}
}
// 1) name = value
// 2) implicit `value` arg with constant value
// 3) implicit `value` arg
def annArg(): Tree = {
def mkNamedArg(name: Ident, value: Tree) = if (value.isEmpty) EmptyTree else gen.mkNamedArg(name, value)
in.token match {
case IDENTIFIER => qualId(orClassLiteral = true) match {
case name: Ident if in.token == EQUALS => accept(EQUALS); mkNamedArg(name, elementValue())
case rhs => mkNamedArg(Ident(nme.value), rhs)
}
case _ => mkNamedArg(Ident(nme.value), elementValue())
}
}
atPos(in.pos) {
val id = convertToTypeId(qualId())
in.token match {
case LPAREN =>
// TODO: fix copyFrom+skipAhead; CharArrayReaderData missing
val saved = new JavaTokenData {}.copyFrom(in) // prep to bail if non-literals/identifiers
accept(LPAREN)
val args = in.token match {
case RPAREN => Nil
case _ => commaSeparated(atPos(in.pos)(annArg()))
}
val ok = !args.contains(EmptyTree)
in.token match {
case RPAREN if ok => accept(RPAREN); New(id, List(args))
case _ => in.copyFrom(saved); skipAhead(); accept(RPAREN); EmptyTree
}
case _ => New(id, ListOfNil)
}
}
}
def modifiers(inInterface: Boolean, annots0: List[Tree] = Nil): Modifiers = {
var flags: Long = Flags.JAVA
// assumed true unless we see public/private/protected
var isPackageAccess = true
var annots: List[Tree] = annots0
def addAnnot(sym: Symbol) = annots :+= New(sym.tpe)
while (true) {
in.token match {
case AT if in.lookaheadToken != INTERFACE =>
in.nextToken()
val annot = annotation()
if (annot.nonEmpty) annots :+= annot
case PUBLIC =>
isPackageAccess = false
in.nextToken()
case PROTECTED =>
flags |= Flags.PROTECTED
in.nextToken()
case PRIVATE =>
isPackageAccess = false
flags |= Flags.PRIVATE
in.nextToken()
case STATIC =>
flags |= Flags.STATIC
in.nextToken()
case ABSTRACT =>
flags |= Flags.ABSTRACT
in.nextToken()
case FINAL =>
flags |= Flags.FINAL
in.nextToken()
case DEFAULT =>
flags |= Flags.JAVA_DEFAULTMETHOD
in.nextToken()
case NATIVE =>
addAnnot(NativeAttr)
in.nextToken()
case TRANSIENT =>
addAnnot(TransientAttr)
in.nextToken()
case VOLATILE =>
addAnnot(VolatileAttr)
in.nextToken()
case STRICTFP =>
addAnnot(ScalaStrictFPAttr)
in.nextToken()
case SYNCHRONIZED =>
in.nextToken()
case _ =>
val unsealed = 0L // no flag for UNSEALED
def consume(added: FlagSet): false = { in.nextToken(); flags |= added; false }
def lookingAhead(s: String): Boolean = {
import scala.reflect.internal.Chars._
var i = 0
val n = s.length
val lookahead = in.in.lookahead
while (i < n && lookahead.ch != SU) {
if (lookahead.ch != s.charAt(i)) return false
lookahead.next()
i += 1
}
i == n && Character.isWhitespace(lookahead.ch)
}
val done = (in.token != IDENTIFIER) || (
in.name match {
case nme.javaRestrictedIdentifiers.SEALED => consume(Flags.SEALED)
case nme.javaRestrictedIdentifiers.UNSEALED => consume(unsealed)
case nme.javaRestrictedIdentifiers.NON =>
!lookingAhead("-sealed") || {
in.nextToken()
in.nextToken()
consume(unsealed)
}
case _ => true
}
)
if (done) {
val privateWithin: TypeName =
if (isPackageAccess && !inInterface) thisPackageName
else tpnme.EMPTY
return Modifiers(flags, privateWithin) withAnnotations annots
}
}
}
abort("should not be here")
}
def typeParams(): List[TypeDef] =
if (in.token == LT) {
in.nextToken()
val tparams = repsep(() => typeParam(), COMMA)
acceptClosingAngle()
tparams
} else List()
def typeParam(): TypeDef =
atPos(in.currentPos) {
val anns = annotations()
val name = identForType()
val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else EmptyTree
TypeDef(Modifiers(Flags.JAVA | Flags.DEFERRED | Flags.PARAM, tpnme.EMPTY, anns), name, Nil, TypeBoundsTree(EmptyTree, hi))
}
def bound(): Tree =
atPos(in.currentPos) {
val buf = ListBuffer[Tree](typ())
while (in.token == AMP) {
in.nextToken()
buf += typ()
}
val ts = buf.toList
if (ts.tail.isEmpty) ts.head
else CompoundTypeTree(Template(ts, noSelfType, List()))
}
def formalParams(): List[ValDef] = {
accept(LPAREN)
val vparams = if (in.token == RPAREN) List() else repsep(() => formalParam(), COMMA)
accept(RPAREN)
vparams
}
def formalParam(): ValDef = {
if (in.token == FINAL) in.nextToken()
val anns = annotations()
var t = typ()
if (in.token == DOTDOTDOT) {
in.nextToken()
t = atPos(t.pos) {
AppliedTypeTree(scalaDot(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME), List(t))
}
}
varDecl(in.currentPos, Modifiers(Flags.JAVA | Flags.PARAM, typeNames.EMPTY, anns), t, ident().toTermName)
}
def optThrows(): Unit = {
if (in.token == THROWS) {
in.nextToken()
repsep(() => typ(), COMMA)
}
}
def methodBody(): Tree = {
skipAhead()
accept(RBRACE) // skip block
blankExpr
}
def definesInterface(token: Int) = token == INTERFACE || token == AT
/** If the next token is the identifier "record", convert it into a proper
* token. Technically, "record" is just a restricted identifier. However,
* once we've figured out that it is in a position where it identifies a
* "record" class, it is much more convenient to promote it to a token.
*/
def adaptRecordIdentifier(): Unit = {
if (in.token == IDENTIFIER && in.name == nme.javaRestrictedIdentifiers.RECORD)
in.token = RECORD
}
def termDecl(mods: Modifiers, parentToken: Int): List[Tree] = {
val inInterface = definesInterface(parentToken)
val tparams = if (in.token == LT) typeParams() else List()
val isVoid = in.token == VOID
var rtpt =
if (isVoid) {
in.nextToken()
TypeTree(UnitTpe) setPos in.pos
} else typ()
var pos = in.currentPos
val rtptName = rtpt match {
case Ident(name) => name
case _ => nme.EMPTY
}
if (in.token == LPAREN && rtptName != nme.EMPTY && !inInterface) {
// constructor declaration
val vparams = formalParams()
optThrows()
List {
atPos(pos) {
DefDef(mods, nme.CONSTRUCTOR, tparams, List(vparams), TypeTree(), methodBody())
}
}
} else if (in.token == LBRACE && rtptName != nme.EMPTY && parentToken == RECORD) {
// compact constructor
methodBody()
List.empty
} else {
var mods1 = mods
if (mods hasFlag Flags.ABSTRACT) mods1 = mods &~ Flags.ABSTRACT | Flags.DEFERRED
pos = in.currentPos
val name = ident()
if (in.token == LPAREN) {
// method declaration
val vparams = formalParams()
if (!isVoid) rtpt = optArrayBrackets(rtpt)
optThrows()
val isConcreteInterfaceMethod = !inInterface || (mods hasFlag Flags.JAVA_DEFAULTMETHOD) || (mods hasFlag Flags.STATIC) || (mods hasFlag Flags.PRIVATE)
val bodyOk = !(mods1 hasFlag Flags.DEFERRED) && isConcreteInterfaceMethod
val body =
if (bodyOk && in.token == LBRACE) {
methodBody()
} else {
if (parentToken == AT && in.token == DEFAULT) {
val annot =
atPos(pos) {
New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil)
}
mods1 = mods1 withAnnotations annot :: Nil
skipTo(SEMI)
accept(SEMI)
blankExpr
} else {
accept(SEMI)
EmptyTree
}
}
// for abstract methods (of classes), the `DEFERRED` flag is already set.
// here we also set it for interface methods that are not static and not default.
if (!isConcreteInterfaceMethod) mods1 |= Flags.DEFERRED
List {
atPos(pos) {
DefDef(mods1, name.toTermName, tparams, List(vparams), rtpt, body)
}
}
} else {
if (inInterface) mods1 |= Flags.FINAL | Flags.STATIC
val result = fieldDecls(pos, mods1, rtpt, name)
accept(SEMI)
result
}
}
}
/** Parse a sequence of field declarations, separated by commas.
* This one is tricky because a comma might also appear in an
* initializer. Since we don't parse initializers we don't know
* what the comma signifies.
* We solve this with a second list buffer `maybe` which contains
* potential variable definitions.
* Once we have reached the end of the statement, we know whether
* these potential definitions are real or not.
*/
def fieldDecls(pos: Position, mods: Modifiers, tpt: Tree, name: Name): List[Tree] = {
val buf = ListBuffer[Tree](varDecl(pos, mods, tpt, name.toTermName))
val maybe = new ListBuffer[Tree] // potential variable definitions.
while (in.token == COMMA) {
in.nextToken()
if (in.token == IDENTIFIER) { // if there's an ident after the comma ...
val name = ident()
if (in.token == EQUALS || in.token == SEMI) { // ... followed by a `=` or `;`, we know it's a real variable definition
buf ++= maybe
buf += varDecl(in.currentPos, mods, tpt.duplicate, name.toTermName)
maybe.clear()
} else if (in.token == COMMA) { // ... if there's a comma after the ident, it could be a real vardef or not.
maybe += varDecl(in.currentPos, mods, tpt.duplicate, name.toTermName)
} else { // ... if there's something else we were still in the initializer of the
// previous var def; skip to next comma or semicolon.
skipTo(COMMA, SEMI)
maybe.clear()
}
} else { // ... if there's no ident following the comma we were still in the initializer of the
// previous var def; skip to next comma or semicolon.
skipTo(COMMA, SEMI)
maybe.clear()
}
}
if (in.token == SEMI) {
buf ++= maybe // every potential vardef that survived until here is real.
}
buf.toList
}
def varDecl(pos: Position, mods: Modifiers, tpt: Tree, name: TermName): ValDef = {
val tpt1 = optArrayBrackets(tpt)
/* Tries to detect final static literals syntactically and returns a constant type replacement */
def optConstantTpe(): Tree = {
def constantTpe(const: Constant): Tree = TypeTree(ConstantType(const))
def forConst(const: Constant): Tree = {
in.nextToken()
if (in.token != SEMI) tpt1
else {
def isStringTyped = tpt1 match {
case Ident(TypeName("String")) => true
case _ => false
}
if (const.tag == StringTag && isStringTyped) constantTpe(const)
else if (tpt1.tpe != null && (const.tag == BooleanTag || const.isNumeric)) {
// for example, literal 'a' is ok for float. 127 is ok for byte, but 128 is not.
val converted = const.convertTo(tpt1.tpe)
if (converted == null) tpt1
else constantTpe(converted)
} else tpt1
}
}
in.nextToken() // EQUALS
if (mods.hasFlag(Flags.STATIC) && mods.isFinal) {
val neg = in.token match {
case MINUS | BANG => in.nextToken(); true
case _ => false
}
tryLiteral(neg).map(forConst).getOrElse(tpt1)
} else tpt1
}
val tpt2: Tree =
if (in.token == EQUALS && !mods.isParameter) {
val res = optConstantTpe()
skipTo(COMMA, SEMI)
res
} else tpt1
val mods1 = if (mods.isFinal) mods &~ Flags.FINAL else mods | Flags.MUTABLE
atPos(pos) {
ValDef(mods1, name, tpt2, blankExpr)
}
}
def memberDecl(mods: Modifiers, parentToken: Int): List[Tree] = {
in.token match {
case CLASS | ENUM | RECORD | INTERFACE | AT =>
typeDecl(mods)
case _ =>
termDecl(mods, parentToken)
}
}
def makeCompanionObject(cdef: ClassDef, statics: List[Tree]): Tree =
atPos(cdef.pos) {
ModuleDef(cdef.mods & (Flags.AccessFlags | Flags.JAVA), cdef.name.toTermName,
makeTemplate(List(), statics))
}
def addCompanionObject(statics: List[Tree], cdef: ClassDef): List[Tree] =
List(makeCompanionObject(cdef, statics), cdef)
def importDecl(): List[Tree] = {
accept(IMPORT)
val pos = in.currentPos
val buf = new ListBuffer[Name]
@tailrec
def collectIdents() : Int = {
if (in.token == ASTERISK) {
val starOffset = in.pos
in.nextToken()
buf += nme.WILDCARD
starOffset
} else {
val nameOffset = in.pos
buf += ident()
if (in.token == DOT) {
in.nextToken()
collectIdents()
} else nameOffset
}
}
if (in.token == STATIC) in.nextToken()
else buf += nme.ROOTPKG
val lastnameOffset = collectIdents()
accept(SEMI)
val names = buf.toList
if (names.lengthIs < 2) {
syntaxError(pos, "illegal import", skipIt = false)
List()
} else {
val qual = names.tail.init.foldLeft(Ident(names.head): Tree)(Select(_, _))
val lastname = names.last
val selector = lastname match {
case nme.WILDCARD => ImportSelector.wildAt(lastnameOffset)
case _ => ImportSelector(lastname, lastnameOffset, lastname, lastnameOffset)
}
List(atPos(pos)(Import(qual, List(selector))))
}
}
def interfacesOpt() =
if (in.token == IMPLEMENTS) {
in.nextToken()
repsep(() => typ(), COMMA)
} else {
List()
}
def permitsOpt() =
if (in.token == IDENTIFIER && in.name == nme.javaRestrictedIdentifiers.PERMITS) {
in.nextToken()
repsep(() => typ(), COMMA)
}
else Nil
def classDecl(mods: Modifiers): List[Tree] = {
if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty
accept(CLASS)
val pos = in.currentPos
val name = identForType()
val tparams = typeParams()
val superclass =
if (in.token == EXTENDS) {
in.nextToken()
typ()
} else {
javaLangObject()
}
val interfaces = interfacesOpt()
val permits = permitsOpt()
val (statics, body) = typeBody(CLASS)
addCompanionObject(statics, atPos(pos) {
ClassDef(mods, name, tparams, makeTemplate(superclass :: interfaces, body))
.tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits)))
})
}
def recordDecl(mods: Modifiers): List[Tree] = {
accept(RECORD)
val pos = in.currentPos
val name = identForType()
val tparams = typeParams()
val header = formalParams()
val superclass = javaLangRecord()
val interfaces = interfacesOpt()
val (statics, body) = typeBody(RECORD)
// Generate accessors, if not already explicitly specified. Record bodies tend to be trivial.
val existing = body.iterator.collect { case DefDef(_, name, Nil, ListOfNil, _, _) => name }.toSet
val accessors = header.iterator
.collect {
case ValDef(mods, name, tpt, _) if !existing(name) =>
DefDef(Modifiers(Flags.JAVA).withAnnotations(mods.annotations), name, tparams = Nil, vparamss = ListOfNil, tpt.duplicate, blankExpr)
}
.toList
// Generate canonical constructor. During parsing this is done unconditionally but the symbol
// is unlinked in Namer if it is found to clash with a manually specified constructor.
val canonicalCtor = DefDef(
mods | Flags.SYNTHETIC,
nme.CONSTRUCTOR,
List(),
List(header.map(_.duplicate)),
TypeTree(),
blankExpr
)
addCompanionObject(statics, atPos(pos) {
ClassDef(
mods | Flags.FINAL,
name,
tparams,
makeTemplate(superclass :: interfaces, canonicalCtor :: accessors ::: body)
)
})
}
def interfaceDecl(mods: Modifiers): List[Tree] = {
if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty
accept(INTERFACE)
val pos = in.currentPos
val name = identForType()
val tparams = typeParams()
val parents =
if (in.token == EXTENDS) {
in.nextToken()
repsep(() => typ(), COMMA)
} else {
List(javaLangObject())
}
val permits = permitsOpt()
val (statics, body) = typeBody(INTERFACE)
addCompanionObject(statics, atPos(pos) {
ClassDef(mods | Flags.TRAIT | Flags.INTERFACE | Flags.ABSTRACT,
name, tparams,
makeTemplate(parents, body))
.tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits)))
})
}
def typeBody(leadingToken: Int): (List[Tree], List[Tree]) = {
accept(LBRACE)
val defs = typeBodyDecls(leadingToken)
accept(RBRACE)
defs
}
def typeBodyDecls(parentToken: Int): (List[Tree], List[Tree]) = {
val inInterface = definesInterface(parentToken)
val statics = new ListBuffer[Tree]
val members = new ListBuffer[Tree]
while (in.token != RBRACE && in.token != EOF) {
var mods = modifiers(inInterface)
if (in.token == LBRACE) {
skipAhead() // skip init block, we just assume we have seen only static
accept(RBRACE)
} else if (in.token == SEMI) {
in.nextToken()
} else {
// See "14.3. Local Class and Interface Declarations"
adaptRecordIdentifier()
if (in.token == ENUM || in.token == RECORD || definesInterface(in.token))
mods |= Flags.STATIC
val decls = joinComment(memberDecl(mods, parentToken))
@tailrec
def isDefDef(tree: Tree): Boolean = tree match {
case _: DefDef => true
case DocDef(_, defn) => isDefDef(defn)
case _ => false
}
(if (mods.hasStaticFlag || inInterface && !(decls exists isDefDef))
statics
else
members) ++= decls
}
}
(statics.toList, members.toList)
}
def annotationParents = Select(javaLangDot(nme.annotation), tpnme.Annotation) :: Nil
def annotationDecl(mods: Modifiers): List[Tree] = {
accept(AT)
accept(INTERFACE)
val pos = in.currentPos
val name = identForType()
val (statics, body) = typeBody(AT)
val templ = makeTemplate(annotationParents, body)
addCompanionObject(statics, atPos(pos) {
import Flags._
ClassDef(
mods | JAVA_ANNOTATION | TRAIT | INTERFACE | ABSTRACT,
name, List(), templ)
})
}
def enumDecl(mods: Modifiers): List[Tree] = {
accept(ENUM)
val pos = in.currentPos
val name = identForType()
def enumType = Ident(name)
val interfaces = interfacesOpt()
accept(LBRACE)
val buf = new ListBuffer[Tree]
var enumIsFinal = true
@tailrec
def parseEnumConsts(): Unit = {
if (in.token != RBRACE && in.token != SEMI && in.token != EOF) {
val (const, hasClassBody) = enumConst(enumType)
buf += const
// if any of the enum constants has a class body, the enum class is not final (JLS 8.9.)
enumIsFinal &&= !hasClassBody
if (in.token == COMMA) {
in.nextToken()
parseEnumConsts()
}
}
}
parseEnumConsts()
val consts = buf.toList
val (statics, body) =
if (in.token == SEMI) {
in.nextToken()
typeBodyDecls(ENUM)
} else {
(List(), List())
}
val predefs = List(
DefDef(
Modifiers(Flags.JAVA | Flags.STATIC), nme.values, List(),
ListOfNil,
arrayOf(enumType),
blankExpr),
DefDef(
Modifiers(Flags.JAVA | Flags.STATIC), nme.valueOf, List(),
List(List(makeParam("x", TypeTree(StringTpe)))),
enumType,
blankExpr))
accept(RBRACE)
val superclazz =
AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType))
val hasAbstractMember = body.exists {
case m: MemberDef => m.mods.hasFlag(Flags.DEFERRED)
case _ => false
}
val finalFlag = if (enumIsFinal) Flags.FINAL else 0L
val abstractFlag = if (hasAbstractMember) Flags.ABSTRACT else 0L
addCompanionObject(consts ::: statics ::: predefs, atPos(pos) {
ClassDef(mods | Flags.JAVA_ENUM | Flags.SEALED | abstractFlag | finalFlag, name, List(),
makeTemplate(superclazz :: interfaces, body))
})
}
def enumConst(enumType: Tree): (ValDef, Boolean) = {
val anns = annotations()
var hasClassBody = false
val res = atPos(in.currentPos) {
val name = ident()
if (in.token == LPAREN) {
// skip arguments
skipAhead()
accept(RPAREN)
}
if (in.token == LBRACE) {
hasClassBody = true
// skip classbody
skipAhead()
accept(RBRACE)
}
ValDef(Modifiers(Flags.JAVA_ENUM | Flags.STABLE | Flags.JAVA | Flags.STATIC, typeNames.EMPTY, anns), name.toTermName, enumType, blankExpr)
}
(res, hasClassBody)
}
def typeDecl(mods: Modifiers): List[Tree] = {
adaptRecordIdentifier()
in.token match {
case ENUM => joinComment(enumDecl(mods))
case INTERFACE => joinComment(interfaceDecl(mods))
case AT => annotationDecl(mods)
case CLASS => joinComment(classDecl(mods))
case RECORD => joinComment(recordDecl(mods))
case _ => in.nextToken(); syntaxError("illegal start of type declaration", skipIt = true); List(errorTypeTree)
}
}
def tryLiteral(negate: Boolean = false): Option[Constant] = {
val l = in.token match {
case TRUE => !negate
case FALSE => negate
case CHARLIT => in.name.charAt(0)
case INTLIT => in.intVal(negate).toInt
case LONGLIT => in.intVal(negate)
case FLOATLIT => in.floatVal(negate).toFloat
case DOUBLELIT => in.floatVal(negate)
case STRINGLIT => in.name.toString
case _ => null
}
if (l == null) None
else Some(Constant(l))
}
/** CompilationUnit ::= [[Annotation] package QualId semi] {Import} {TypeDecl} //TopStatSeq
*/
def compilationUnit(): Tree = {
val buf = ListBuffer.empty[Tree]
var pos = in.currentPos
val leadingAnnots = if (in.token == AT) annotations() else Nil
val pkg: RefTree =
if (in.token == PACKAGE) {
if (!leadingAnnots.isEmpty) { // TODO: put these somewhere?
//if (unit.source.file.name != "package-info.java")
// syntaxError(pos, "package annotations must be in file package-info.java")
pos = in.currentPos
}
accept(PACKAGE)
qualId().asInstanceOf[RefTree].tap(_ => accept(SEMI))
}
else {
if (!leadingAnnots.isEmpty)
buf ++= typeDecl(modifiers(inInterface = false, annots0 = leadingAnnots))
Ident(nme.EMPTY_PACKAGE_NAME)
}
thisPackageName = gen.convertToTypeName(pkg) match {
case Some(t) => t.name.toTypeName
case _ => tpnme.EMPTY
}
if (buf.isEmpty)
while (in.token == IMPORT)
buf ++= importDecl()
while (in.token != EOF && in.token != RBRACE) {
while (in.token == SEMI) in.nextToken()
if (in.token != EOF)
buf ++= typeDecl(modifiers(inInterface = false))
}
accept(EOF)
atPos(pos) {
makePackaging(pkg, buf.toList)
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy