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

import ast._
import core._
import Trees._
import Types._, Inferencing._, Contexts._, Decorators._, Denotations._, Symbols._
import Applications._, Implicits._
import util.Positions._
import printing.Showable
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
  }

  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) = {
      val (typerStateStr, explanationStr) =
        if (ctx.settings.explaintypes.value) {
          val nestedCtx = ctx.fresh.withTypeComparerFn(new ExplainingTypeComparer(_))
          (found <:< expected)(nestedCtx)
          ("\n" + ctx.typerState.show, "\n" + nestedCtx.typeComparer.toString)
        }
        else ("", "")
      i"""type mismatch:
           | found   : $found
           | required: $expected""".stripMargin + typerStateStr + explanationStr
    }
  }

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

  /** Implementation of i"..." string interpolator */
  implicit class InfoString(val sc: StringContext) extends AnyVal {

    def i(args: Any*)(implicit ctx: Context): String = {

      def isSensical(arg: Any): Boolean = arg match {
        case tpe: Type if tpe.isErroneous => false
        case NoType => false
        case sym: Symbol if sym.isCompleted =>
          sym.info != ErrorType && sym.info != TypeAlias(ErrorType) && sym.info != NoType
        case _ => true
      }

      def treatArg(arg: Any, suffix: String): (Any, String) = arg match {
        case arg: List[_] if suffix.nonEmpty && suffix.head == '%' =>
          val (rawsep, rest) = suffix.tail.span(_ != '%')
          val sep = StringContext.treatEscapes(rawsep)
          if (rest.nonEmpty) (arg map treatSingleArg mkString sep, rest.tail)
          else (arg, suffix)
        case _ =>
          (treatSingleArg(arg), suffix)
      }

      def treatSingleArg(arg: Any) : Any = arg match {
        case arg: Showable => arg.show
        case _ => arg
      }

      if (ctx.reporter.hasErrors &&
          ctx.suppressNonSensicalErrors &&
          !ctx.settings.YshowSuppressedErrors.value &&
          !args.forall(isSensical(_)))
        throw new SuppressedMessage
      val prefix :: suffixes = sc.parts.toList
      val (args1, suffixes1) = (args, suffixes).zipped.map(treatArg(_, _)).unzip
      new StringContext(prefix :: suffixes1.toList: _*).s(args1: _*)
    }
  }
}