Skip to content

Commit 7f5eaa8

Browse files
committed
Only accept singletons, only check arguments
1 parent 9fcfbaf commit 7f5eaa8

File tree

19 files changed

+95
-110
lines changed

19 files changed

+95
-110
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
144144
def allTermArguments(tree: Tree): List[Tree] = unsplice(tree) match {
145145
case Apply(fn, args) => allTermArguments(fn) ::: args
146146
case TypeApply(fn, args) => allTermArguments(fn)
147+
// TOOD(mbovel): is it really safe to skip all blocks here and in `allArguments`?
147148
case Block(_, expr) => allTermArguments(expr)
148149
case _ => Nil
149150
}

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -937,16 +937,15 @@ object Checking {
937937
case Literal(_) => // ok
938938
case _ =>
939939
report.error(em"@${cls.name} needs a string literal as argument", arg.srcPos)
940-
case Apply(tycon, args) => args.foreach(checkAnnotArg)
941940
case _ =>
942-
// `FirstTransform.toTypeTree` creates `Annotated` nodes where the
943-
// annotation tree is not an `Apply` node. For example, in
944-
// tests/neg/i13044.scala, `FirstTransform.toTypeTree` creates two
945-
// annotations with trees `t` and `ts`. In tests/run/t2755.scala,
946-
// `FirstTransform.toTypeTree` creates an annotated types with tree `_`.
947-
//
948-
// report.error(em"unexpected annotation tree: $tree", tree.srcPos)
949-
()
941+
if cls.isRetainsLike then () // Do not check @retain annotations
942+
else if cls == defn.ThrowsAnnot then
943+
// Do not check @throws annotations.
944+
// TODO(mbovel): in tests/run/t6380.scala, an annotation tree is
945+
// `new throws[Exception](throws.<init>[Exception])`. What is this?
946+
()
947+
else
948+
tpd.allTermArguments(tree).foreach(checkAnnotArg)
950949
tree
951950

952951
private def checkAnnotArg(tree: Tree)(using Context): Unit =
@@ -955,40 +954,38 @@ object Checking {
955954

956955
def isFunctionAllowed(t: Tree): Boolean =
957956
t match
958-
case Select(qual, nme.apply) => qual.symbol == defn.ArrayModule || isTupleModule(qual.symbol)
957+
case Select(qual, nme.apply) =>
958+
qual.symbol == defn.ArrayModule
959+
|| qual.symbol == defn.ClassTagModule // class tags are used as arguments to Array.apply
960+
|| isTupleModule(qual.symbol)
961+
case Select(New(clazz), nme.CONSTRUCTOR) => clazz.symbol.isAnnotation
959962
case Apply(fun, _) => isFunctionAllowed(fun)
960963
case TypeApply(fun, _) => isFunctionAllowed(fun)
961964
case _ => false
962965

963966
def valid(t: Tree): Boolean =
964-
t match
965-
case Literal(_) => true
966-
case Apply(fun, args) => isFunctionAllowed(fun) && args.forall(valid)
967-
case SeqLiteral(elems, _) => elems.forall(valid)
968-
case Typed(expr, _) => valid(expr)
969-
case NamedArg(_, arg) => valid(arg)
970-
case _ =>
971-
t.tpe.stripped match
972-
case _: SingletonType => true
973-
// We need to handle type refs for these test cases:
974-
// - tests/pos/dependent-annot.scala
975-
// - tests/pos/i16208.scala
976-
// - tests/run/java-ann-super-class
977-
// - tests/run/java-ann-super-class-separate
978-
// - tests/neg/i19470.scala (@retains)
979-
// Why do we get type refs in these cases?
980-
case _: TypeRef => true
981-
case _: TypeParamRef => true
982-
case tp => false
967+
t.tpe.isEffectivelySingleton
968+
|| (
969+
t match
970+
case Literal(_) => true
971+
case Ident(nme.WILDCARD) => true // example: tests/run/java-ann-super-class
972+
case Apply(fun, args) => isFunctionAllowed(fun) && args.forall(valid)
973+
case TypeApply(fun, args) => isFunctionAllowed(fun)
974+
//case TypeApply(meth @ Select(arg, _), _) if meth.symbol == defn.Any_asInstanceOf => valid(arg)
975+
case SeqLiteral(elems, _) => elems.forall(valid)
976+
case Typed(expr, _) => valid(expr)
977+
case NamedArg(_, arg) => valid(arg)
978+
case _ =>
979+
false
980+
)
983981

984982
if !valid(tree) then
985983
report.error(
986-
s"""Implementation restriction: not a valid annotation argument.
984+
i"""Implementation restriction: not a valid annotation argument.
987985
| Argument: $tree
988-
| Type: ${tree.tpe}""".stripMargin,
986+
| Type: ${tree.tpe}""",
989987
tree.srcPos
990988
)
991-
992989
}
993990

994991
trait Checking {

tests/neg/i15054.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.annotation.Annotation
2+
3+
class AnAnnotation(function: Int => String) extends Annotation
4+
5+
@AnAnnotation(_.toString) // error: not a valid annotation
6+
val a = 1
7+
@AnAnnotation(_.toString.length.toString) // error: not a valid annotation
8+
val b = 2
9+
10+
def test =
11+
@AnAnnotation(_.toString) // error: not a valid annotation
12+
val a = 1
13+
@AnAnnotation(_.toString.length.toString) // error: not a valid annotation
14+
val b = 2
15+
a + b

tests/neg/i7740a.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class A(a: Any) extends annotation.StaticAnnotation
2+
@A({val x = 0}) trait B // error: not a valid annotation

tests/neg/i7740b.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class A(a: Any) extends annotation.StaticAnnotation
2+
@A({def x = 0}) trait B // error: not a valid annotation
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
final class fooAnnot[T](member: T) extends scala.annotation.StaticAnnotation // must have type parameter
22

3-
@fooAnnot(new RecAnnotated {}) // must pass instance of anonymous subclass
3+
@fooAnnot(new RecAnnotated {}) // error: not a valid annotation
44
trait RecAnnotated

tests/neg/nowarn.check

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
| its body in a block; no exceptions are handled.
66
|
77
| longer explanation available when compiling with `-explain`
8-
-- [E002] Syntax Warning: tests/neg/nowarn.scala:25:25 -----------------------------------------------------------------
9-
25 |@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant)
8+
-- [E002] Syntax Warning: tests/neg/nowarn.scala:22:25 -----------------------------------------------------------------
9+
22 |@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant)
1010
| ^^^^^
1111
| A try without catch or finally is equivalent to putting
1212
| its body in a block; no exceptions are handled.
1313
|
1414
| longer explanation available when compiling with `-explain`
15-
-- [E002] Syntax Warning: tests/neg/nowarn.scala:33:26 -----------------------------------------------------------------
16-
33 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id)
15+
-- [E002] Syntax Warning: tests/neg/nowarn.scala:30:26 -----------------------------------------------------------------
16+
30 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id)
1717
| ^^^^^
1818
| A try without catch or finally is equivalent to putting
1919
| its body in a block; no exceptions are handled.
2020
|
2121
| longer explanation available when compiling with `-explain`
22-
-- [E002] Syntax Warning: tests/neg/nowarn.scala:35:28 -----------------------------------------------------------------
23-
35 |@nowarn("verbose") def t5 = try 1 // warning with details
22+
-- [E002] Syntax Warning: tests/neg/nowarn.scala:32:28 -----------------------------------------------------------------
23+
32 |@nowarn("verbose") def t5 = try 1 // warning with details
2424
| ^^^^^
2525
| A try without catch or finally is equivalent to putting
2626
| its body in a block; no exceptions are handled.
@@ -40,71 +40,61 @@ Matching filters for @nowarn or -Wconf:
4040
| ^^^^^^
4141
| Invalid message filter
4242
| unknown filter: wat?
43-
-- [E129] Potential Issue Warning: tests/neg/nowarn.scala:18:12 --------------------------------------------------------
44-
18 |def t2a = { 1; 2 } // warning (invalid nowarn doesn't silence)
45-
| ^
46-
| A pure expression does nothing in statement position
47-
|
48-
| longer explanation available when compiling with `-explain`
49-
-- Warning: tests/neg/nowarn.scala:17:8 --------------------------------------------------------------------------------
50-
17 |@nowarn(t1a.toString) // warning (typer, argument not a compile-time constant)
51-
| ^^^^^^^^^^^^
52-
| filter needs to be a compile-time constant string
53-
-- Warning: tests/neg/nowarn.scala:25:10 -------------------------------------------------------------------------------
54-
25 |@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant)
43+
-- Warning: tests/neg/nowarn.scala:22:10 -------------------------------------------------------------------------------
44+
22 |@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant)
5545
| ^^^^^
5646
| filter needs to be a compile-time constant string
57-
-- Deprecation Warning: tests/neg/nowarn.scala:39:10 -------------------------------------------------------------------
58-
39 |def t6a = f // warning (refchecks, deprecation)
47+
-- Deprecation Warning: tests/neg/nowarn.scala:36:10 -------------------------------------------------------------------
48+
36 |def t6a = f // warning (refchecks, deprecation)
5949
| ^
6050
| method f is deprecated
61-
-- Deprecation Warning: tests/neg/nowarn.scala:42:30 -------------------------------------------------------------------
62-
42 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation)
51+
-- Deprecation Warning: tests/neg/nowarn.scala:39:30 -------------------------------------------------------------------
52+
39 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation)
6353
| ^
6454
| method f is deprecated
65-
-- Deprecation Warning: tests/neg/nowarn.scala:49:10 -------------------------------------------------------------------
66-
49 |def t7c = f // warning (deprecation)
55+
-- Deprecation Warning: tests/neg/nowarn.scala:46:10 -------------------------------------------------------------------
56+
46 |def t7c = f // warning (deprecation)
6757
| ^
6858
| method f is deprecated
69-
-- [E092] Pattern Match Unchecked Warning: tests/neg/nowarn.scala:55:7 -------------------------------------------------
70-
55 | case _: List[Int] => 0 // warning (patmat, unchecked)
59+
-- [E092] Pattern Match Unchecked Warning: tests/neg/nowarn.scala:52:7 -------------------------------------------------
60+
52 | case _: List[Int] => 0 // warning (patmat, unchecked)
7161
| ^
7262
|the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from Any
7363
|
7464
| longer explanation available when compiling with `-explain`
75-
-- Error: tests/neg/nowarn.scala:33:1 ----------------------------------------------------------------------------------
76-
33 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id)
65+
-- Error: tests/neg/nowarn.scala:30:1 ----------------------------------------------------------------------------------
66+
30 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id)
7767
|^^^^^^^^^^^^^^^
7868
|@nowarn annotation does not suppress any warnings
79-
-- Error: tests/neg/nowarn.scala:42:1 ----------------------------------------------------------------------------------
80-
42 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation)
69+
-- Error: tests/neg/nowarn.scala:39:1 ----------------------------------------------------------------------------------
70+
39 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation)
8171
|^^^^^^^^^^^^^^^^^^^
8272
|@nowarn annotation does not suppress any warnings
83-
-- Error: tests/neg/nowarn.scala:50:5 ----------------------------------------------------------------------------------
84-
50 | : @nowarn("msg=fish") // error (unused nowarn)
73+
-- Error: tests/neg/nowarn.scala:47:5 ----------------------------------------------------------------------------------
74+
47 | : @nowarn("msg=fish") // error (unused nowarn)
8575
| ^^^^^^^^^^^^^^^^^^^
8676
| @nowarn annotation does not suppress any warnings
87-
-- Error: tests/neg/nowarn.scala:62:0 ----------------------------------------------------------------------------------
88-
62 |@nowarn def t9a = { 1: @nowarn; 2 } // error (outer @nowarn is unused)
77+
-- Error: tests/neg/nowarn.scala:59:0 ----------------------------------------------------------------------------------
78+
59 |@nowarn def t9a = { 1: @nowarn; 2 } // error (outer @nowarn is unused)
8979
|^^^^^^^
9080
|@nowarn annotation does not suppress any warnings
91-
-- Error: tests/neg/nowarn.scala:63:27 ---------------------------------------------------------------------------------
92-
63 |@nowarn def t9b = { 1: Int @nowarn; 2 } // error (inner @nowarn is unused, it covers the type, not the expression)
81+
-- Error: tests/neg/nowarn.scala:60:27 ---------------------------------------------------------------------------------
82+
60 |@nowarn def t9b = { 1: Int @nowarn; 2 } // error (inner @nowarn is unused, it covers the type, not the expression)
9383
| ^^^^^^^
9484
| @nowarn annotation does not suppress any warnings
95-
-- Error: tests/neg/nowarn.scala:68:0 ----------------------------------------------------------------------------------
96-
68 |@nowarn @ann(f) def t10b = 0 // error (unused nowarn)
85+
-- Error: tests/neg/nowarn.scala:65:0 ----------------------------------------------------------------------------------
86+
65 |@nowarn @ann(f) def t10b = 0 // error (unused nowarn)
9787
|^^^^^^^
9888
|@nowarn annotation does not suppress any warnings
99-
-- Error: tests/neg/nowarn.scala:69:8 ----------------------------------------------------------------------------------
100-
69 |@ann(f: @nowarn) def t10c = 0 // error (unused nowarn), should be silent
89+
-- Error: tests/neg/nowarn.scala:66:8 ----------------------------------------------------------------------------------
90+
66 |@ann(f: @nowarn) def t10c = 0 // error (unused nowarn), should be silent
10191
| ^^^^^^^
10292
| @nowarn annotation does not suppress any warnings
103-
-- Error: tests/neg/nowarn.scala:72:0 ----------------------------------------------------------------------------------
104-
72 |@nowarn class I1a { // error (unused nowarn)
93+
-- Error: tests/neg/nowarn.scala:69:0 ----------------------------------------------------------------------------------
94+
69 |@nowarn class I1a { // error (unused nowarn)
10595
|^^^^^^^
10696
|@nowarn annotation does not suppress any warnings
107-
-- Error: tests/neg/nowarn.scala:77:0 ----------------------------------------------------------------------------------
108-
77 |@nowarn class I1b { // error (unused nowarn)
97+
-- Error: tests/neg/nowarn.scala:74:0 ----------------------------------------------------------------------------------
98+
74 |@nowarn class I1b { // error (unused nowarn)
10999
|^^^^^^^
110100
|@nowarn annotation does not suppress any warnings

tests/neg/nowarn.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ def t1a = try 1 // warning (parser)
1414
@nowarn("wat?") // warning (typer, invalid filter)
1515
def t2 = { 1; 2 } // warning (the invalid nowarn doesn't silence anything)
1616

17-
@nowarn(t1a.toString) // warning (typer, argument not a compile-time constant)
18-
def t2a = { 1; 2 } // warning (invalid nowarn doesn't silence)
19-
2017
object o:
2118
final val const = "msg=try"
2219
inline def inl = "msg=try"

tests/neg/serialversionuid-not-const.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
@SerialVersionUID(13l.toLong) class C1 extends Serializable // OK because toLong is constant-folded
22
@SerialVersionUID(13l) class C2 extends Serializable // OK
3-
@SerialVersionUID(13.asInstanceOf[Long]) class C3 extends Serializable // error
3+
4+
//@SerialVersionUID(13.asInstanceOf[Long]) class C3 extends Serializable
5+
//now catched in typer already: not a valid annotation argument
6+
47
@SerialVersionUID(Test.bippy) class C4 extends Serializable // error
58

69
object Test {

tests/pos/t1942/A_1.scala renamed to tests/neg/t1942.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ class ann(x: Int) extends annotation.StaticAnnotation
77

88
class t {
99
val a = new A
10-
@ann(a.foo(1)) def bar = 1
10+
@ann(a.foo(1)) def bar = 1 // error: not a valid annotation
1111
}

0 commit comments

Comments
 (0)