aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
blob: 47f201a09a0d1eed8d999191869bc00520c20b0c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                               
                      









                   
                                                                        



                                          






























































                                                                                            

                                                                                             


                                                            


                                                                               
                                     










                                                             
                                                                                                                   
                                                                                   
                                                                               




                                                       
                                                       
                                                                          


                                                                                                   
                             



                                                                                                              



                                                           
                                                      
                                                                                               
 
                                                                                                            
                                                                                            
                                                                                                       




































                                                                    

                                                              



















































                                                                           
                                                    

                 
                                                              
















                                                                        
                                                                





                                           
                                                                   
























                                                                                            

                                                                                

                                                           
                                                


                                   
                                                                               


















                                                                                     
                                             












                                         
                             


























                                                                                     




                                      
















                                                        
                                     











                                        

                                    

                                   

























                                                                                                          
                                                                               





















                                                                                  
                           




                                           
                                
                                           

         


                                                                                  








                               
                                               






                                                                        
                                                                                                                



                                                                                               






                                








                                                                        


                                                                       




                                                                  
                              












                                                                    
                                     

                                                                                        
                                                       
                                                     

                            
                             






                                                    
                                      
                                                                                                        


                
                                                                  
                                                                       














                                                                       


                                                                                                                  



                                                                                
                                      


                                                                                                                                
                                                                                       

                                                                                                                      
                                                                                         
















                                                                                                       
                                                                       

                                                                           
                                                                         
                                                                                                   

     
                                                                                                                                 
                                           
                                                                                             
               
                                                         



                                                                       
                               

                                                                                                                                   

       
                                                    
                                                                                         

















                                                                                         
                                      
                                                                                    
                                                                                





                                                          
                           
                    





















                                            
                                                            








                                                                                

                                                            










                                   
                                                                 
                   
                                










                                                          
                                          
                                                                                                 

                                      

     
                                                                     
                       
                                









                                                              
                                            
                
               
                                                                                                                          

                                        













                                                                                                                     

                                                          





                                                                                 
                                                                                      
                                                                         
                                                                                                     





                                                           
                                                              
                                                                    
                                                                       











                                                               
                                                                      

                       
                                


                                                      
                                                     
       

                                                                                                      
                                                        
                                                                                 
                                            
                                                            

                                        

     
                                                                
                  
                                
























                                                                        
                             

                            
                                                                                                      
               
                              
                                                                      
                   
                                                                                                      







                                                                               
                                                
                     
                                                                                                 
       
                                                                   















                                     
                                                                                                                                                  


       




                                                                               





                                                                                                                            
                           


                                                    















                                                      




                                                                    
       
                                                             
                 
          


     
package dotty.tools
package dotc
package parsing

import dotty.tools.dotc.core.Constants.Constant
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Flags.FlagSet

import scala.language.implicitConversions

import JavaTokens._
import JavaScanners._
import Scanners.Offset
import Parsers._
import core._
import Contexts._
import Names._
import NameOps._
import Types._
import Symbols._
import ast.Trees._
import Decorators._
import StdNames._
import dotty.tools.dotc.reporting.diagnostic.messages.IdentifierExpected
import dotty.tools.dotc.util.SourceFile
import util.Positions._
import annotation.switch
import scala.collection.mutable.ListBuffer

object JavaParsers {

  import ast.untpd._

  class JavaParser(source: SourceFile)(implicit ctx: Context) extends ParserCommon(source) {

    val definitions = ctx.definitions
    import definitions._

    val in: JavaScanner = new JavaScanner(source)

    /** The simple name of the package of the currently parsed file */
    private var thisPackageName: TypeName = tpnme.EMPTY

    /** This is the general parse entry point.
     *  Overridden by ScriptParser
     */
    def parse(): Tree = {
      val t = compilationUnit()
      accept(EOF)
      t
    }

    // -------- error handling ---------------------------------------

    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 syntaxError(msg: String, skipIt: Boolean): Unit = {
      syntaxError(in.offset, msg, skipIt)
    }

    def syntaxError(pos: Int, msg: String, skipIt: Boolean): Unit = {
      if (pos > lastErrorOffset) {
        syntaxError(msg, pos)
        // no more errors on this token.
        lastErrorOffset = in.offset
      }
      if (skipIt)
        skip()
    }

    def errorTypeTree = TypeTree().withType(UnspecifiedErrorType) withPos Position(in.offset)

    // --------- tree building -----------------------------

    def scalaAnnotationDot(name: Name) = Select(scalaDot(nme.annotation), name)

    def javaDot(name: Name): Tree =
      Select(rootDot(nme.java), name)

    def javaLangDot(name: Name): Tree =
      Select(javaDot(nme.lang), name)

    def javaLangObject(): Tree = javaLangDot(tpnme.Object)

    def arrayOf(tpt: Tree) =
      AppliedTypeTree(Ident(nme.Array.toTypeName), List(tpt))

    def unimplementedExpr = Ident("???".toTermName)

    def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef], needsDummyConstr: Boolean) = {
      def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match {
        case (meth: DefDef) :: rest if meth.name == CONSTRUCTOR => (meth, rest)
        case first :: rest =>
          val (constr, tail) = pullOutFirstConstr(rest)
          (constr, first :: tail)
        case nil => (EmptyTree, nil)
      }
      var (constr1, stats1) = pullOutFirstConstr(stats)
      if (constr1 == EmptyTree) constr1 = makeConstructor(List(), tparams)
      // A dummy first constructor is needed for Java classes so that the real constructors see the
      // import of the companion object. The constructor has parameter of type Unit so no Java code
      // can call it.
      if (needsDummyConstr) {
        stats1 = constr1 :: stats1
        constr1 = makeConstructor(List(scalaDot(tpnme.Unit)), tparams, Flags.JavaDefined | Flags.PrivateLocal)
      }
      Template(constr1.asInstanceOf[DefDef], parents, EmptyValDef, stats1)
    }

    def makeSyntheticParam(count: Int, tpt: Tree): ValDef =
      makeParam(nme.syntheticParamName(count), tpt)
    def makeParam(name: TermName, tpt: Tree): ValDef =
      ValDef(name, tpt, EmptyTree).withMods(Modifiers(Flags.JavaDefined | Flags.ParamAccessor))

    def makeConstructor(formals: List[Tree], tparams: List[TypeDef], flags: FlagSet = Flags.JavaDefined) = {
      val vparams = formals.zipWithIndex.map { case (p, i) => makeSyntheticParam(i + 1, p) }
      DefDef(nme.CONSTRUCTOR, tparams, List(vparams), TypeTree(), EmptyTree).withMods(Modifiers(flags))
    }

    // ------------- 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.
      *
      * @return The offset at the start of the token to accept
      */
    def accept(token: Int): Int = {
      val offset = in.offset
      if (in.token != token) {
        val offsetToReport = in.offset
        val msg =
          tokenString(token) + " expected but " +
            tokenString(in.token) + " found."

        syntaxError(offsetToReport, msg, skipIt = true)
      }
      if (in.token == token) in.nextToken()
      offset
    }

    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 = convertToTypeName(tree) match {
      case Some(t)  => t withPos tree.pos
      case _        => tree match {
        case AppliedTypeTree(_, _) | Select(_, _) =>
          tree
        case _ =>
          syntaxError(IdentifierExpected(tree.show), tree.pos)
          errorTypeTree
      }
    }

    /** Translate names in Select/Ident nodes to type names.
      */
    def convertToTypeName(tree: Tree): Option[RefTree] = tree match {
      case Select(qual, name) => Some(Select(qual, name.toTypeName))
      case Ident(name)        => Some(Ident(name.toTypeName))
      case _                  => None
    }
    // -------------------- specific parsing routines ------------------

    def qualId(): RefTree = {
      var t: RefTree = atPos(in.offset) { Ident(ident()) }
      while (in.token == DOT) {
        in.nextToken()
        t = atPos(t.pos.start, in.offset) { Select(t, ident()) }
      }
      t
    }

    def optArrayBrackets(tpt: Tree): Tree =
      if (in.token == LBRACKET) {
        val tpt1 = atPos(tpt.pos.start, in.offset) { arrayOf(tpt) }
        in.nextToken()
        accept(RBRACKET)
        optArrayBrackets(tpt1)
      } else tpt

    def basicType(): Tree =
      atPos(in.offset) {
        in.token match {
          case BYTE    => in.nextToken(); TypeTree(ByteType)
          case SHORT   => in.nextToken(); TypeTree(ShortType)
          case CHAR    => in.nextToken(); TypeTree(CharType)
          case INT     => in.nextToken(); TypeTree(IntType)
          case LONG    => in.nextToken(); TypeTree(LongType)
          case FLOAT   => in.nextToken(); TypeTree(FloatType)
          case DOUBLE  => in.nextToken(); TypeTree(DoubleType)
          case BOOLEAN => in.nextToken(); TypeTree(BooleanType)
          case _       => syntaxError("illegal start of type", skipIt = true); errorTypeTree
        }
      }

    def typ(): Tree =
      optArrayBrackets {
        if (in.token == FINAL) in.nextToken()
        if (in.token == IDENTIFIER) {
          var t = typeArgs(atPos(in.offset)(Ident(ident())))
          // typeSelect generates Select nodes if the lhs is an Ident or Select,
          // For other nodes it always assumes that the selected item is a type.
          def typeSelect(t: Tree, name: Name) = t match {
            case Ident(_) | Select(_, _) => Select(t, name)
            case _ => Select(t, name.toTypeName)
          }
          while (in.token == DOT) {
            in.nextToken()
            t = typeArgs(atPos(t.pos.start, in.offset)(typeSelect(t, ident())))
          }
          convertToTypeId(t)
        } else {
          basicType()
        }
      }

    def typeArgs(t: Tree): Tree = {
      var wildnum = 0
      def typeArg(): Tree =
        if (in.token == QMARK) {
          val offset = in.offset
          in.nextToken()
          val hi = if (in.token == EXTENDS) { in.nextToken() ; typ() } else EmptyTree
          val lo = if (in.token == SUPER)   { in.nextToken() ; typ() } else EmptyTree
          atPos(offset) {
            /*
            TypeDef(
              Modifiers(Flags.JavaDefined | Flags.Deferred),
              typeName("_$" +(wildnum += 1)),
              List(),
              TypeBoundsTree(lo, hi))
              */
            TypeBoundsTree(lo, hi)
          }
        } else {
          typ()
        }
      if (in.token == LT) {
        in.nextToken()
        val t1 = convertToTypeId(t)
        val args = repsep(typeArg, COMMA)
        acceptClosingAngle()
        atPos(t1.pos.start) {
          AppliedTypeTree(t1, args)
        }
      } else t
    }

    def annotations(): List[Tree] = {
      //var annots = new ListBuffer[Tree]
      while (in.token == AT) {
        in.nextToken()
        annotation()
      }
      List() // don't pass on annotations for now
    }

    /** Annotation ::= TypeName [`(` AnnotationArgument {`,` AnnotationArgument} `)`]
      */
    def annotation(): Unit = {
      qualId()
      if (in.token == LPAREN) { skipAhead(); accept(RPAREN) }
      else if (in.token == LBRACE) { skipAhead(); accept(RBRACE) }
    }

    def modifiers(inInterface: Boolean): Modifiers = {
      var flags = Flags.JavaDefined
      // assumed true unless we see public/private/protected
      var isPackageAccess = true
      var annots: List[Tree] = Nil
      def addAnnot(sym: ClassSymbol) =
        annots :+= atPos(in.offset) {
          in.nextToken()
          New(TypeTree(sym.typeRef))
        }

      while (true) {
        in.token match {
          case AT if (in.lookaheadToken != INTERFACE) =>
            in.nextToken()
            annotation()
          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.JavaStatic
            in.nextToken()
          case ABSTRACT =>
            flags |= Flags.Abstract
            in.nextToken()
          case FINAL =>
            flags |= Flags.Final
            in.nextToken()
          case DEFAULT =>
            flags |= Flags.DefaultMethod
            in.nextToken()
          case NATIVE =>
            addAnnot(NativeAnnot)
          case TRANSIENT =>
            addAnnot(TransientAnnot)
          case VOLATILE =>
            addAnnot(VolatileAnnot)
          case SYNCHRONIZED | STRICTFP =>
            in.nextToken()
          case _ =>
            val privateWithin: TypeName =
              if (isPackageAccess && !inInterface) thisPackageName
              else tpnme.EMPTY

            return Modifiers(flags, privateWithin) withAnnotations annots
        }
      }
      assert(false, "should not be here")
      throw new RuntimeException
    }

    def typeParams(flags: FlagSet = Flags.JavaDefined | Flags.PrivateLocal | Flags.Param): List[TypeDef] =
      if (in.token == LT) {
        in.nextToken()
        val tparams = repsep(() => typeParam(flags), COMMA)
        acceptClosingAngle()
        tparams
      } else List()

    def typeParam(flags: FlagSet): TypeDef =
      atPos(in.offset) {
        val name = identForType()
        val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else EmptyTree
        TypeDef(name, TypeBoundsTree(EmptyTree, hi)).withMods(Modifiers(flags))
      }

    def bound(): Tree =
      atPos(in.offset) {
        val buf = ListBuffer[Tree](typ())
        while (in.token == AMP) {
          in.nextToken()
          buf += typ()
        }
        val ts = buf.toList
        if (ts.tail.isEmpty) ts.head
        else ts.reduce(AndTypeTree(_,_))
      }

    def formalParams(): List[ValDef] = {
      accept(LPAREN)
      val vparams = if (in.token == RPAREN) List() else repsep(formalParam, COMMA)
      accept(RPAREN)
      vparams
    }

    def formalParam(): ValDef = {
      val start = in.offset
      if (in.token == FINAL) in.nextToken()
      annotations()
      var t = typ()
      if (in.token == DOTDOTDOT) {
        in.nextToken()
        t = atPos(t.pos.start) {
          PostfixOp(t, Ident(nme.raw.STAR))
        }
      }
      atPos(start, in.offset) {
        varDecl(Modifiers(Flags.JavaDefined | Flags.Param), t, ident().toTermName)
      }
    }

    def optThrows(): Unit = {
      if (in.token == THROWS) {
        in.nextToken()
        repsep(typ, COMMA)
      }
    }

    def methodBody(): Tree = atPos(in.offset) {
      skipAhead()
      accept(RBRACE) // skip block
      unimplementedExpr
    }

    def definesInterface(token: Int) = token == INTERFACE || token == AT

    def termDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = {
      val inInterface = definesInterface(parentToken)
      val tparams = if (in.token == LT) typeParams(Flags.JavaDefined | Flags.Param) else List()
      val isVoid = in.token == VOID
      var rtpt =
        if (isVoid)
          atPos(in.offset) {
            in.nextToken()
            TypeTree(UnitType)
          }
        else typ()
      var nameOffset = in.offset
      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(start) {
            DefDef(nme.CONSTRUCTOR, parentTParams,
                List(vparams), TypeTree(), methodBody()).withMods(mods)
          }
        }
      } else {
        var mods1 = mods
        if (mods is Flags.Abstract) mods1 = mods &~ Flags.Abstract
        nameOffset = in.offset
        val name = ident()
        if (in.token == LPAREN) {
          // method declaration
          val vparams = formalParams()
          if (!isVoid) rtpt = optArrayBrackets(rtpt)
          optThrows()
          val bodyOk = !inInterface || (mods is Flags.DefaultMethod)
          val body =
            if (bodyOk && in.token == LBRACE) {
              methodBody()
            } else {
              if (parentToken == AT && in.token == DEFAULT) {
                val annot =
                  atPos(nameOffset) {
                    New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil)
                  }
                mods1 = mods1 withAddedAnnotation annot
                val unimplemented = unimplementedExpr
                skipTo(SEMI)
                accept(SEMI)
                unimplemented
              } else {
                accept(SEMI)
                EmptyTree
              }
            }
          //if (inInterface) mods1 |= Flags.Deferred
          List {
            atPos(start, nameOffset) {
              DefDef(name.toTermName, tparams, List(vparams), rtpt, body).withMods(mods1 | Flags.Method)
            }
          }
        } else {
          if (inInterface) mods1 |= Flags.Final | Flags.JavaStatic
          val result = fieldDecls(start, nameOffset, 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(start: Offset, firstNameOffset: Offset, mods: Modifiers, tpt: Tree, name: Name): List[Tree] = {
      val buf = ListBuffer[Tree](
          atPos(start, firstNameOffset) { varDecl(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 nextNameOffset = in.offset
        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 += atPos(start, nextNameOffset) { varDecl(mods, tpt, 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 += atPos(start, nextNameOffset) { varDecl(mods, tpt, 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(mods: Modifiers, tpt: Tree, name: TermName): ValDef = {
      val tpt1 = optArrayBrackets(tpt)
      if (in.token == EQUALS && !(mods is Flags.Param)) skipTo(COMMA, SEMI)
      val mods1 = if (mods is Flags.Final) mods else mods | Flags.Mutable
      ValDef(name, tpt1, if (mods is Flags.Param) EmptyTree else unimplementedExpr).withMods(mods1)
    }

    def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match {
      case CLASS | ENUM | INTERFACE | AT =>
        typeDecl(start, if (definesInterface(parentToken)) mods | Flags.JavaStatic else mods)
      case _ =>
        termDecl(start, mods, parentToken, parentTParams)
    }

    def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree =
      atPos(cdef.pos) {
        assert(cdef.pos.exists)
        ModuleDef(cdef.name.toTermName,
          makeTemplate(List(), statics, List(), false)).withMods((cdef.mods & (Flags.AccessFlags | Flags.JavaDefined)).toTermFlags)
      }

    def importCompanionObject(cdef: TypeDef): Tree =
      Import(Ident(cdef.name.toTermName).withPos(NoPosition), Ident(nme.WILDCARD) :: Nil)

    // Importing the companion object members cannot be done uncritically: see
    // ticket #2377 wherein a class contains two static inner classes, each of which
    // has a static inner class called "Builder" - this results in an ambiguity error
    // when each performs the import in the enclosing class's scope.
    //
    // To address this I moved the import Companion._ inside the class, as the first
    // statement.  This should work without compromising the enclosing scope, but may (?)
    // end up suffering from the same issues it does in scala - specifically that this
    // leaves auxiliary constructors unable to access members of the companion object
    // as unqualified identifiers.
    def addCompanionObject(statics: List[Tree], cdef: TypeDef): List[Tree] = {
      // if there are no statics we can use the original cdef, but we always
      // create the companion so import A._ is not an error (see ticket #1700)
      val cdefNew =
        if (statics.isEmpty) cdef
        else {
          val template = cdef.rhs.asInstanceOf[Template]
          cpy.TypeDef(cdef)(cdef.name,
            cpy.Template(template)(template.constr, template.parents, template.self,
              importCompanionObject(cdef) :: template.body)).withMods(cdef.mods)
        }

      List(makeCompanionObject(cdefNew, statics), cdefNew)
    }

    def importDecl(): List[Tree] = {
      val start = in.offset
      accept(IMPORT)
      val buf = new ListBuffer[Name]
      def collectIdents() : Int = {
        if (in.token == ASTERISK) {
          val starOffset = in.offset
          in.nextToken()
          buf += nme.WILDCARD
          starOffset
        } else {
          val nameOffset = in.offset
          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.length < 2) {
        syntaxError(start, "illegal import", skipIt = false)
        List()
      } else {
        val qual = ((Ident(names.head): Tree) /: names.tail.init) (Select(_, _))
        val lastname = names.last
        val ident = Ident(lastname) withPos Position(lastnameOffset)
//        val selector = lastname match {
//          case nme.WILDCARD => Pair(ident, Ident(null) withPos Position(-1))
//          case _            => Pair(ident, ident)
//        }
        val imp = atPos(start) { Import(qual, List(ident)) }
        imp :: Nil
      }
    }

    def interfacesOpt() =
      if (in.token == IMPLEMENTS) {
        in.nextToken()
        repsep(typ, COMMA)
      } else {
        List()
      }

    def classDecl(start: Offset, mods: Modifiers): List[Tree] = {
      accept(CLASS)
      val nameOffset = in.offset
      val name = identForType()
      val tparams = typeParams()
      val superclass =
        if (in.token == EXTENDS) {
          in.nextToken()
          typ()
        } else {
          javaLangObject()
        }
      val interfaces = interfacesOpt()
      val (statics, body) = typeBody(CLASS, name, tparams)
      val cls = atPos(start, nameOffset) {
        TypeDef(name, makeTemplate(superclass :: interfaces, body, tparams, true)).withMods(mods)
      }
      addCompanionObject(statics, cls)
    }

    def interfaceDecl(start: Offset, mods: Modifiers): List[Tree] = {
      accept(INTERFACE)
      val nameOffset = in.offset
      val name = identForType()
      val tparams = typeParams()
      val parents =
        if (in.token == EXTENDS) {
          in.nextToken()
          repsep(typ, COMMA)
        } else {
          List(javaLangObject())
        }
      val (statics, body) = typeBody(INTERFACE, name, tparams)
      val iface = atPos(start, nameOffset) {
        TypeDef(
          name,
          makeTemplate(parents, body, tparams, false)).withMods(mods | Flags.Trait | Flags.JavaInterface | Flags.Abstract)
      }
      addCompanionObject(statics, iface)
    }

    def typeBody(leadingToken: Int, parentName: Name, parentTParams: List[TypeDef]): (List[Tree], List[Tree]) = {
      accept(LBRACE)
      val defs = typeBodyDecls(leadingToken, parentName, parentTParams)
      accept(RBRACE)
      defs
    }

    def typeBodyDecls(parentToken: Int, parentName: Name, parentTParams: List[TypeDef]): (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) {
        val start = in.offset
        var mods = atPos(start) { 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 {
          if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.JavaStatic
          val decls = memberDecl(start, mods, parentToken, parentTParams)
          (if ((mods is Flags.JavaStatic) || inInterface && !(decls exists (_.isInstanceOf[DefDef])))
            statics
          else
            members) ++= decls
        }
      }
      def forwarders(sdef: Tree): List[Tree] = sdef match {
        case TypeDef(name, _) if (parentToken == INTERFACE) =>
          var rhs: Tree = Select(Ident(parentName.toTermName), name)
          List(TypeDef(name, rhs).withMods(Modifiers(Flags.Protected)))
        case _ =>
          List()
      }
      val sdefs = statics.toList
      val idefs = members.toList ::: (sdefs flatMap forwarders)
      (sdefs, idefs)
    }
    def annotationParents = List(
      scalaAnnotationDot(tpnme.Annotation),
      Select(javaLangDot(nme.annotation), tpnme.Annotation),
      scalaAnnotationDot(tpnme.ClassfileAnnotation)
    )
    def annotationDecl(start: Offset, mods: Modifiers): List[Tree] = {
      accept(AT)
      accept(INTERFACE)
      val nameOffset = in.offset
      val name = identForType()
      val (statics, body) = typeBody(AT, name, List())
      val constructorParams = body.collect {
        case dd: DefDef => makeParam(dd.name, dd.tpt)
      }
      val constr = DefDef(nme.CONSTRUCTOR,
        List(), List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined))
      val body1 = body.filterNot(_.isInstanceOf[DefDef])
      val templ = makeTemplate(annotationParents, constr :: body1, List(), false)
      val annot = atPos(start, nameOffset) {
        TypeDef(name, templ).withMods(mods | Flags.Abstract)
      }
      addCompanionObject(statics, annot)
    }

    def enumDecl(start: Offset, mods: Modifiers): List[Tree] = {
      accept(ENUM)
      val nameOffset = in.offset
      val name = identForType()
      def enumType = Ident(name)
      val interfaces = interfacesOpt()
      accept(LBRACE)
      val buf = new ListBuffer[Tree]
      def parseEnumConsts(): Unit = {
        if (in.token != RBRACE && in.token != SEMI && in.token != EOF) {
          buf += enumConst(enumType)
          if (in.token == COMMA) {
            in.nextToken()
            parseEnumConsts()
          }
        }
      }
      parseEnumConsts()
      val consts = buf.toList
      val (statics, body) =
        if (in.token == SEMI) {
          in.nextToken()
          typeBodyDecls(ENUM, name, List())
        } else {
          (List(), List())
        }
      val predefs = List(
        DefDef(
          nme.values, List(),
          ListOfNil,
          arrayOf(enumType),
          unimplementedExpr).withMods(Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method)),
        DefDef(
          nme.valueOf, List(),
          List(List(makeParam("x".toTermName, TypeTree(StringType)))),
          enumType,
          unimplementedExpr).withMods(Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method)))
      accept(RBRACE)
      /*
      val superclazz =
        AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType))
        */
      val superclazz = Apply(TypeApply(
        Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)),
        List(Literal(Constant(null)),Literal(Constant(0))))
      val enumclazz = atPos(start, nameOffset) {
        TypeDef(name,
          makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.Enum)
      }
      addCompanionObject(consts ::: statics ::: predefs, enumclazz)
    }

    def enumConst(enumType: Tree) = {
      annotations()
      atPos(in.offset) {
        val name = ident()
        if (in.token == LPAREN) {
          // skip arguments
          skipAhead()
          accept(RPAREN)
        }
        if (in.token == LBRACE) {
          // skip classbody
          skipAhead()
          accept(RBRACE)
        }
        ValDef(name.toTermName, enumType, unimplementedExpr).withMods(Modifiers(Flags.Enum | Flags.Stable | Flags.JavaDefined | Flags.JavaStatic))
      }
    }

    def typeDecl(start: Offset, mods: Modifiers): List[Tree] = in.token match {
      case ENUM      => enumDecl(start, mods)
      case INTERFACE => interfaceDecl(start, mods)
      case AT        => annotationDecl(start, mods)
      case CLASS     => classDecl(start, mods)
      case _         => in.nextToken(); syntaxError("illegal start of type declaration", skipIt = true); List(errorTypeTree)
    }

    /** CompilationUnit ::= [package QualId semi] TopStatSeq
      */
    def compilationUnit(): Tree = {
      val start = in.offset
      val pkg: RefTree =
        if (in.token == AT || in.token == PACKAGE) {
          annotations()
          accept(PACKAGE)
          val pkg = qualId()
          accept(SEMI)
          pkg
        } else {
          Ident(nme.EMPTY_PACKAGE)
        }
      thisPackageName = convertToTypeName(pkg) match {
        case Some(t)  => t.name.toTypeName
        case _        => tpnme.EMPTY
      }
      val buf = new ListBuffer[Tree]
      while (in.token == IMPORT)
        buf ++= importDecl()
      while (in.token != EOF && in.token != RBRACE) {
        while (in.token == SEMI) in.nextToken()
        if (in.token != EOF) {
          val start = in.offset
          val mods = atPos(start) { modifiers(inInterface = false) }
          buf ++= typeDecl(start, mods)
        }
      }
      val unit = atPos(start) { PackageDef(pkg, buf.toList) }
      accept(EOF)
      unit
    }
  }
}