aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/typer/ErrorReporting.scala
blob: 5e7faab22a01ecbf62065ae6b57fb9f6192c84be (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
package dotty.tools
package dotc
package typer

import ast._
import core._
import Trees._
import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._
import Applications._, Implicits._
import util.Positions._
import printing.Showable
import printing.Disambiguation.disambiguated
import reporting.Reporter.SuppressedMessage

object ErrorReporting {

  import tpd._

  def errorTree(tree: untpd.Tree, msg: => String)(implicit ctx: Context): tpd.Tree =
    tree withType errorType(msg, tree.pos)

  def errorType(msg: => String, pos: Position)(implicit ctx: Context): ErrorType = {
    ctx.error(msg, pos)
    ErrorType
  }

  def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = {
    val cycleSym = ex.denot.symbol
    def errorMsg(msg: String, cx: Context): String =
      if (cx.mode is Mode.InferringReturnType) {
        cx.tree match {
          case tree: Trees.ValOrDefDef[_] =>
            val treeSym = ctx.symOfContextTree(tree)
            if (treeSym.exists && treeSym.name == cycleSym.name && treeSym.owner == cycleSym.owner) {
              val result = if (cycleSym.isSourceMethod) " result" else ""
              i"overloaded or recursive $cycleSym needs$result type"
            }
            else errorMsg(msg, cx.outer)
          case _ =>
            errorMsg(msg, cx.outer)
        }
      } else msg
    errorMsg(ex.show, ctx)
  }

  class Errors(implicit ctx: Context) {

    def expectedTypeStr(tp: Type): String = tp match {
      case tp: FunProto =>
        val result = tp.resultType match {
          case tp: WildcardType => ""
          case tp => i"and expected result type $tp"
        }
        i"arguments (${tp.typedArgs.tpes}%, %)$result"
      case _ =>
        i"expected type $tp"
    }

    def anonymousTypeMemberStr(tpe: Type) = {
      val kind = tpe match {
          case _: TypeBounds => "type with bounds"
          case _: PolyType | _: MethodType => "method"
          case _ => "value of type"
        }
        i"$kind $tpe"
    }

    def overloadedAltsStr(alts: List[SingleDenotation]) =
      i"overloaded alternatives of ${denotStr(alts.head)} with types\n" +
      i" ${alts map (_.info)}%\n %"

    def denotStr(denot: Denotation): String =
      if (denot.isOverloaded) overloadedAltsStr(denot.alternatives)
      else if (denot.symbol.exists) denot.symbol.showLocated
      else anonymousTypeMemberStr(denot.info)

    def refStr(tp: Type): String = tp match {
      case tp: NamedType => denotStr(tp.denot)
      case _ => anonymousTypeMemberStr(tp)
    }

    def exprStr(tree: Tree): String = refStr(tree.tpe)

    def patternConstrStr(tree: Tree): String = ???

    def typeMismatch(tree: Tree, pt: Type, implicitFailure: SearchFailure = NoImplicitMatches): Tree = {
      errorTree(tree, typeMismatchStr(tree.tpe, pt) + implicitFailure.postscript)
    }

    def typeMismatchStr(found: Type, expected: Type) = disambiguated { implicit ctx =>
      val (typerStateStr, explanationStr) =
        if (ctx.settings.explaintypes.value)
          ("\n" + ctx.typerState.show, "\n" + TypeComparer.explained((found <:< expected)(_)))
        else
          ("", "")
      def infoStr = found match { // DEBUG
          case tp: TypeRef => s"with info ${tp.info} / ${tp.prefix.toString} / ${tp.prefix.dealias.toString}"
          case _ => ""
        }
      i"""type mismatch:
           | found   : $found
           | required: $expected""".stripMargin + typerStateStr + explanationStr
    }
  }

  def err(implicit ctx: Context): Errors = new Errors

}