Skip to content

Commit

Permalink
Allow lambdas in annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
mbovel committed Jan 13, 2025
1 parent a4afa21 commit de0e3e7
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 64 deletions.
30 changes: 23 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,7 @@ trait Checking {
report.error(em"@${cls.name} needs a string literal as argument", arg.srcPos)
tree
case _ =>
tree.find(!isValidAnnotSubtree(_)) match
findInvalidAnnotSubTree(tree) match
case None => tree
case Some(invalidSubTree) =>
errorTree(
Expand All @@ -1441,11 +1441,9 @@ trait Checking {
invalidSubTree.srcPos
)

/** Returns `true` if this tree can appear inside an annotation argument. */
private def isValidAnnotSubtree(subTree: Tree) =
subTree.isType || subTree.isInstanceOf[
EmptyTree.type
| Ident
private def findInvalidAnnotSubTree(tree: Tree)(using Context): Option[Tree] =
type ValidAnnotTree =
Ident
| Select
| This
| Super
Expand All @@ -1461,7 +1459,25 @@ trait Checking {
| Inlined
| Hole
| Annotated
]
| EmptyTree.type

val accumulator = new TreeAccumulator[Option[Tree]]:
def apply(acc: Option[Tree], tree: Tree)(using Context): Option[Tree] =
if acc.isDefined then acc
else
tree match
case tree if tree.isType => foldOver(acc, tree)
case closureDef(meth) =>
val paramsRes =
meth.paramss.foldLeft(acc): (acc: Option[Tree], params: List[ValDef] | List[TypeDef]) =>
params.foldLeft(acc): (acc: Option[Tree], param: ValDef | TypeDef) =>
foldOver(acc, param)
foldOver(paramsRes, meth.rhs)
case tree: ValidAnnotTree => foldOver(acc, tree)
case _ => Some(tree)

accumulator(None, tree)


/** 1. Check that all case classes that extend `scala.reflect.Enum` are `enum` cases
* 2. Check that parameterised `enum` cases do not extend java.lang.Enum.
Expand Down
48 changes: 12 additions & 36 deletions tests/neg/annot-invalid.check
Original file line number Diff line number Diff line change
Expand Up @@ -22,62 +22,38 @@
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: def f: Int = 2
| Type: (f : => Int)
-- Error: tests/neg/annot-invalid.scala:13:30 --------------------------------------------------------------------------
13 | val x5: Int @annot((x: Int) => x) = 0 // error
| ^^^^^^^^^^^^^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: closure($anonfun)
| Type: Int => Int
-- Error: tests/neg/annot-invalid.scala:14:21 --------------------------------------------------------------------------
14 | val x6: Int @annot(O.g) = 0 // error
| ^^^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: closure($anonfun)
| Type: Int => Int
-- Error: tests/neg/annot-invalid.scala:16:25 --------------------------------------------------------------------------
16 | val x7: Int @annot('{4}) = 0 // error
-- Error: tests/neg/annot-invalid.scala:14:25 --------------------------------------------------------------------------
14 | val x5: Int @annot('{4}) = 0 // error
| ^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: '{4}
| Type: (scala.quoted.Quotes) ?=> scala.quoted.Expr[Int]
-- Error: tests/neg/annot-invalid.scala:18:9 ---------------------------------------------------------------------------
18 | @annot(new Object {}) val y1: Int = 0 // error
-- Error: tests/neg/annot-invalid.scala:16:9 ---------------------------------------------------------------------------
16 | @annot(new Object {}) val y1: Int = 0 // error
| ^^^^^^^^^^^^^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: final class $anon() extends Object() {}
| Type: Object {...}
-- Error: tests/neg/annot-invalid.scala:19:16 --------------------------------------------------------------------------
19 | @annot({class C}) val y2: Int = 0 // error
-- Error: tests/neg/annot-invalid.scala:17:16 --------------------------------------------------------------------------
17 | @annot({class C}) val y2: Int = 0 // error
| ^^^^^^^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: class C() extends Object() {}
| Type: C
-- Error: tests/neg/annot-invalid.scala:20:14 --------------------------------------------------------------------------
20 | @annot({val y: Int = 2}) val y3: Int = 0 // error
-- Error: tests/neg/annot-invalid.scala:18:14 --------------------------------------------------------------------------
18 | @annot({val y: Int = 2}) val y3: Int = 0 // error
| ^^^^^^^^^^^^^^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: val y: Int = 2
| Type: (y : Int)
-- Error: tests/neg/annot-invalid.scala:21:14 --------------------------------------------------------------------------
21 | @annot({def f = 2}) val y4: Int = 0 // error
-- Error: tests/neg/annot-invalid.scala:19:14 --------------------------------------------------------------------------
19 | @annot({def f = 2}) val y4: Int = 0 // error
| ^^^^^^^^^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: def f: Int = 2
| Type: (f : => Int)
-- Error: tests/neg/annot-invalid.scala:22:18 --------------------------------------------------------------------------
22 | @annot((x: Int) => x) val y5: Int = 0 // error
| ^^^^^^^^^^^^^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: closure($anonfun)
| Type: Int => Int
-- Error: tests/neg/annot-invalid.scala:23:9 ---------------------------------------------------------------------------
23 | @annot(O.g) val y6: Int = 0 // error
| ^^^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: closure($anonfun)
| Type: Int => Int
-- Error: tests/neg/annot-invalid.scala:25:13 --------------------------------------------------------------------------
25 | @annot('{4}) val y7: Int = 0 // error
-- Error: tests/neg/annot-invalid.scala:21:13 --------------------------------------------------------------------------
21 | @annot('{4}) val y5: Int = 0 // error
| ^
| Implementation restriction: expression cannot be used inside an annotation argument.
| Tree: '{4}
Expand Down
8 changes: 2 additions & 6 deletions tests/neg/annot-invalid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@ def main =
val x2: Int @annot({class C}) = 0 // error
val x3: Int @annot({val y: Int = 2}) = 0 // error
val x4: Int @annot({def f = 2}) = 0 // error
val x5: Int @annot((x: Int) => x) = 0 // error
val x6: Int @annot(O.g) = 0 // error
def withQuotes(using Quotes) =
val x7: Int @annot('{4}) = 0 // error
val x5: Int @annot('{4}) = 0 // error

@annot(new Object {}) val y1: Int = 0 // error
@annot({class C}) val y2: Int = 0 // error
@annot({val y: Int = 2}) val y3: Int = 0 // error
@annot({def f = 2}) val y4: Int = 0 // error
@annot((x: Int) => x) val y5: Int = 0 // error
@annot(O.g) val y6: Int = 0 // error
def withQuotes2(using Quotes) =
@annot('{4}) val y7: Int = 0 // error
@annot('{4}) val y5: Int = 0 // error

()
15 changes: 0 additions & 15 deletions tests/neg/i15054.scala

This file was deleted.

15 changes: 15 additions & 0 deletions tests/pos/annot-15054.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.annotation.Annotation

class AnAnnotation(function: Int => String) extends Annotation

@AnAnnotation(_.toString)
val a = 1
@AnAnnotation(_.toString.length.toString)
val b = 2

def test =
@AnAnnotation(_.toString)
val a = 1
@AnAnnotation(_.toString.length.toString)
val b = 2
a + b
6 changes: 6 additions & 0 deletions tests/pos/annot-valid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ def main =
val x13: Int @annot(throw new Error()) = 0
val x14: Int @annot(42: Double) = 0
val x15: Int @annot(O.g(2)) = 0
val x16: Int @annot((x: Int) => x) = 0
val x17: Int @annot([T] => (x: T) => x) = 0
val x18: Int @annot(O.g) = 0

@annot(42) val y1: Int = 0
@annot("hello") val y2: Int = 0
Expand All @@ -38,5 +41,8 @@ def main =
@annot(throw new Error()) val y13: Int = 0
@annot(42: Double) val y14: Int = 0
@annot(O.g(2)) val y15: Int = 0
@annot((x: Int) => x) val y16: Int = 0
@annot([T] => (x: T) => x) val y17: Int = 0
@annot(O.g) val y18: Int = 0

()

0 comments on commit de0e3e7

Please sign in to comment.