aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala
blob: f1131b3f279c76ef89baea4cefcaadaafa3174da (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package dotty.tools.dotc.transform

import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts.Context
import scala.collection.mutable.ListBuffer
import dotty.tools.dotc.core.{Scopes, Flags}
import dotty.tools.dotc.core.Symbols.NoSymbol
import scala.annotation.tailrec
import dotty.tools.dotc.core._
import Symbols._
import scala.Some
import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer}
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts.Context
import scala.collection.mutable
import dotty.tools.dotc.core.Names.Name
import NameOps._

/** A transformer that provides a convenient way to create companion objects
  */
abstract class CreateCompanionObjects extends TreeTransform {

  import tpd._

  /** Given class definition should return true if companion object creation should be enforced
    */
  def predicate(cls: TypeDef)(implicit ctx: Context): Boolean

  override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = {
    @tailrec
    def transformStats0(trees: List[Tree], acc: ListBuffer[Tree]): List[Tree] = {
      trees match {
        case Nil => acc.toList
        case (claz: TypeDef) :: stats if claz.symbol.isClass && !(claz.symbol is Flags.Module) => {
          val moduleExists = !(claz.symbol.companionModule eq NoSymbol)
          if (moduleExists || !predicate(claz)) transformStats0(stats, acc += claz)
          else {
            val moduleSymbol = ctx.newCompleteModuleSymbol(claz.symbol.owner, claz.name.toTermName, Flags.Synthetic, Flags.Synthetic, List(defn.ObjectClass.typeRef), Scopes.newScope)
            if (moduleSymbol.owner.isClass) moduleSymbol.entered
            val companion = tpd.ModuleDef(moduleSymbol, List(EmptyTree))
            acc += claz
            acc += companion
            transformStats0(stats, acc)
          }
        }
        case stat :: stats => transformStats0(stats, acc += stat)
      }
    }

    transformStats0(trees, ListBuffer())
  }
}