summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/macros/compiler/Errors.scala
blob: 98fd091e9cd5549a398e622f67ef7a5d594944fe (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
package scala.reflect.macros
package compiler

import scala.compat.Platform.EOL
import scala.reflect.macros.util.Traces

trait Errors extends Traces {
  self: DefaultMacroCompiler =>

  import global._
  import analyzer._
  import definitions._
  import treeInfo._
  import typer.infer.InferErrorGen._
  import runDefinitions._
  def globalSettings = global.settings

  private def implRefError(message: String) = {
    val Applied(culprit, _, _) = macroDdef.rhs
    abort(culprit.pos, message)
  }

  private def bundleRefError(message: String) = {
    val Applied(core, _, _) = macroDdef.rhs
    val culprit = core match {
      case Select(Applied(core, _, _), _) => core
      case _ => core
    }
    abort(culprit.pos, message)
  }

  def MacroImplAmbiguousError() = implRefError(
    "macro implementation reference is ambiguous: makes sense both as\n"+
    "a macro bundle method reference and a vanilla object method reference")

  def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static")

  def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter")

  trait Error {
    self: MacroImplRefCompiler =>

    // sanity check errors

    def MacroImplReferenceWrongShapeError() = implRefError(
      "macro implementation reference has wrong shape. required:\n"+
      "macro [<static object>].<method name>[[<type args>]] or\n" +
      "macro [<macro bundle>].<method name>[[<type args>]]")

    def MacroImplWrongNumberOfTypeArgumentsError() = {
      val diagnostic = if (macroImpl.typeParams.length > targs.length) "has too few type arguments" else "has too many arguments"
      implRefError(s"macro implementation reference $diagnostic for " + treeSymTypeMsg(macroImplRef))
    }

    private def macroImplementationWording =
      if (isImplBundle) "bundle implementation"
      else "macro implementation"

    def MacroImplNotPublicError() = implRefError(s"${macroImplementationWording} must be public")

    def MacroImplOverloadedError() = implRefError(s"${macroImplementationWording} cannot be overloaded")

    def MacroImplNonTagImplicitParameters(params: List[Symbol]) = implRefError(s"${macroImplementationWording}s cannot have implicit parameters other than WeakTypeTag evidences")

    // compatibility errors

    // helpers

    private def lengthMsg(flavor: String, violation: String, extra: Symbol) = {
      val noun = if (flavor == "value") "parameter" else "type parameter"
      val message = noun + " lists have different length, " + violation + " extra " + noun
      val suffix = if (extra ne NoSymbol) " " + extra.defString else ""
      message + suffix
    }

    private def abbreviateCoreAliases(s: String): String = {
      val coreAliases = List("WeakTypeTag", "Expr", "Tree")
      coreAliases.foldLeft(s)((res, x) => res.replace("c.universe." + x, "c." + x))
    }

    private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean, untype: Boolean) = {
      def preprocess(tpe: Type) = if (untype) untypeMetalevel(tpe) else tpe
      var pssPart = (pss map (ps => ps map (p => p.defStringSeenAs(preprocess(p.info))) mkString ("(", ", ", ")"))).mkString
      if (abbreviate) pssPart = abbreviateCoreAliases(pssPart)
      var retPart = preprocess(restpe).toString
      if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart)
      pssPart + ": " + retPart
    }

    // not exactly an error generator, but very related
    // and I dearly wanted to push it away from Macros.scala
    private def checkConforms(slot: String, rtpe: Type, atpe: Type) = {
      val verbose = macroDebugVerbose

      def check(rtpe: Type, atpe: Type): Boolean = {
        def success() = { if (verbose) println(rtpe + " <: " + atpe + "?" + EOL + "true"); true }
        (rtpe, atpe) match {
          case _ if rtpe eq atpe => success()
          case (TypeRef(_, RepeatedParamClass, rtpe :: Nil), TypeRef(_, RepeatedParamClass, atpe :: Nil)) => check(rtpe, atpe)
          case (ExprClassOf(_), TreeType()) if rtpe.prefix =:= atpe.prefix => success()
          case (SubtreeType(), ExprClassOf(_)) if rtpe.prefix =:= atpe.prefix => success()
          case _ => rtpe <:< atpe
        }
      }

      val ok =
        if (verbose) withTypesExplained(check(rtpe, atpe))
        else check(rtpe, atpe)
      if (!ok) {
        if (!verbose) explainTypes(rtpe, atpe)
        val msg = {
          val ss = Seq(rtpe, atpe) map (this abbreviateCoreAliases _.toString)
          s"type mismatch for $slot: ${ss(0)} does not conform to ${ss(1)}"
        }
        compatibilityError(msg)
      }
    }

    private def compatibilityError(message: String) =
      implRefError(
        s"${macroImplementationWording} has incompatible shape:"+
        "\n required: " + showMeth(rparamss, rret, abbreviate = true, untype = false) +
        "\n or      : " + showMeth(rparamss, rret, abbreviate = true, untype = true) +
        "\n found   : " + showMeth(aparamss, aret, abbreviate = false, untype = false) +
        "\n" + message)

    def MacroImplParamssMismatchError() = compatibilityError("number of parameter sections differ")

    def MacroImplExtraParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(lengthMsg("value", "found", aparams(rparams.length)))

    def MacroImplMissingParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(abbreviateCoreAliases(lengthMsg("value", "required", rparams(aparams.length))))

    def checkMacroImplParamTypeMismatch(atpe: Type, rparam: Symbol) = checkConforms("parameter " + rparam.name, rparam.tpe, atpe)

    def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkConforms("return type", atpe, rret)

    def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name)

    def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = {
      def fail(paramName: Name) = compatibilityError("types incompatible for parameter " + paramName + ": corresponding is not a vararg parameter")
      if (isRepeated(rparam) && !isRepeated(aparam)) fail(rparam.name)
      if (!isRepeated(rparam) && isRepeated(aparam)) fail(aparam.name)
    }

    def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) =
      compatibilityError(NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value))

    def MacroImplTparamInstantiationError(atparams: List[Symbol], e: NoInstance) = {
      val badps = atparams map (_.defString) mkString ", "
      compatibilityError(f"type parameters $badps cannot be instantiated%n${e.getMessage}")
    }
  }
}