Skip to content

Commit 0f020f8

Browse files
committed
Allow to annotate classes
1 parent 7e991d2 commit 0f020f8

File tree

7 files changed

+83
-1
lines changed

7 files changed

+83
-1
lines changed

compiler/src/dotty/tools/dotc/core/Symbols.scala

+2
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@ object Symbols extends SymUtils {
639639
parentTypes: Symbol => List[Type],
640640
selfInfo: Type = NoType,
641641
privateWithin: Symbol = NoSymbol,
642+
annotations: List[Tree] = Nil,
642643
coord: Coord = NoCoord,
643644
compUnitInfo: CompilationUnitInfo | Null = null)(using Context): ClassSymbol = {
644645
def completer = new LazyType {
@@ -648,6 +649,7 @@ object Symbols extends SymUtils {
648649
val parents = parentTypes(cls).map(_.dealias)
649650
assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class")
650651
denot.info = ClassInfo(owner.thisType, cls, parents, decls, selfInfo)
652+
denot.annotations = annotations.map(Annotations.Annotation(_))
651653
}
652654
}
653655
newClassSymbol(owner, name, flags, completer, privateWithin, coord, compUnitInfo)

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

+5-1
Original file line numberDiff line numberDiff line change
@@ -2702,6 +2702,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27022702
selfType,
27032703
clsFlags,
27042704
clsPrivateWithin,
2705+
Nil,
27052706
conMethodType = res => MethodType(conParamNames)(_ => conParamTypes, _ => res),
27062707
conFlags = Flags.EmptyFlags,
27072708
conPrivateWithin = Symbol.noSymbol,
@@ -2716,6 +2717,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27162717
selfType: Option[TypeRepr],
27172718
clsFlags: Flags,
27182719
clsPrivateWithin: Symbol,
2720+
clsAnnotations: List[Term],
27192721
conMethodType: TypeRepr => MethodOrPoly,
27202722
conFlags: Flags,
27212723
conPrivateWithin: Symbol,
@@ -2731,7 +2733,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27312733
clsFlags,
27322734
parents,
27332735
selfType.getOrElse(Types.NoType),
2734-
clsPrivateWithin)
2736+
clsPrivateWithin,
2737+
clsAnnotations
2738+
)
27352739
val methodType: MethodOrPoly = conMethodType(cls.typeRef)
27362740
def throwShapeException() = throw new Exception("Shapes of conMethodType and conParamFlags differ.")
27372741
def checkMethodOrPolyShape(checkedMethodType: TypeRepr, clauseIdx: Int): Unit =

library/src/scala/quoted/Quotes.scala

+2
Original file line numberDiff line numberDiff line change
@@ -3895,6 +3895,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38953895
* @param selfType The self type of the class if it has one
38963896
* @param clsFlags extra flags with which the class symbol should be constructed
38973897
* @param clsPrivateWithin the symbol within which this new class symbol should be private. May be noSymbol
3898+
* @param clsAnnotations annotations of the class
38983899
* @param conMethodType Function returning MethodOrPoly type representing the type of the constructor.
38993900
* Takes the result type as parameter which must be returned from the innermost MethodOrPoly.
39003901
* PolyType may only represent the first clause of the constructor.
@@ -3918,6 +3919,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
39183919
selfType: Option[TypeRepr],
39193920
clsFlags: Flags,
39203921
clsPrivateWithin: Symbol,
3922+
clsAnnotations: List[Term],
39213923
conMethodType: TypeRepr => MethodOrPoly,
39223924
conFlags: Flags,
39233925
conPrivateWithin: Symbol,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
@JavaAnnot("string in a java annotation") @ScalaAnnotation("string in a scala annotation") class name() extends java.lang.Object
3+
4+
(new name(): scala.Any)
5+
}
6+
class Test_2$package$name$1
7+
string in a java annotation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import java.lang.annotation.*;
2+
public @Retention(RetentionPolicy.RUNTIME)
3+
@interface JavaAnnot {
4+
public String value();
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//> using options -experimental
2+
3+
import scala.quoted.*
4+
import scala.annotation.StaticAnnotation
5+
6+
class ScalaAnnotation(value: String) extends StaticAnnotation
7+
8+
inline def makeClass(inline name: String): Any = ${ makeClassExpr('name) }
9+
private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Any] = {
10+
import quotes.reflect.*
11+
12+
val name = nameExpr.valueOrAbort
13+
def decls(cls: Symbol): List[Symbol] = Nil
14+
val conMethodType = (classType: TypeRepr) => MethodType(Nil)((_: MethodType) => Nil, (_: MethodType) => classType)
15+
16+
val javaAnnotSym = TypeRepr.of[JavaAnnot].typeSymbol
17+
val scalaAnnotSym = TypeRepr.of[ScalaAnnotation].typeSymbol
18+
19+
val javaAnnotationDef = Apply(Select(New(TypeIdent(javaAnnotSym)), javaAnnotSym.primaryConstructor), List(Literal(StringConstant("string in a java annotation"))))
20+
val scalaAnnotationDef = Apply(Select(New(TypeIdent(scalaAnnotSym)), scalaAnnotSym.primaryConstructor), List(Literal(StringConstant("string in a scala annotation"))))
21+
22+
val cls = Symbol.newClass(
23+
Symbol.spliceOwner,
24+
name,
25+
parents = _ => List(TypeRepr.of[Object]),
26+
decls,
27+
selfType = None,
28+
clsFlags = Flags.EmptyFlags,
29+
clsPrivateWithin = Symbol.noSymbol,
30+
clsAnnotations = List(javaAnnotationDef, scalaAnnotationDef),
31+
conMethodType,
32+
conFlags = Flags.EmptyFlags,
33+
conPrivateWithin = Symbol.noSymbol,
34+
conParamFlags = List(List())
35+
)
36+
37+
val clsDef = ClassDef(cls, List(TypeTree.of[Object]), body = Nil)
38+
val newCls =
39+
Typed(
40+
Apply(
41+
Select(New(TypeIdent(cls)), cls.primaryConstructor),
42+
Nil
43+
),
44+
TypeTree.of[Any]
45+
)
46+
47+
val res = Block(List(clsDef), newCls).asExpr
48+
49+
Expr.ofTuple(res, Expr(res.show))
50+
51+
// {
52+
// @JavaAnnot("string in a java annotation") @ScalaAnnotation("string in a scala annotation") class name() extends java.lang.Object
53+
// (new name(): scala.Any)
54+
// }
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//> using options -experimental
2+
3+
@main def Test =
4+
val (cls, str) = makeClass("name")
5+
println(str)
6+
println(cls.getClass)
7+
println(cls.getClass.getAnnotation(classOf[JavaAnnot]).value)

0 commit comments

Comments
 (0)