diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index aa8ead280bbf..532ec936f7ec 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -2,12 +2,13 @@ package dotty.tools package dotc package core -import Symbols._, Types._, Contexts._, Constants._ -import dotty.tools.dotc.ast.tpd, tpd.* +import Symbols._, Types._, Contexts._, Constants._, Phases.* +import ast.tpd, tpd.* import util.Spans.Span import printing.{Showable, Printer} import printing.Texts.Text -import annotation.internal.sharable + +import scala.annotation.internal.sharable object Annotations { @@ -87,6 +88,22 @@ object Annotations { def sameAnnotation(that: Annotation)(using Context): Boolean = symbol == that.symbol && tree.sameTree(that.tree) + def hasOneOfMetaAnnotation(metaSyms: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Boolean = atPhaseNoLater(erasurePhase) { + def go(metaSyms: Set[Symbol]) = + def recTp(tp: Type): Boolean = tp.dealiasKeepAnnots match + case AnnotatedType(parent, metaAnnot) => metaSyms.exists(metaAnnot.matches) || recTp(parent) + case _ => false + def rec(tree: Tree): Boolean = methPart(tree) match + case New(tpt) => rec(tpt) + case Select(qual, _) => rec(qual) + case Annotated(arg, metaAnnot) => metaSyms.exists(metaAnnot.tpe.classSymbol.derivesFrom) || rec(arg) + case t @ Ident(_) => recTp(t.tpe) + case Typed(expr, _) => rec(expr) + case _ => false + metaSyms.exists(symbol.hasAnnotation) || rec(tree) + go(metaSyms) || orNoneOf.nonEmpty && !go(orNoneOf) + } + /** Operations for hash-consing, can be overridden */ def hash: Int = System.identityHashCode(this) def eql(that: Annotation) = this eq that diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index bad796df2bd5..360132eaeb8f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1021,6 +1021,8 @@ class Definitions { @tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance") @tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile") @tu lazy val WithPureFunsAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WithPureFuns") + @tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter") + @tu lazy val BeanSetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanSetter") @tu lazy val FieldMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.field") @tu lazy val GetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.getter") @tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param") @@ -1037,8 +1039,10 @@ class Definitions { @tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable") // A list of meta-annotations that are relevant for fields and accessors - @tu lazy val FieldAccessorMetaAnnots: Set[Symbol] = + @tu lazy val NonBeanMetaAnnots: Set[Symbol] = Set(FieldMetaAnnot, GetterMetaAnnot, ParamMetaAnnot, SetterMetaAnnot) + @tu lazy val MetaAnnots: Set[Symbol] = + NonBeanMetaAnnots + BeanGetterMetaAnnot + BeanSetterMetaAnnot // A list of annotations that are commonly used to indicate that a field/method argument or return // type is not null. These annotations are used by the nullification logic in JavaNullInterop to diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 134781f7de19..414ab3fa3c14 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -252,6 +252,15 @@ object SymDenotations { final def filterAnnotations(p: Annotation => Boolean)(using Context): Unit = annotations = annotations.filterConserve(p) + def annotationsCarrying(meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): List[Annotation] = + annotations.filterConserve(_.hasOneOfMetaAnnotation(meta, orNoneOf = orNoneOf)) + + def copyAndKeepAnnotationsCarrying(phase: DenotTransformer, meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Unit = + if annotations.nonEmpty then + val cpy = copySymDenotation() + cpy.annotations = annotationsCarrying(meta, orNoneOf = orNoneOf) + cpy.installAfter(phase) + /** Optionally, the annotation matching the given class symbol */ final def getAnnotation(cls: Symbol)(using Context): Option[Annotation] = dropOtherAnnotations(annotations, cls) match { diff --git a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala index 0d464d319848..0c1f40d4f2bd 100644 --- a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala +++ b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala @@ -5,7 +5,8 @@ import core._ import ast.tpd._ import Annotations._ import Contexts._ -import Symbols.newSymbol +import Symbols.* +import SymUtils.* import Decorators._ import Flags._ import Names._ @@ -23,8 +24,6 @@ class BeanProperties(thisPhase: DenotTransformer): } ::: origBody) def generateAccessors(valDef: ValDef)(using Context): List[Tree] = - import Symbols.defn - def generateGetter(valDef: ValDef, annot: Annotation)(using Context) : Tree = val prefix = if annot matches defn.BooleanBeanPropertyAnnot then "is" else "get" val meth = newSymbol( @@ -34,9 +33,9 @@ class BeanProperties(thisPhase: DenotTransformer): info = MethodType(Nil, valDef.denot.info), coord = annot.tree.span ).enteredAfter(thisPhase).asTerm - meth.addAnnotations(valDef.symbol.annotations) + .withAnnotationsCarrying(valDef.symbol, defn.BeanGetterMetaAnnot) val body: Tree = ref(valDef.symbol) - DefDef(meth, body) + DefDef(meth, body).withSpan(meth.span) def maybeGenerateSetter(valDef: ValDef, annot: Annotation)(using Context): Option[Tree] = Option.when(valDef.denot.asSymDenotation.flags.is(Mutable)) { @@ -48,9 +47,9 @@ class BeanProperties(thisPhase: DenotTransformer): info = MethodType(valDef.name :: Nil, valDef.denot.info :: Nil, defn.UnitType), coord = annot.tree.span ).enteredAfter(thisPhase).asTerm - meth.addAnnotations(valDef.symbol.annotations) + .withAnnotationsCarrying(valDef.symbol, defn.BeanSetterMetaAnnot) def body(params: List[List[Tree]]): Tree = Assign(ref(valDef.symbol), params.head.head) - DefDef(meth, body) + DefDef(meth, body).withSpan(meth.span) } def prefixedName(prefix: String, valName: Name) = diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 6456066bfdb0..5a2eda4101a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -4,7 +4,7 @@ package transform import core._ import DenotTransformers._ import Contexts._ -import Phases.phaseOf +import Phases.* import SymDenotations.SymDenotation import Denotations._ import Symbols._ @@ -114,26 +114,10 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => flags = Private | (if (sym.is(StableRealizable)) EmptyFlags else Mutable), info = fieldType, coord = tree.span - ).withAnnotationsCarrying(sym, defn.FieldMetaAnnot) + ).withAnnotationsCarrying(sym, defn.FieldMetaAnnot, orNoneOf = defn.MetaAnnots) .enteredAfter(thisPhase) } - def addAnnotations(denot: Denotation): Unit = - denot match { - case fieldDenot: SymDenotation if sym.annotations.nonEmpty => - val cpy = fieldDenot.copySymDenotation() - cpy.annotations = sym.annotations - cpy.installAfter(thisPhase) - case _ => () - } - - def removeUnwantedAnnotations(denot: SymDenotation, metaAnnotSym: ClassSymbol): Unit = - if (sym.annotations.nonEmpty) { - val cpy = sym.copySymDenotation() - cpy.filterAnnotations(_.symbol.hasAnnotation(metaAnnotSym)) - cpy.installAfter(thisPhase) - } - val NoFieldNeeded = Lazy | Deferred | JavaDefined | Inline def erasedBottomTree(sym: Symbol) = @@ -183,8 +167,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => if isErasableBottomField(field, rhsClass) then erasedBottomTree(rhsClass) else transformFollowingDeep(ref(field))(using ctx.withOwner(sym)) val getterDef = cpy.DefDef(tree)(rhs = getterRhs) - addAnnotations(fieldDef.denot) - removeUnwantedAnnotations(sym, defn.GetterMetaAnnot) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot)) Thicket(fieldDef, getterDef) else if sym.isSetter then if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs: @unchecked } // This is intended as an assertion @@ -210,7 +193,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => then Literal(Constant(())) else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol))) val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym))) - removeUnwantedAnnotations(sym, defn.SetterMetaAnnot) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) setterDef else // Curiously, some accessors from Scala2 have ' ' suffixes. diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index b862ca7fe01e..52cd59c9483a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package transform import core._ @@ -147,6 +148,7 @@ class Pickler extends Phase { if unequal then output("before-pickling.txt", previous) output("after-pickling.txt", unpickled) + //sys.process.Process("diff -u before-pickling.txt after-pickling.txt").! report.error(em"""pickling difference for $cls in ${cls.source}, for details: | | diff before-pickling.txt after-pickling.txt""") diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index a669be3d1e3c..eec7a1c85b3b 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package transform import dotty.tools.dotc.ast.{Trees, tpd, untpd, desugar} @@ -156,12 +157,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase checkInferredWellFormed(tree.tpt) if sym.is(Method) then if sym.isSetter then - removeUnwantedAnnotations(sym, defn.SetterMetaAnnot, NoSymbol, keepIfNoRelevantAnnot = false) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) else if sym.is(Param) then - removeUnwantedAnnotations(sym, defn.ParamMetaAnnot, NoSymbol, keepIfNoRelevantAnnot = true) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) + else if sym.is(ParamAccessor) then + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot)) else - removeUnwantedAnnotations(sym, defn.GetterMetaAnnot, defn.FieldMetaAnnot, keepIfNoRelevantAnnot = !sym.is(ParamAccessor)) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then if !sym.owner.unforcedDecls.exists(p => !p.isScala2Macro && p.name == sym.name && p.signature == sym.signature) // Allow scala.reflect.materializeClassTag to be able to compile scala/reflect/package.scala @@ -183,17 +186,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase => Checking.checkAppliedTypesIn(tree) case _ => - private def removeUnwantedAnnotations(sym: Symbol, metaAnnotSym: Symbol, - metaAnnotSymBackup: Symbol, keepIfNoRelevantAnnot: Boolean)(using Context): Unit = - def shouldKeep(annot: Annotation): Boolean = - val annotSym = annot.symbol - annotSym.hasAnnotation(metaAnnotSym) - || annotSym.hasAnnotation(metaAnnotSymBackup) - || (keepIfNoRelevantAnnot && { - !annotSym.annotations.exists(metaAnnot => defn.FieldAccessorMetaAnnots.contains(metaAnnot.symbol)) - }) - if sym.annotations.nonEmpty then - sym.filterAnnotations(shouldKeep(_)) private def transformSelect(tree: Select, targs: List[Tree])(using Context): Tree = { val qual = tree.qualifier diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 6010fe2a2a44..766f2ab23873 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -270,11 +270,8 @@ object SymUtils: def isEnumCase(using Context): Boolean = self.isAllOf(EnumCase, butNot = JavaDefined) - def annotationsCarrying(meta: ClassSymbol)(using Context): List[Annotation] = - self.annotations.filter(_.symbol.hasAnnotation(meta)) - - def withAnnotationsCarrying(from: Symbol, meta: ClassSymbol)(using Context): self.type = { - self.addAnnotations(from.annotationsCarrying(meta)) + def withAnnotationsCarrying(from: Symbol, meta: Symbol, orNoneOf: Set[Symbol] = Set.empty)(using Context): self.type = { + self.addAnnotations(from.annotationsCarrying(Set(meta), orNoneOf)) self } diff --git a/compiler/src/dotty/tools/dotc/util/Spans.scala b/compiler/src/dotty/tools/dotc/util/Spans.scala index baf2cfa121b0..ba537e9aec01 100644 --- a/compiler/src/dotty/tools/dotc/util/Spans.scala +++ b/compiler/src/dotty/tools/dotc/util/Spans.scala @@ -182,6 +182,7 @@ object Spans { assert(isSpan) if (this == NoCoord) NoSpan else Span(-1 - encoding) } + override def toString = if isSpan then s"$toSpan" else s"Coord(idx=$toIndex)" } /** An index coordinate */ diff --git a/tests/run/beans.check b/tests/run/beans.check index eb70eefb720c..3a6d7b04e6a8 100644 --- a/tests/run/beans.check +++ b/tests/run/beans.check @@ -2,5 +2,6 @@ true 10 [@beans.LibraryAnnotation_1()] +[] some text other text diff --git a/tests/run/beans/Test_3.java b/tests/run/beans/Test_3.java index 4ac967419e50..ad515d787a0e 100644 --- a/tests/run/beans/Test_3.java +++ b/tests/run/beans/Test_3.java @@ -7,6 +7,7 @@ public A run() throws ReflectiveOperationException{ System.out.println(a.isY()); System.out.println(new T2().getX()); + System.out.println(Arrays.asList(a.getClass().getDeclaredField("retainingAnnotation").getAnnotations())); System.out.println(Arrays.asList(a.getClass().getMethod("getRetainingAnnotation").getAnnotations())); System.out.println(a.getMutableOneWithLongName()); diff --git a/tests/run/i12492.check b/tests/run/i12492.check new file mode 100644 index 000000000000..1c38f98a3c09 --- /dev/null +++ b/tests/run/i12492.check @@ -0,0 +1,37 @@ +inspecting constructor MyTable +inspecting param aaaParam1 @MyColumnBase +inspecting param fldParam1 +inspecting param getParam1 +inspecting param parParam1 @MyColumnBase +inspecting field aaaField1 @MyColumnBase +inspecting field aaaParam1 +inspecting field fldField1 @MyColumnBase +inspecting field fldParam1 @MyColumnBase +inspecting field getField1 +inspecting field getParam1 +inspecting field parField1 +inspecting field parParam1 +inspecting method aaaField1 +inspecting method aaaParam1 +inspecting method fldField1 +inspecting method fldParam1 +inspecting method getField1 @MyColumnBase +inspecting method getParam1 @MyColumnBase +inspecting method parField1 +inspecting method parParam1 +inspecting constructor MyTable2 +inspecting param fldParam2 +inspecting param getParam2 +inspecting param parParam2 @MyColumnBase +inspecting field fldField2 @MyColumnBase +inspecting field fldParam2 @MyColumnBase +inspecting field getField2 +inspecting field getParam2 +inspecting field parField2 +inspecting field parParam2 +inspecting method fldField2 +inspecting method fldParam2 +inspecting method getField2 @MyColumnBase +inspecting method getParam2 @MyColumnBase +inspecting method parField2 +inspecting method parParam2 diff --git a/tests/run/i12492/MyColumnBase.java b/tests/run/i12492/MyColumnBase.java new file mode 100644 index 000000000000..f24b50b8d80c --- /dev/null +++ b/tests/run/i12492/MyColumnBase.java @@ -0,0 +1,5 @@ +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface MyColumnBase {} diff --git a/tests/run/i12492/MyTable.scala b/tests/run/i12492/MyTable.scala new file mode 100644 index 000000000000..b22f76c5ec91 --- /dev/null +++ b/tests/run/i12492/MyTable.scala @@ -0,0 +1,27 @@ +import scala.annotation.meta.{ field as fld, getter as get, param as par } + +type FldColumn = MyColumnBase @fld +type GetColumn = MyColumnBase @get +type ParColumn = MyColumnBase @par + +class MyTable( + @(MyColumnBase ) val aaaParam1: String, + @(MyColumnBase @fld) val fldParam1: String, + @(MyColumnBase @get) val getParam1: String, + @(MyColumnBase @par) val parParam1: String, +) { + @(MyColumnBase ) val aaaField1: String = "" + @(MyColumnBase @fld) val fldField1: String = "" + @(MyColumnBase @get) val getField1: String = "" + @(MyColumnBase @par) val parField1: String = "" +} + +class MyTable2( + @FldColumn val fldParam2: String, + @GetColumn val getParam2: String, + @ParColumn val parParam2: String, +) { + @FldColumn val fldField2: String = "" + @GetColumn val getField2: String = "" + @ParColumn val parField2: String = "" +} diff --git a/tests/run/i12492/Test.scala b/tests/run/i12492/Test.scala new file mode 100644 index 000000000000..190b84fd66f5 --- /dev/null +++ b/tests/run/i12492/Test.scala @@ -0,0 +1,31 @@ +// scalajs: --skip +object Test: + def main(args: Array[String]): Unit = + go(classOf[MyTable]) + go(classOf[MyTable2]) + + def go(cls: Class[?]): Unit = + for c <- cls.getDeclaredConstructors.sortBy(_.getName) do + c.setAccessible(true) + println(s"inspecting constructor ${c.getName}") + for p <- c.getParameters.sortBy(_.getName) do + print(s"inspecting param ${p.getName}") + for a <- p.getAnnotations.sortBy(_.annotationType.toString) do + print(s" @${a.annotationType.getName}") + println() + + for (m <- cls.getDeclaredFields.sortBy(_.getName)) { + m.setAccessible(true) + print(s"inspecting field ${m.getName}") + for a <- m.getAnnotations().sortBy(_.annotationType.toString) do + print(s" @${a.annotationType.getName}") + println() + } + + for (m <- cls.getDeclaredMethods.sortBy(_.getName)) { + m.setAccessible(true) + print(s"inspecting method ${m.getName}") + for a <- m.getAnnotations().sortBy(_.annotationType.toString) do + print(s" @${a.annotationType.getName}") + println() + } diff --git a/tests/run/i15318.check b/tests/run/i15318.check new file mode 100644 index 000000000000..d1fb39e729e8 --- /dev/null +++ b/tests/run/i15318.check @@ -0,0 +1,30 @@ +inspecting constructor Bean +inspecting field beanAaa @JsonProperty +inspecting field beanGet +inspecting field beanSet +inspecting field normAaa @JsonProperty +inspecting field normFld @JsonProperty +inspecting field normGet +inspecting field normSet +inspecting method beanAaa +inspecting method beanGet +inspecting method beanSet +inspecting method beanSet_$eq +inspecting method getBeanAaa +inspecting method getBeanGet @JsonProperty +inspecting method getBeanSet +inspecting method normAaa +inspecting method normFld +inspecting method normGet @JsonProperty +inspecting method normSet +inspecting method normSet_$eq @JsonProperty +inspecting method setBeanSet @JsonProperty +inspecting constructor Bean2 +inspecting field beanGet +inspecting field beanSet +inspecting method beanGet +inspecting method beanSet +inspecting method beanSet_$eq +inspecting method getBeanGet @JsonProperty +inspecting method getBeanSet +inspecting method setBeanSet @JsonProperty diff --git a/tests/run/i15318/Bean.scala b/tests/run/i15318/Bean.scala new file mode 100644 index 000000000000..0833a264806f --- /dev/null +++ b/tests/run/i15318/Bean.scala @@ -0,0 +1,21 @@ +import scala.annotation.meta.{ field as fld, getter as get, setter as set, * } +import scala.beans.BeanProperty + +type BeanGetJsonProperty = JsonProperty @beanGetter +type BeanSetJsonProperty = JsonProperty @beanSetter + +class Bean { + @(JsonProperty ) val normAaa: String = "" + @(JsonProperty @fld) val normFld: String = "" + @(JsonProperty @get) val normGet: String = "" + @(JsonProperty @set) var normSet: String = "" + + @(JsonProperty ) @BeanProperty val beanAaa: String = "" + @(JsonProperty @beanGetter) @BeanProperty val beanGet: String = "" + @(JsonProperty @beanSetter) @BeanProperty var beanSet: String = "" +} + +class Bean2 { + @BeanGetJsonProperty @BeanProperty val beanGet: String = "" + @BeanSetJsonProperty @BeanProperty var beanSet: String = "" +} diff --git a/tests/run/i15318/JsonProperty.java b/tests/run/i15318/JsonProperty.java new file mode 100644 index 000000000000..0f588b63dc7d --- /dev/null +++ b/tests/run/i15318/JsonProperty.java @@ -0,0 +1,5 @@ +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface JsonProperty {} diff --git a/tests/run/i15318/Test.scala b/tests/run/i15318/Test.scala new file mode 100644 index 000000000000..1669a3f25e1d --- /dev/null +++ b/tests/run/i15318/Test.scala @@ -0,0 +1,31 @@ +// scalajs: --skip +object Test: + def main(args: Array[String]): Unit = + go(classOf[Bean]) + go(classOf[Bean2]) + + def go(cls: Class[?]): Unit = + for c <- cls.getDeclaredConstructors.sortBy(_.getName) do + c.setAccessible(true) + println(s"inspecting constructor ${c.getName}") + for p <- c.getParameters.sortBy(_.getName) do + print(s"inspecting param ${p.getName}") + for a <- p.getAnnotations.sortBy(_.annotationType.toString) do + print(s" @${a.annotationType.getName}") + println() + + for (m <- cls.getDeclaredFields.sortBy(_.getName)) { + m.setAccessible(true) + print(s"inspecting field ${m.getName}") + for a <- m.getAnnotations().sortBy(_.annotationType.toString) do + print(s" @${a.annotationType.getName}") + println() + } + + for (m <- cls.getDeclaredMethods.sortBy(_.getName)) { + m.setAccessible(true) + print(s"inspecting method ${m.getName}") + for a <- m.getAnnotations().sortBy(_.annotationType.toString) do + print(s" @${a.annotationType.getName}") + println() + }