aboutsummaryrefslogtreecommitdiff
path: root/doc-tool/src/dotty/tools/dottydoc/core/StatisticsPhase.scala
blob: e7f9a9ec7613764ec1b0b4566536e57c57fe7310 (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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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)
  }
}