Skip to content

Commit d683839

Browse files
committed
Allow to annotate classes
1 parent 85fdd02 commit d683839

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
@@ -2665,6 +2665,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26652665
selfType,
26662666
clsFlags,
26672667
clsPrivateWithin,
2668+
Nil,
26682669
conMethodType = res => MethodType(conParamNames)(_ => conParamTypes, _ => res),
26692670
conFlags = Flags.EmptyFlags,
26702671
conPrivateWithin = Symbol.noSymbol,
@@ -2679,6 +2680,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26792680
selfType: Option[TypeRepr],
26802681
clsFlags: Flags,
26812682
clsPrivateWithin: Symbol,
2683+
clsAnnotations: List[Term],
26822684
conMethodType: TypeRepr => MethodOrPoly,
26832685
conFlags: Flags,
26842686
conPrivateWithin: Symbol,
@@ -2694,7 +2696,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26942696
clsFlags,
26952697
parents,
26962698
selfType.getOrElse(Types.NoType),
2697-
clsPrivateWithin)
2699+
clsPrivateWithin,
2700+
clsAnnotations
2701+
)
26982702
val methodType: MethodOrPoly = conMethodType(cls.typeRef)
26992703
def throwShapeException() = throw new Exception("Shapes of conMethodType and conParamFlags differ.")
27002704
def checkMethodOrPolyShape(checkedMethodType: TypeRepr, clauseIdx: Int): Unit =

library/src/scala/quoted/Quotes.scala

+2
Original file line numberDiff line numberDiff line change
@@ -3880,6 +3880,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38803880
* @param selfType The self type of the class if it has one
38813881
* @param clsFlags extra flags with which the class symbol should be constructed
38823882
* @param clsPrivateWithin the symbol within which this new class symbol should be private. May be noSymbol
3883+
* @param clsAnnotations annotations of the class
38833884
* @param conMethodType Function returning MethodOrPoly type representing the type of the constructor.
38843885
* Takes the result type as parameter which must be returned from the innermost MethodOrPoly.
38853886
* PolyType may only represent the first clause of the constructor.
@@ -3903,6 +3904,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
39033904
selfType: Option[TypeRepr],
39043905
clsFlags: Flags,
39053906
clsPrivateWithin: Symbol,
3907+
clsAnnotations: List[Term],
39063908
conMethodType: TypeRepr => MethodOrPoly,
39073909
conFlags: Flags,
39083910
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)