diff --git a/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala b/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala index 24c46cd98e4c..e8f8a80e1a0d 100644 --- a/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala +++ b/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala @@ -11,6 +11,8 @@ import Constants._ import Types._ import Decorators._ +import scala.collection.mutable + class RepeatableAnnotations extends MiniPhase: override def phaseName: String = RepeatableAnnotations.name @@ -28,7 +30,7 @@ class RepeatableAnnotations extends MiniPhase: tree private def aggregateAnnotations(annotations: Seq[Annotation])(using Context): List[Annotation] = - val annsByType = annotations.groupBy(_.symbol) + val annsByType = stableGroupBy(annotations, _.symbol) annsByType.flatMap { case (_, a :: Nil) => a :: Nil case (sym, anns) if sym.derivesFrom(defn.ClassfileAnnotationClass) => @@ -50,6 +52,14 @@ class RepeatableAnnotations extends MiniPhase: case (_, anns) => anns }.toList + private def stableGroupBy[A, K](ins: Seq[A], f: A => K): scala.collection.MapView[K, List[A]] = + val out = new mutable.LinkedHashMap[K, mutable.ListBuffer[A]]() + for (in <- ins) { + val buffer = out.getOrElseUpdate(f(in), new mutable.ListBuffer) + buffer += in + } + out.view.mapValues(_.toList) + object RepeatableAnnotations: val name: String = "repeatableAnnotations" val description: String = "aggregate repeatable annotations" diff --git a/tests/pos/Annotations.scala b/tests/pos/Annotations.scala new file mode 100644 index 000000000000..322cf2eafb06 --- /dev/null +++ b/tests/pos/Annotations.scala @@ -0,0 +1,8 @@ +package foo.bar + +import jdk.jfr.Enabled + +@Enabled +@Deprecated +final class Annotations { +} diff --git a/tests/pos/annotations1/a.scala b/tests/pos/annotations1/a.scala new file mode 100644 index 000000000000..8d0df5a2cac5 --- /dev/null +++ b/tests/pos/annotations1/a.scala @@ -0,0 +1,2 @@ +class Annot1(s: String) extends scala.annotation.StaticAnnotation +class Annot2(s: Class[_]) extends scala.annotation.StaticAnnotation diff --git a/tests/pos/annotations1/b.scala b/tests/pos/annotations1/b.scala new file mode 100644 index 000000000000..03b7d0722dfa --- /dev/null +++ b/tests/pos/annotations1/b.scala @@ -0,0 +1,3 @@ +@Annot1("foo") +@Annot2(classOf[AnyRef]) +class Test diff --git a/tests/pos/annotationsJava/Annot1.java b/tests/pos/annotationsJava/Annot1.java new file mode 100644 index 000000000000..dc75aa3ab772 --- /dev/null +++ b/tests/pos/annotationsJava/Annot1.java @@ -0,0 +1,5 @@ +import java.lang.annotation.*; +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +@interface Annot1 { String value() default ""; } diff --git a/tests/pos/annotationsJava/Annot2.java b/tests/pos/annotationsJava/Annot2.java new file mode 100644 index 000000000000..56d4f2168585 --- /dev/null +++ b/tests/pos/annotationsJava/Annot2.java @@ -0,0 +1,5 @@ +import java.lang.annotation.*; +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +@interface Annot2 { Class value(); } diff --git a/tests/pos/annotationsJava/b.scala b/tests/pos/annotationsJava/b.scala new file mode 100644 index 000000000000..03be7d0e35fd --- /dev/null +++ b/tests/pos/annotationsJava/b.scala @@ -0,0 +1 @@ +@Annot1("foo") @Annot2(classOf[AnyRef]) class Test diff --git a/tests/pos/annotationsJavaRepeatable/Annot1.java b/tests/pos/annotationsJavaRepeatable/Annot1.java new file mode 100644 index 000000000000..60cf37a21f6b --- /dev/null +++ b/tests/pos/annotationsJavaRepeatable/Annot1.java @@ -0,0 +1,13 @@ +import java.lang.annotation.*; + +@Repeatable(Annot1.Container.class) +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@interface Annot1 { String value() default ""; + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public static @interface Container { + Annot1[] value(); + } +} diff --git a/tests/pos/annotationsJavaRepeatable/Annot2.java b/tests/pos/annotationsJavaRepeatable/Annot2.java new file mode 100644 index 000000000000..fb3c7915b0c9 --- /dev/null +++ b/tests/pos/annotationsJavaRepeatable/Annot2.java @@ -0,0 +1,6 @@ +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +@interface Annot2 { Class value(); } diff --git a/tests/pos/annotationsJavaRepeatable/b.scala b/tests/pos/annotationsJavaRepeatable/b.scala new file mode 100644 index 000000000000..6c775fdecf6c --- /dev/null +++ b/tests/pos/annotationsJavaRepeatable/b.scala @@ -0,0 +1 @@ +@Annot1("foo") @Annot2(classOf[String]) @Annot1("bar") class Test