All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.softwaremill.clippy.CompilationError.scala Maven / Gradle / Ivy

package com.softwaremill.clippy

import org.json4s.JValue
import org.json4s.JsonAST.{JArray, JField, JObject, JString}
import org.json4s.native.JsonMethods._
import CompilationError._

sealed trait CompilationError[T <: Template] {
  def toJson: JValue
  def toJsonString: String = compact(render(toJson))
  def matches(other: CompilationError[ExactT])(implicit ev: T =:= RegexT): Boolean
  def asRegex(implicit ev: T =:= ExactT): CompilationError[RegexT]
}

case class TypeMismatchError[T <: Template](found: T, foundExpandsTo: Option[T],
  required: T, requiredExpandsTo: Option[T], notes: Option[String]) extends CompilationError[T] {

  def notesAfterNewline = notes.fold("")(n => "\n"+n)

  override def toString = {
    def expandsTo(et: Option[T]): String = et.fold("")(e => s" (expands to: $e)")
    s"Type mismatch error.\nFound: $found${expandsTo(foundExpandsTo)},\nrequired: $required${expandsTo(requiredExpandsTo)}$notesAfterNewline"
  }

  override def toJson =
    JObject(
      List(
        TypeField -> JString("typeMismatch"),
        "found" -> JString(found.v),
        "required" -> JString(required.v))
        ++ foundExpandsTo.fold[List[JField]](Nil)(e => List("foundExpandsTo" -> JString(e.v)))
        ++ requiredExpandsTo.fold[List[JField]](Nil)(e => List("requiredExpandsTo" -> JString(e.v)))
    )

  override def matches(other: CompilationError[ExactT])(implicit ev: T =:= RegexT) = other match {
    case TypeMismatchError(f, fe, r, re, _) =>
      def optMatches(t: Option[T], v: Option[ExactT]) = (for {
        tt <- t
        vv <- v
      } yield tt.matches(vv)).getOrElse(true)

      found.matches(f) && optMatches(foundExpandsTo, fe) && required.matches(r) && optMatches(requiredExpandsTo, re)

    case _ =>
      false
  }

  def hasExpands: Boolean = foundExpandsTo.nonEmpty || requiredExpandsTo.nonEmpty

  override def asRegex(implicit ev: T =:= ExactT) = TypeMismatchError(
    RegexT.fromPattern(found.v), foundExpandsTo.map(fe => RegexT.fromPattern(fe.v)),
    RegexT.fromPattern(required.v), requiredExpandsTo.map(re => RegexT.fromPattern(re.v)),
    notes)
}

case class NotFoundError[T <: Template](what: T) extends CompilationError[T] {

  override def toString = s"Not found error: $what"

  override def toJson =
    JObject(TypeField -> JString("notFound"), "what" -> JString(what.v))

  override def matches(other: CompilationError[ExactT])(implicit ev: T =:= RegexT) = other match {
    case NotFoundError(w) => what.matches(w)
    case _ => false
  }

  override def asRegex(implicit ev: T =:= ExactT) = NotFoundError(RegexT.fromPattern(what.v))
}

case class NotAMemberError[T <: Template](what: T, notAMemberOf: T) extends CompilationError[T] {

  override def toString = s"Not a member error: $what isn't a member of $notAMemberOf"

  override def toJson =
    JObject(
      TypeField -> JString("notAMember"),
      "what" -> JString(what.v),
      "notAMemberOf" -> JString(notAMemberOf.v)
    )

  override def matches(other: CompilationError[ExactT])(implicit ev: T =:= RegexT) = other match {
    case NotAMemberError(w, n) => what.matches(w) && notAMemberOf.matches(n)
    case _ => false
  }

  override def asRegex(implicit ev: T =:= ExactT) = NotAMemberError(RegexT.fromPattern(what.v),
    RegexT.fromPattern(notAMemberOf.v))
}

case class ImplicitNotFoundError[T <: Template](parameter: T, implicitType: T) extends CompilationError[T] {

  override def toString = s"Implicit not found error: for parameter $parameter of type $implicitType"

  override def toJson =
    JObject(
      TypeField -> JString("implicitNotFound"),
      "parameter" -> JString(parameter.v),
      "implicitType" -> JString(implicitType.v)
    )

  override def matches(other: CompilationError[ExactT])(implicit ev: T =:= RegexT) = other match {
    case ImplicitNotFoundError(p, i) => parameter.matches(p) && implicitType.matches(i)
    case _ => false
  }

  override def asRegex(implicit ev: T =:= ExactT) = ImplicitNotFoundError(RegexT.fromPattern(parameter.v),
    RegexT.fromPattern(implicitType.v))
}

case class TypeclassNotFoundError[T <: Template](typeclass: T, forType: T) extends CompilationError[T] {

  override def toString = s"Implicit $typeclass typeclass not found error: for type $forType"

  override def toJson =
    JObject(
      TypeField -> JString("typeclassNotFound"),
      "typeclass" -> JString(typeclass.v),
      "forType" -> JString(forType.v)
    )

  override def matches(other: CompilationError[ExactT])(implicit ev: T =:= RegexT) = other match {
    case TypeclassNotFoundError(p, i) => typeclass.matches(p) && forType.matches(i)
    case _ => false
  }

  override def asRegex(implicit ev: T =:= ExactT) = TypeclassNotFoundError(RegexT.fromPattern(typeclass.v),
    RegexT.fromPattern(forType.v))
}

case class DivergingImplicitExpansionError[T <: Template](forType: T, startingWith: T, in: T) extends CompilationError[T] {

  override def toString = s"Diverging implicit expansion error: for type $forType starting with $startingWith in $in"

  override def toJson =
    JObject(
      TypeField -> JString("divergingImplicitExpansion"),
      "forType" -> JString(forType.v),
      "startingWith" -> JString(startingWith.v),
      "in" -> JString(in.v)
    )

  override def matches(other: CompilationError[ExactT])(implicit ev: T =:= RegexT) = other match {
    case DivergingImplicitExpansionError(f, s, i) => forType.matches(f) && startingWith.matches(s) && in.matches(i)
    case _ => false
  }

  override def asRegex(implicit ev: T =:= ExactT) = DivergingImplicitExpansionError(
    RegexT.fromPattern(forType.v), RegexT.fromPattern(startingWith.v), RegexT.fromPattern(in.v))
}

case class TypeArgumentsDoNotConformToOverloadedBoundsError[T <: Template](typeArgs: T, alternativesOf: T, alternatives: Set[T]) extends CompilationError[T] {

  override def toString = s"Type arguments: $typeArgs for overloaded: $alternativesOf do not conform to any bounds: ${alternatives.map(_.toString).mkString("  ")}"

  override def toJson =
    JObject(
      TypeField -> JString("typeArgumentsDoNotConformToOverloadedBounds"),
      "typeArgs" -> JString(typeArgs.v),
      "alternativesOf" -> JString(alternativesOf.v),
      "alternatives" -> JArray(alternatives.map(a => JString(a.v)).toList)
    )

  override def matches(other: CompilationError[ExactT])(implicit ev: T =:= RegexT) = other match {
    case TypeArgumentsDoNotConformToOverloadedBoundsError(t, af, a) => typeArgs.matches(t) &&
      alternativesOf.matches(af) && RegexT.setMatches(alternatives.map(ev.apply), a)
    case _ => false
  }

  override def asRegex(implicit ev: T =:= ExactT) = TypeArgumentsDoNotConformToOverloadedBoundsError(
    RegexT.fromPattern(typeArgs.v), RegexT.fromPattern(alternativesOf.v), alternatives.map(a => RegexT.fromPattern(a.v)))
}

object CompilationError {
  val TypeField = "type"

  def fromJsonString(s: String): Option[CompilationError[RegexT]] = fromJson(parse(s))

  def fromJson(jvalue: JValue): Option[CompilationError[RegexT]] = {

    def regexTFromJson(fields: List[JField], name: String): Option[RegexT] =
      (for {
        JField(`name`, JString(v)) <- fields
      } yield RegexT.fromRegex(v)).headOption

    def multipleRegexTFromJson(fields: List[JField], name: String): Option[Set[RegexT]] =
      (for {
        JField(`name`, JArray(vv)) <- fields
      } yield vv.collect { case JString(v) => RegexT.fromRegex(v) }.toSet).headOption

    def extractWithType(typeValue: String, fields: List[JField]): Option[CompilationError[RegexT]] = typeValue match {
      case "typeMismatch" =>
        for {
          found <- regexTFromJson(fields, "found")
          foundExpandsTo = regexTFromJson(fields, "foundExpandsTo")
          required <- regexTFromJson(fields, "required")
          requiredExpandsTo = regexTFromJson(fields, "requiredExpandsTo")
        } yield TypeMismatchError(found, foundExpandsTo, required, requiredExpandsTo, None)

      case "notFound" =>
        for {
          what <- regexTFromJson(fields, "what")
        } yield NotFoundError(what)

      case "notAMember" =>
        for {
          what <- regexTFromJson(fields, "what")
          notAMemberOf <- regexTFromJson(fields, "notAMemberOf")
        } yield NotAMemberError(what, notAMemberOf)

      case "implicitNotFound" =>
        for {
          parameter <- regexTFromJson(fields, "parameter")
          implicitType <- regexTFromJson(fields, "implicitType")
        } yield ImplicitNotFoundError(parameter, implicitType)

      case "divergingImplicitExpansion" =>
        for {
          forType <- regexTFromJson(fields, "forType")
          startingWith <- regexTFromJson(fields, "startingWith")
          in <- regexTFromJson(fields, "in")
        } yield DivergingImplicitExpansionError(forType, startingWith, in)

      case "typeArgumentsDoNotConformToOverloadedBounds" =>
        for {
          typeArgs <- regexTFromJson(fields, "typeArgs")
          alternativesOf <- regexTFromJson(fields, "alternativesOf")
          alternatives <- multipleRegexTFromJson(fields, "alternatives")
        } yield TypeArgumentsDoNotConformToOverloadedBoundsError(typeArgs, alternativesOf, alternatives)

      case "typeclassNotFound" =>
        for {
          typeclass <- regexTFromJson(fields, "typeclass")
          forType <- regexTFromJson(fields, "forType")
        } yield TypeclassNotFoundError(typeclass, forType)

      case _ => None
    }

    (for {
      JObject(fields) <- jvalue
      JField(TypeField, JString(typeValue)) <- fields
      v <- extractWithType(typeValue, fields).toList
    } yield v).headOption
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy