Skip to content

Commit

Permalink
Support use-site meta-annotations
Browse files Browse the repository at this point in the history
Co-Authored-By: Guillaume Martres <[email protected]>
  • Loading branch information
dwijnand and smarter committed Nov 30, 2022
1 parent 7b9c1a8 commit 3a3ecf7
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 8 deletions.
13 changes: 13 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ object Annotations {
def sameAnnotation(that: Annotation)(using Context): Boolean =
symbol == that.symbol && tree.sameTree(that.tree)

def hasOneOfMetaAnnotation(metaSyms: Symbol*)(using Context): Boolean =
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)

/** Operations for hash-consing, can be overridden */
def hash: Int = System.identityHashCode(this)
def eql(that: Annotation) = this eq that
Expand Down
11 changes: 4 additions & 7 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package dotty.tools.dotc
package dotty.tools
package dotc
package transform

import dotty.tools.dotc.ast.{Trees, tpd, untpd, desugar}
Expand Down Expand Up @@ -186,12 +187,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
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))
})
annot.hasOneOfMetaAnnotation(metaAnnotSym, metaAnnotSymBackup)
|| keepIfNoRelevantAnnot && !annot.hasOneOfMetaAnnotation(defn.FieldAccessorMetaAnnots.toList*)
if sym.annotations.nonEmpty then
sym.filterAnnotations(shouldKeep(_))

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ object SymUtils:
self.isAllOf(EnumCase, butNot = JavaDefined)

def annotationsCarrying(meta: ClassSymbol)(using Context): List[Annotation] =
self.annotations.filter(_.symbol.hasAnnotation(meta))
self.annotations.filter(_.hasOneOfMetaAnnotation(meta))

def withAnnotationsCarrying(from: Symbol, meta: ClassSymbol)(using Context): self.type = {
self.addAnnotations(from.annotationsCarrying(meta))
Expand Down
9 changes: 9 additions & 0 deletions tests/run/i12492.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
inspecting field brandName
@MyColumnBase(name="BRAND_NAME")
inspecting field companyName
@MyColumnBase(name="COMPANY_NAME")
inspecting method companyName
inspecting method brandName
inspecting constructor MyTable
inspecting param brandName
inspecting param companyName
7 changes: 7 additions & 0 deletions tests/run/i12492/MyColumnBase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyColumnBase {
String name() default "";
}
36 changes: 36 additions & 0 deletions tests/run/i12492/MyTable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import scala.annotation.meta.field

type MyColumn = MyColumnBase @field

class MyTable(
@(MyColumnBase @field)(name="BRAND_NAME")
val brandName: String,
@MyColumn(name="COMPANY_NAME")
val companyName: String,
)

object Test:
def main(args: Array[String]): Unit =
val clasz = classOf[MyTable]

for (m <- clasz.getDeclaredFields) {
m.setAccessible(true)
println(s"inspecting field ${m.getName}")
for a <- m.getAnnotations() do
println(a)
}

for (m <- clasz.getDeclaredMethods) {
m.setAccessible(true)
println(s"inspecting method ${m.getName}")
for a <- m.getAnnotations() do
println(a)
}

for c <- clasz.getDeclaredConstructors do
c.setAccessible(true)
println(s"inspecting constructor ${c.getName}")
for p <- c.getParameters do
println(s"inspecting param ${p.getName}")
for a <- p.getAnnotations do
println(s"annotation $a")

0 comments on commit 3a3ecf7

Please sign in to comment.