aboutsummaryrefslogblamecommitdiff
path: root/doc-tool/src/dotty/tools/dottydoc/core/StatisticsPhase.scala
blob: e7f9a9ec7613764ec1b0b4566536e57c57fe7310 (plain) (tree)



























































































































































                                                                                                                          
package dotty.tools
package dottydoc
package core

import dotc.core.Phases.Phase
import dotc.core.Contexts.Context
import dotc.core.Symbols.Symbol
import dotc.core.Decorators._
import dotc.core.Flags._
import dotc.CompilationUnit
import dottydoc.util.syntax._
import dottydoc.util.traversing._

import model._

object Statistics {
  implicit class MapTotals(val map: Map[String, Statistics]) extends AnyVal {
    def totalEntities =
      map.values.foldLeft(0)(_ + _.totalEntities)
  }
}

case class Statistics(pkgName: String, api: Counters, internalApi: Counters) {
  def totalEntities =
    api.totalEntities + internalApi.totalEntities

  def totalDocstrings =
    api.totalDocstrings + internalApi.totalDocstrings
}

case class Counters(
  publicEntities: Int,
  privateEntities: Int,
  protectedEntities: Int,

  publicDocstrings: Int,
  privateDocstrings: Int,
  protectedDocstrings: Int
) {
  def totalEntities =
    publicEntities + privateEntities + protectedEntities

  def totalDocstrings =
    publicDocstrings + privateDocstrings + protectedDocstrings

  def merge(o: Counters): Counters = Counters(
    publicEntities + o.publicEntities,
    privateEntities + o.privateEntities,
    protectedEntities + o.protectedEntities,
    publicDocstrings + o.publicDocstrings,
    privateDocstrings + o.privateDocstrings,
    protectedDocstrings + o.protectedDocstrings
  )
}

class StatisticsPhase extends Phase {

  def phaseName = "StatisticsPhase"

  override def run(implicit ctx: Context): Unit = ()

  override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
    for {
      (pkgName, pack) <- ctx.docbase.packages
      externalApi = collectPublicStats(pack)
      internalApi = collectInternalStats(pack)
      stats = Statistics(pkgName, externalApi, internalApi)
    } ctx.docbase.registerStatistics(pkgName, stats)

    units
  }

  def collectPublicStats(pack: Package)(implicit ctx: Context): Counters = {
    var publicEntities: Int = 0
    var protectedEntities: Int = 0
    var publicDocstrings: Int = 0
    var protectedDocstrings: Int = 0

    if (pack.comment.isDefined) {
      publicEntities += 1
      publicDocstrings += 1
    }

    def doCount(sym: Symbol, comment: Int): Unit =
      if (!sym.is(Protected)) {
        publicEntities += 1
        publicDocstrings += comment
      }
      else {
        protectedEntities += 1
        protectedDocstrings += comment
      }


    def recur(e: Entity, reachable: Boolean): Unit = {
      val isVisible = !e.symbol.is(Private) && !e.symbol.privateWithin.exists
      val shouldCount = isVisible && reachable
      e match {
        case e: Package => ()
        case e: Entity with Members  => if (shouldCount) {
          doCount(e.symbol, if (e.comment.isDefined) 1 else 0)
          e.members.foreach { c =>
            if (!(e.symbol.is(Final) && c.symbol.is(Protected))) recur(c, true)
          }
        }
        case e =>
          if (shouldCount) doCount(e.symbol, if (e.comment.isDefined) 1 else 0)
      }
    }

    pack.members.foreach(recur(_, true))
    Counters(publicEntities, 0, protectedEntities, publicDocstrings, 0, protectedDocstrings)
  }

  def collectInternalStats(pack: Package)(implicit ctx: Context): Counters = {
    var publicEntities: Int = 0
    var privateEntities: Int = 0
    var protectedEntities: Int = 0
    var publicDocstrings: Int = 0
    var privateDocstrings: Int = 0
    var protectedDocstrings: Int = 0

    def doCount(sym: Symbol, comment: Int): Unit =
      if (sym.is(Private)) {
        privateEntities += 1
        privateDocstrings += comment
      }
      else if (!sym.is(Protected)) {
        publicEntities += 1
        publicDocstrings += comment
      }
      else {
        protectedEntities += 1
        protectedDocstrings += comment
      }


    def recur(e: Entity, reachable: Boolean): Unit = {
      val internal = !reachable || e.symbol.is(Private) || e.symbol.privateWithin.exists
      e match {
        case _: Package => ()
        case e: Entity with Members =>
          e.members.foreach { c =>
            val childIsInternal = !internal || (e.symbol.is(Final) && c.symbol.is(Protected))
            recur(c, childIsInternal)
          }
          if (internal) doCount(e.symbol, if (e.comment.isDefined) 1 else 0)
        case _ =>
          if (internal) doCount(e.symbol, if (e.comment.isDefined) 1 else 0)
      }
    }

    pack.members.foreach(recur(_, true))
    Counters(publicEntities, privateEntities, protectedEntities, publicDocstrings, privateDocstrings, protectedDocstrings)
  }
}