diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index b6d898b3b221..c49b7d9556c9 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -61,7 +61,6 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { @threadUnsafe lazy val AnnotationRetentionSourceAttr: TermSymbol = requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("SOURCE") @threadUnsafe lazy val AnnotationRetentionClassAttr: TermSymbol = requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("CLASS") @threadUnsafe lazy val AnnotationRetentionRuntimeAttr: TermSymbol = requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("RUNTIME") - @threadUnsafe lazy val JavaAnnotationClass: ClassSymbol = requiredClass("java.lang.annotation.Annotation") val bCodeAsmCommon: BCodeAsmCommon[int.type] = new BCodeAsmCommon(int) @@ -415,7 +414,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { arrAnnotV.visitEnd() } // for the lazy val in ScalaSigBytes to be GC'ed, the invoker of emitAnnotations() should hold the ScalaSigBytes in a method-local var that doesn't escape. */ - case t @ Apply(constr, args) if t.tpe.derivesFrom(JavaAnnotationClass) => + case t @ Apply(constr, args) if t.tpe.classSymbol.is(JavaAnnotation) => val typ = t.tpe.classSymbol.denot.info val assocs = assocsFromApply(t) val desc = innerClasesStore.typeDescriptor(typ) // the class descriptor of the nested annotation class diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 104a81ec9977..b43857b7d28c 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -969,7 +969,6 @@ class Definitions { // Annotation base classes @tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation") - @tu lazy val ClassfileAnnotationClass: ClassSymbol = requiredClass("scala.annotation.ClassfileAnnotation") @tu lazy val StaticAnnotationClass: ClassSymbol = requiredClass("scala.annotation.StaticAnnotation") @tu lazy val RefiningAnnotationClass: ClassSymbol = requiredClass("scala.annotation.RefiningAnnotation") diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index a91da1cf5f96..f23dce020f10 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -350,8 +350,8 @@ object Flags { /** Symbol is a method which should be marked ACC_SYNCHRONIZED */ val (_, Synchronized @ _, _) = newFlags(36, "") - /** Symbol is a Java-style varargs method */ - val (_, JavaVarargs @ _, _) = newFlags(37, "") + /** Symbol is a Java-style varargs method / a Java annotation */ + val (_, JavaVarargs @ _, JavaAnnotation @ _) = newFlags(37, "", "") /** Symbol is a Java default method */ val (_, DefaultMethod @ _, _) = newFlags(38, "") @@ -477,7 +477,7 @@ object Flags { */ val AfterLoadFlags: FlagSet = commonFlags( FromStartFlags, AccessFlags, Final, AccessorOrSealed, - Abstract, LazyOrTrait, SelfName, JavaDefined, Transparent) + Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent) /** A value that's unstable unless complemented with a Stable flag */ val UnstableValueFlags: FlagSet = Mutable | Method diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index c0aca9d8abf4..50c96191143c 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -243,7 +243,6 @@ object StdNames { final val ToString: N = "ToString" final val Xor: N = "^" - final val ClassfileAnnotation: N = "ClassfileAnnotation" final val ClassManifest: N = "ClassManifest" final val Enum: N = "Enum" final val Group: N = "Group" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index f0ab8253da72..a4f1bf3c5e80 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -808,7 +808,7 @@ object SymDenotations { /** Is this a Scala or Java annotation ? */ def isAnnotation(using Context): Boolean = - isClass && derivesFrom(defn.AnnotationClass) + isClass && (derivesFrom(defn.AnnotationClass) || is(JavaAnnotation)) /** Is this symbol a class that extends `java.io.Serializable` ? */ def isSerializable(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala index 3b05ee351b86..4aa60d973264 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala @@ -346,6 +346,7 @@ object ClassfileConstants { case JAVA_ACC_ENUM => Enum case JAVA_ACC_ABSTRACT => if (isClass) Abstract else Deferred case JAVA_ACC_INTERFACE => PureInterfaceCreationFlags | JavaDefined + case JAVA_ACC_ANNOTATION => JavaAnnotation case _ => EmptyFlags } @@ -353,18 +354,16 @@ object ClassfileConstants { if (jflag == 0) base else base | translateFlag(jflag) private def translateFlags(jflags: Int, baseFlags: FlagSet): FlagSet = { - val nflags = - if ((jflags & JAVA_ACC_ANNOTATION) == 0) jflags - else jflags & ~(JAVA_ACC_ABSTRACT | JAVA_ACC_INTERFACE) // annotations are neither abstract nor interfaces var res: FlagSet = baseFlags | JavaDefined - res = addFlag(res, nflags & JAVA_ACC_PRIVATE) - res = addFlag(res, nflags & JAVA_ACC_PROTECTED) - res = addFlag(res, nflags & JAVA_ACC_FINAL) - res = addFlag(res, nflags & JAVA_ACC_SYNTHETIC) - res = addFlag(res, nflags & JAVA_ACC_STATIC) - res = addFlag(res, nflags & JAVA_ACC_ENUM) - res = addFlag(res, nflags & JAVA_ACC_ABSTRACT) - res = addFlag(res, nflags & JAVA_ACC_INTERFACE) + res = addFlag(res, jflags & JAVA_ACC_PRIVATE) + res = addFlag(res, jflags & JAVA_ACC_PROTECTED) + res = addFlag(res, jflags & JAVA_ACC_FINAL) + res = addFlag(res, jflags & JAVA_ACC_SYNTHETIC) + res = addFlag(res, jflags & JAVA_ACC_STATIC) + res = addFlag(res, jflags & JAVA_ACC_ENUM) + res = addFlag(res, jflags & JAVA_ACC_ABSTRACT) + res = addFlag(res, jflags & JAVA_ACC_INTERFACE) + res = addFlag(res, jflags & JAVA_ACC_ANNOTATION) res } diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 4763cd25ff41..33a1e1dd6e73 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -165,11 +165,7 @@ class ClassfileParser( * Updates the read pointer of 'in'. */ def parseParents: List[Type] = { val superType = - if (isAnnotation) { - in.nextChar - defn.AnnotationClass.typeRef - } - else if (classRoot.symbol == defn.ComparableClass || + if (classRoot.symbol == defn.ComparableClass || classRoot.symbol == defn.JavaCloneableClass || classRoot.symbol == defn.JavaSerializableClass) { // Treat these interfaces as universal traits @@ -186,7 +182,6 @@ class ClassfileParser( // Consequently, no best implicit for the "Integral" evidence parameter of "range" // is found. Previously, this worked because of weak conformance, which has been dropped. - if (isAnnotation) ifaces = defn.ClassfileAnnotationClass.typeRef :: ifaces superType :: ifaces } @@ -845,7 +840,7 @@ class ClassfileParser( class AnnotConstructorCompleter(classInfo: TempClassInfoType) extends LazyType { def complete(denot: SymDenotation)(using Context): Unit = { - val attrs = classInfo.decls.toList.filter(sym => sym.isTerm && sym != denot.symbol) + val attrs = classInfo.decls.toList.filter(sym => sym.isTerm && sym != denot.symbol && sym.name != nme.CONSTRUCTOR) val paramNames = attrs.map(_.name.asTermName) val paramTypes = attrs.map(_.info.resultType) denot.info = MethodType(paramNames, paramTypes, classRoot.typeRef) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 4611554a01a3..183845fcf3ec 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -822,7 +822,7 @@ object JavaParsers { val iface = atSpan(start, nameOffset) { TypeDef( name, - makeTemplate(parents, body, tparams, false)).withMods(mods | Flags.Trait | Flags.JavaInterface | Flags.Abstract) + makeTemplate(parents, body, tparams, false)).withMods(mods | Flags.JavaInterface) } addCompanionObject(statics, iface) } @@ -858,10 +858,9 @@ object JavaParsers { } (statics.toList, members.toList) } - def annotationParents: List[Select] = List( - scalaAnnotationDot(tpnme.Annotation), - Select(javaLangDot(nme.annotation), tpnme.Annotation), - scalaAnnotationDot(tpnme.ClassfileAnnotation) + def annotationParents: List[Tree] = List( + javaLangObject(), + Select(javaLangDot(nme.annotation), tpnme.Annotation) ) def annotationDecl(start: Offset, mods: Modifiers): List[Tree] = { accept(AT) @@ -877,7 +876,7 @@ object JavaParsers { List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined)) val templ = makeTemplate(annotationParents, constr :: body, List(), true) val annot = atSpan(start, nameOffset) { - TypeDef(name, templ).withMods(mods | Flags.Abstract) + TypeDef(name, templ).withMods(mods | Flags.JavaInterface | Flags.JavaAnnotation) } addCompanionObject(statics, annot) } diff --git a/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala b/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala index e8f8a80e1a0d..1cf687187eeb 100644 --- a/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala +++ b/compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala @@ -10,6 +10,7 @@ import Symbols.defn import Constants._ import Types._ import Decorators._ +import Flags._ import scala.collection.mutable @@ -33,7 +34,7 @@ class RepeatableAnnotations extends MiniPhase: val annsByType = stableGroupBy(annotations, _.symbol) annsByType.flatMap { case (_, a :: Nil) => a :: Nil - case (sym, anns) if sym.derivesFrom(defn.ClassfileAnnotationClass) => + case (sym, anns) if sym.is(JavaDefined) => sym.getAnnotation(defn.JavaRepeatableAnnot).flatMap(_.argumentConstant(0)) match case Some(Constant(containerTpe: Type)) => val clashingAnns = annsByType.getOrElse(containerTpe.classSymbol, Nil) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index bfebb7dbdabb..448c66838164 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -547,7 +547,7 @@ trait Applications extends Compatibility { /** Is `sym` a constructor of a Java-defined annotation? */ def isJavaAnnotConstr(sym: Symbol): Boolean = - sym.is(JavaDefined) && sym.isConstructor && sym.owner.derivesFrom(defn.AnnotationClass) + sym.is(JavaDefined) && sym.isConstructor && sym.owner.is(JavaAnnotation) /** Match re-ordered arguments against formal parameters * @param n The position of the first parameter in formals in `methType`. diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ed9cce9ca0eb..99399832085f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1110,6 +1110,8 @@ trait Checking { def checkParentCall(call: Tree, caller: ClassSymbol)(using Context): Unit = if (!ctx.isAfterTyper) { val called = call.tpe.classSymbol + if (called.is(JavaAnnotation)) + report.error(i"${called.name} must appear without any argument to be a valid class parent because it is a Java annotation", call.srcPos) if (caller.is(Trait)) report.error(i"$caller may not call constructor of $called", call.srcPos) else if (called.is(Trait) && !caller.mixins.contains(called)) @@ -1263,6 +1265,23 @@ trait Checking { if !Inlines.inInlineMethod && !ctx.isInlineContext then report.error(em"$what can only be used in an inline method", pos) + /** Check that the class corresponding to this tree is either a Scala or Java annotation. + * + * @return The original tree or an error tree in case `tree` isn't a valid + * annotation or already an error tree. + */ + def checkAnnotClass(tree: Tree)(using Context): Tree = + if tree.tpe.isError then + return tree + val cls = Annotations.annotClass(tree) + if cls.is(JavaDefined) then + if !cls.is(JavaAnnotation) then + errorTree(tree, em"$cls is not a valid Java annotation: it was not declared with `@interface`") + else tree + else if !cls.derivesFrom(defn.AnnotationClass) then + errorTree(tree, em"$cls is not a valid Scala annotation: it does not extend `scala.annotation.Annotation`") + else tree + /** Check arguments of compiler-defined annotations */ def checkAnnotArgs(tree: Tree)(using Context): tree.type = val cls = Annotations.annotClass(tree) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index ad8d0e50d348..6aab561c44b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -833,7 +833,7 @@ class Namer { typer: Typer => if (cls eq sym) report.error("An annotation class cannot be annotated with iself", annotTree.srcPos) else { - val ann = Annotation.deferred(cls)(typedAheadAnnotation(annotTree)(using annotCtx)) + val ann = Annotation.deferred(cls)(typedAheadExpr(annotTree)(using annotCtx)) sym.addAnnotation(ann) } } @@ -1618,9 +1618,6 @@ class Namer { typer: Typer => def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(using Context): tpd.Tree = typedAhead(tree, typer.typedExpr(_, pt)) - def typedAheadAnnotation(tree: Tree)(using Context): tpd.Tree = - typedAheadExpr(tree, defn.AnnotationClass.typeRef) - def typedAheadAnnotationClass(tree: Tree)(using Context): Symbol = tree match { case Apply(fn, _) => typedAheadAnnotationClass(fn) case TypeApply(fn, _) => typedAheadAnnotationClass(fn) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 33638df54fb1..182351bfe7c4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2259,7 +2259,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } def typedAnnotation(annot: untpd.Tree)(using Context): Tree = - checkAnnotArgs(typed(annot, defn.AnnotationClass.typeRef)) + checkAnnotClass(checkAnnotArgs(typed(annot))) def registerNowarn(tree: Tree, mdef: untpd.Tree)(using Context): Unit = val annot = Annotations.Annotation(tree) @@ -2595,6 +2595,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer */ def ensureConstrCall(cls: ClassSymbol, parent: Tree, psym: Symbol)(using Context): Tree = if parent.isType && !cls.is(Trait) && !cls.is(JavaDefined) && psym.isClass + // Annotations are represented as traits with constructors, but should + // never be called as such outside of annotation trees. + && !psym.is(JavaAnnotation) && (!psym.is(Trait) || psym.primaryConstructor.info.takesParams && !cls.superClass.isSubClass(psym)) then typed(untpd.New(untpd.TypedSplice(parent), Nil)) @@ -2669,7 +2672,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer end typedPackageDef def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = { - val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef) + val annot1 = checkAnnotClass(typedExpr(tree.annot)) val annotCls = Annotations.annotClass(annot1) if annotCls == defn.NowarnAnnot then registerNowarn(annot1, tree) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index f8e439baeb0e..3cab93f247ae 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2765,6 +2765,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def Invisible: Flags = dotc.core.Flags.Invisible def JavaDefined: Flags = dotc.core.Flags.JavaDefined def JavaStatic: Flags = dotc.core.Flags.JavaStatic + def JavaAnnotation: Flags = dotc.core.Flags.JavaAnnotation def Lazy: Flags = dotc.core.Flags.Lazy def Local: Flags = dotc.core.Flags.Local def Macro: Flags = dotc.core.Flags.Macro diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 3e2863f2260b..309e49be1370 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4278,6 +4278,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Is implemented as a Java static */ def JavaStatic: Flags + /** Is this an annotation defined in Java */ + @experimental def JavaAnnotation: Flags + /** Is this symbol `lazy` */ def Lazy: Flags diff --git a/tests/pending/neg/i5690.scala b/tests/neg/i5690.scala similarity index 100% rename from tests/pending/neg/i5690.scala rename to tests/neg/i5690.scala diff --git a/tests/neg/java-ann-extends-separate/Ann_1.java b/tests/neg/java-ann-extends-separate/Ann_1.java new file mode 100644 index 000000000000..97184df24c83 --- /dev/null +++ b/tests/neg/java-ann-extends-separate/Ann_1.java @@ -0,0 +1,3 @@ +public @interface Ann_1 { + int value(); +} diff --git a/tests/neg/java-ann-extends-separate/Test_2.scala b/tests/neg/java-ann-extends-separate/Test_2.scala new file mode 100644 index 000000000000..4e73b71679f6 --- /dev/null +++ b/tests/neg/java-ann-extends-separate/Test_2.scala @@ -0,0 +1,2 @@ +def test(x: Ann_1) = + val y: scala.annotation.Annotation = x // error diff --git a/tests/neg/java-ann-extends/Ann.java b/tests/neg/java-ann-extends/Ann.java new file mode 100644 index 000000000000..9ae845a8af63 --- /dev/null +++ b/tests/neg/java-ann-extends/Ann.java @@ -0,0 +1,3 @@ +public @interface Ann { + int value(); +} diff --git a/tests/neg/java-ann-extends/Test.scala b/tests/neg/java-ann-extends/Test.scala new file mode 100644 index 000000000000..629f1daa9acc --- /dev/null +++ b/tests/neg/java-ann-extends/Test.scala @@ -0,0 +1,2 @@ +def test(x: Ann) = + val y: scala.annotation.Annotation = x // error diff --git a/tests/neg/java-ann-super-class/Ann.java b/tests/neg/java-ann-super-class/Ann.java new file mode 100644 index 000000000000..9ae845a8af63 --- /dev/null +++ b/tests/neg/java-ann-super-class/Ann.java @@ -0,0 +1,3 @@ +public @interface Ann { + int value(); +} diff --git a/tests/neg/java-ann-super-class/Test.scala b/tests/neg/java-ann-super-class/Test.scala new file mode 100644 index 000000000000..cf2f72d2f633 --- /dev/null +++ b/tests/neg/java-ann-super-class/Test.scala @@ -0,0 +1,9 @@ +class Bar extends Ann(1) { // error + def value = 1 + def annotationType = classOf[Ann] +} + +def test = + // Typer errors + new Ann // error + new Ann(1) {} // error diff --git a/tests/neg/java-ann-super-class2/Ann.java b/tests/neg/java-ann-super-class2/Ann.java new file mode 100644 index 000000000000..9ae845a8af63 --- /dev/null +++ b/tests/neg/java-ann-super-class2/Ann.java @@ -0,0 +1,3 @@ +public @interface Ann { + int value(); +} diff --git a/tests/neg/java-ann-super-class2/Test.scala b/tests/neg/java-ann-super-class2/Test.scala new file mode 100644 index 000000000000..d5c22860899c --- /dev/null +++ b/tests/neg/java-ann-super-class2/Test.scala @@ -0,0 +1,3 @@ +def test = + // Posttyper errors + new Ann(1) // error diff --git a/tests/neg/java-ann-super-class3/Ann.java b/tests/neg/java-ann-super-class3/Ann.java new file mode 100644 index 000000000000..9ae845a8af63 --- /dev/null +++ b/tests/neg/java-ann-super-class3/Ann.java @@ -0,0 +1,3 @@ +public @interface Ann { + int value(); +} diff --git a/tests/neg/java-ann-super-class3/Test.scala b/tests/neg/java-ann-super-class3/Test.scala new file mode 100644 index 000000000000..8fd9791e6fe3 --- /dev/null +++ b/tests/neg/java-ann-super-class3/Test.scala @@ -0,0 +1,3 @@ +def test = + // Refchecks error + new Ann {} // error diff --git a/tests/neg/java-fake-ann-separate/FakeAnn_1.java b/tests/neg/java-fake-ann-separate/FakeAnn_1.java new file mode 100644 index 000000000000..597ea980585d --- /dev/null +++ b/tests/neg/java-fake-ann-separate/FakeAnn_1.java @@ -0,0 +1 @@ +interface FakeAnn_1 extends java.lang.annotation.Annotation { } diff --git a/tests/neg/java-fake-ann-separate/Test_2.scala b/tests/neg/java-fake-ann-separate/Test_2.scala new file mode 100644 index 000000000000..becc8babdaa0 --- /dev/null +++ b/tests/neg/java-fake-ann-separate/Test_2.scala @@ -0,0 +1,3 @@ +@FakeAnn_1 def test = // error + (1: @FakeAnn_1) // error + diff --git a/tests/neg/java-fake-ann/FakeAnn.java b/tests/neg/java-fake-ann/FakeAnn.java new file mode 100644 index 000000000000..2b055f782d42 --- /dev/null +++ b/tests/neg/java-fake-ann/FakeAnn.java @@ -0,0 +1 @@ +interface FakeAnn extends java.lang.annotation.Annotation { } diff --git a/tests/neg/java-fake-ann/Test.scala b/tests/neg/java-fake-ann/Test.scala new file mode 100644 index 000000000000..827527cb80bf --- /dev/null +++ b/tests/neg/java-fake-ann/Test.scala @@ -0,0 +1,2 @@ +@FakeAnn def test = // error + (1: @FakeAnn) // error diff --git a/tests/neg/repeatable/Test_1.scala b/tests/neg/repeatable/Test_1.scala index 3779b6ffa4a8..6466da95dfa8 100644 --- a/tests/neg/repeatable/Test_1.scala +++ b/tests/neg/repeatable/Test_1.scala @@ -6,11 +6,11 @@ import repeatable._ @FirstLevel_0(Array()) // error trait U -@FirstLevel_0(Array(Plain_0(4), Plain_0(5))) -@FirstLevel_0(Array(Plain_0(6), Plain_0(7))) +@FirstLevel_0(Array(new Plain_0(4), new Plain_0(5))) +@FirstLevel_0(Array(new Plain_0(6), new Plain_0(7))) @SecondLevel_0(Array()) // error trait T @SecondLevel_0(Array()) @SecondLevel_0(Array()) // error -trait S \ No newline at end of file +trait S diff --git a/tests/pos/i5690.scala b/tests/pos/i5690.scala deleted file mode 100644 index 25c81b72d0b6..000000000000 --- a/tests/pos/i5690.scala +++ /dev/null @@ -1,8 +0,0 @@ -// TODO: this should be a compilation error -// program fails at runtime with java.lang.InstantiationError -// see tests/pending/neg/i5690.scala -object AnnotInst{ - def main(a: Array[String]) = { - new java.lang.annotation.Inherited - } -} diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index 8901ba53b605..a7c437242ebe 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -62,6 +62,7 @@ val experimentalDefinitionInLibrary = Set( //// New APIs: Quotes // Can be stabilized in 3.3.0 (unsure) or later "scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings", + "scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation", // Cant be stabilized yet. // Need newClass variant that can add constructor parameters. // Need experimental annotation macros to check that design works. diff --git a/tests/run-macros/annot-java-tree/AnnoMacro.scala b/tests/run-macros/annot-java-tree/AnnoMacro.scala new file mode 100644 index 000000000000..3dae57868eab --- /dev/null +++ b/tests/run-macros/annot-java-tree/AnnoMacro.scala @@ -0,0 +1,29 @@ +import scala.quoted.* + +inline def checkSuppressWarnings[T]: Unit = ${ checkSuppressWarningsImpl[T] } + +def checkSuppressWarningsImpl[T: Type](using Quotes): Expr[Unit] = + import quotes.reflect.* + val SuppressWarningsSymbol = TypeTree.of[SuppressWarnings].symbol + val sym = TypeRepr.of[T].typeSymbol + // Imitate what wartremover does, so we can avoid unintentionally breaking it: + // https://github.com/wartremover/wartremover/blob/fb18e6eafe9a47823e04960aaf4ec7a9293719ef/core/src/main/scala-3/org/wartremover/WartUniverse.scala#L63-L77 + val actualArgs = sym + .getAnnotation(SuppressWarningsSymbol) + .collect { + case Apply( + Select(_, ""), + Apply(Apply(_, Typed(Repeated(values, _), _) :: Nil), Apply(_, _ :: Nil) :: Nil) :: Nil + ) => + // "-Yexplicit-nulls" + // https://github.com/wartremover/wartremover/issues/660 + values.collect { case Literal(StringConstant(str)) => + str + } + } + .toList + .flatten + val expectedArgs = List("a", "b") + assert(actualArgs == expectedArgs, + s"Expected $expectedArgs arguments for SuppressWarnings annotation of $sym but got $actualArgs") + '{} diff --git a/tests/run-macros/annot-java-tree/S.scala b/tests/run-macros/annot-java-tree/S.scala new file mode 100644 index 000000000000..07065cde46d1 --- /dev/null +++ b/tests/run-macros/annot-java-tree/S.scala @@ -0,0 +1,4 @@ +@SuppressWarnings(Array("a", "b")) class Foo + +@main def Test = + checkSuppressWarnings[Foo] diff --git a/tests/run/java-ann-super-class-separate/Ann_1.java b/tests/run/java-ann-super-class-separate/Ann_1.java new file mode 100644 index 000000000000..b7fcb6952273 --- /dev/null +++ b/tests/run/java-ann-super-class-separate/Ann_1.java @@ -0,0 +1,4 @@ +public @interface Ann_1 { + int bar() default 1; + int baz() default 2; +} diff --git a/tests/run/java-ann-super-class-separate/Test_2.scala b/tests/run/java-ann-super-class-separate/Test_2.scala new file mode 100644 index 000000000000..77da86b4f057 --- /dev/null +++ b/tests/run/java-ann-super-class-separate/Test_2.scala @@ -0,0 +1,21 @@ +// scalajs: --skip + +class Foo extends Ann_1 { + override def bar = 3 + override def baz = 4 + def annotationType = classOf[Ann_1] +} + +object Test { + def main(args: Array[String]): Unit = { + val x = new Foo + val y: Ann_1 = x + val z: Int @Ann_1(1) = 1 + val zz: Int @Ann_1() = 1 + val ann: java.lang.annotation.Annotation = new Ann_1 { + def bar = 3 + def baz = 4 + def annotationType = classOf[Ann_1] + } + } +} diff --git a/tests/run/java-ann-super-class/Ann.java b/tests/run/java-ann-super-class/Ann.java new file mode 100644 index 000000000000..8c40c14fcb59 --- /dev/null +++ b/tests/run/java-ann-super-class/Ann.java @@ -0,0 +1,4 @@ +public @interface Ann { + int bar() default 1; + int baz() default 2; +} diff --git a/tests/run/java-ann-super-class/Test.scala b/tests/run/java-ann-super-class/Test.scala new file mode 100644 index 000000000000..7c5040552cc4 --- /dev/null +++ b/tests/run/java-ann-super-class/Test.scala @@ -0,0 +1,21 @@ +// scalajs: --skip + +class Foo extends Ann { + override def bar = 3 + override def baz = 4 + def annotationType = classOf[Ann] +} + +object Test { + def main(args: Array[String]): Unit = { + val x = new Foo + val y: Ann = x + val z: Int @Ann(1) = 1 + val zz: Int @Ann() = 1 + val ann: java.lang.annotation.Annotation = new Ann { + override def bar = 3 + override def baz = 4 + def annotationType = classOf[Ann] + } + } +} diff --git a/tests/run/repeatable/Test_1.scala b/tests/run/repeatable/Test_1.scala index 2a8654dea6ba..281f91262cce 100644 --- a/tests/run/repeatable/Test_1.scala +++ b/tests/run/repeatable/Test_1.scala @@ -7,8 +7,8 @@ import repeatable._ @Plain_0(3) trait U -@FirstLevel_0(Array(Plain_0(4), Plain_0(5))) -@FirstLevel_0(Array(Plain_0(6), Plain_0(7))) +@FirstLevel_0(Array(new Plain_0(4), new Plain_0(5))) +@FirstLevel_0(Array(new Plain_0(6), new Plain_0(7))) trait T object Test: diff --git a/tests/run/t9400.scala b/tests/run/t9400.scala new file mode 100644 index 000000000000..9f6fda026810 --- /dev/null +++ b/tests/run/t9400.scala @@ -0,0 +1,28 @@ +// scalajs: --skip + +class Deprecation extends Deprecated { + final val annotationType = classOf[Deprecated] + + def forRemoval(): Boolean = false + def since(): String = "" +} + +class Suppression extends SuppressWarnings { + final val annotationType = classOf[SuppressWarnings] + + def value = Array("unchecked") +} + +class Retention(runtime: Boolean) extends java.lang.annotation.Retention { + final val annotationType = classOf[Retention] + + def value = + if (runtime) java.lang.annotation.RetentionPolicy.RUNTIME + else java.lang.annotation.RetentionPolicy.SOURCE +} + +object Test extends App { + new Deprecation + new Suppression + new Retention(true) +}