Skip to content

Unable to match a lower-case constructor in quoted pattern #22616

Open
@Kalin-Rudnicki

Description

@Kalin-Rudnicki

Compiler version

3.6.3

Minimized code

final case class caseName(name: String) extends scala.annotation.Annotation
object caseName {

  given FromExpr[caseName] =
    new FromExpr[caseName] {
      override def unapply(x: Expr[caseName])(using Quotes): Option[caseName] =
        x match {
          case '{ caseName(${ Expr(name) }) }     => caseName(name).some
          // with/without the following line...
          case '{ new caseName(${ Expr(name) }) } => caseName(name).some
          case _                                  => println(x.show); None
        }
    }

}
sealed trait SealedTrait3[+A, +B] derives Show
object SealedTrait3 {
  final case class AB1[+B, +A](a: B, b: A) extends SealedTrait3[B, A]
  final case class AB2[+C, +D](a: C, b: D) extends SealedTrait3[D, C]
  final case class A[+A](a: A) extends SealedTrait3[A, Nothing]
  @caseName("_B_") final case class B[+B](b: B) extends SealedTrait3[Nothing, B]
  case object Neither extends SealedTrait3[Nothing, Nothing]
}
    def optionalAnnotation[Annot: Type]: Option[Expr[Annot]] = {
      val annotTpe = TypeRepr.of[Annot]
      val annotFlags = annotTpe.typeSymbol.flags

      if (annotFlags.is(Flags.Abstract) || annotFlags.is(Flags.Trait))
        report.errorAndAbort(s"Bad annotation type ${annotTpe.show} is abstract")

      this.getAnnotation(annotTpe.typeSymbol) match
        case Some(tree) if tree.tpe <:< annotTpe => tree.asExprOf[Annot].some
        case _                                   => None
    }

    def requiredAnnotation[Annot: Type]: Expr[Annot] =
      optionalAnnotation[Annot].getOrElse(report.errorAndAbort(s"Missing required annotation `${TypeRepr.of[Annot].show}` for `$this`"))

    def optionalAnnotationValue[Annot: {Type, FromExpr}]: Option[Annot] =
      optionalAnnotation[Annot].map { expr =>
        expr.value.getOrElse(report.errorAndAbort(s"Found annotation `${TypeRepr.of[Annot].show}` for `$this`, but are unable to extract Expr.value\n${expr.show}"))
      }

    def requiredAnnotationValue[Annot: {Type, FromExpr}]: Annot = {
      val expr = requiredAnnotation[Annot]
      expr.value.getOrElse(report.errorAndAbort(s"Found annotation `${TypeRepr.of[Annot].show}` for `$this`, but are unable to extract Expr.value\n${expr.show}"))
    }

Output

[error]    |Found annotation `oxygen.meta.example.caseName` for `oxygen.meta.example.SealedTrait3.B[B]`, but are unable to extract Expr.value
[error]    |new oxygen.meta.example.caseName("_B_")

then add a case for that in the FromExpr, and...

[error] 28 |          case '{ new caseName(${ Expr(name) }) } => caseName(name).some
[error]    |                      ^^^^^^^^
[error]    |                      caseName does not have a constructor

Expectation

That either:

  1. case '{ caseName(${ Expr(name) }) } => caseName(name).some matches
  2. case '{ new caseName(${ Expr(name) }) } => caseName(name).some is allowed, and matches

Quite difficult to handle when you see "we found this, but cant match on it", and then when you try to match on the exact expr.show, it says that expr is not valid?

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions