-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Support for parsing annotation arguments from java #11117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
74ab3d1
d3f46fc
d1d5826
e057a3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -854,8 +854,24 @@ class Typer extends Namer | |
case _ => tree | ||
} | ||
|
||
|
||
def typedNamedArg(tree: untpd.NamedArg, pt: Type)(using Context): NamedArg = { | ||
val arg1 = typed(tree.arg, pt) | ||
/* Special case for resolving types for arguments of an annotation defined in Java. | ||
* It allows that value of any type T can appear in positions where Array[T] is expected. | ||
* For example, both `@Annot(5)` and `@Annot({5, 6}) are viable calls of the constructor | ||
* of annotation defined as `@interface Annot { int[] value() }` | ||
* We assume that calling `typedNamedArg` in context of Java implies that we are dealing | ||
* with annotation contructor, as named arguments are not allowed anywhere else in Java. | ||
*/ | ||
val arg1 = pt match { | ||
case AppliedType(a, typ :: Nil) if ctx.isJava && a.isRef(defn.ArrayClass) => | ||
tryAlternatively { typed(tree.arg, pt) } { | ||
val elemTp = untpd.TypedSplice(TypeTree(typ)) | ||
typed(untpd.JavaSeqLiteral(tree.arg :: Nil, elemTp), pt) | ||
} | ||
case _ => typed(tree.arg, pt) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe merge this pattern match with the one above, to make the logic more clear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've done it that way because I wanted to avoid repeating |
||
|
||
assignType(cpy.NamedArg(tree)(tree.name, arg1), arg1) | ||
} | ||
|
||
|
@@ -1977,10 +1993,10 @@ class Typer extends Namer | |
*/ | ||
def annotContext(mdef: untpd.Tree, sym: Symbol)(using Context): Context = { | ||
def isInner(owner: Symbol) = owner == sym || sym.is(Param) && owner == sym.owner | ||
val c = ctx.outersIterator.dropWhile(c => isInner(c.owner)).next() | ||
c.property(ExprOwner) match { | ||
case Some(exprOwner) if c.owner.isClass => c.exprContext(mdef, exprOwner) | ||
case _ => c | ||
val outer = ctx.outersIterator.dropWhile(c => isInner(c.owner)).next() | ||
outer.property(ExprOwner) match { | ||
case Some(exprOwner) if outer.owner.isClass => outer.exprContext(mdef, exprOwner) | ||
case _ => outer | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
class annots.A | ||
class annots.A | ||
SOME STRING | ||
VALUE OF CONST | ||
false | ||
13.7 | ||
VALUE | ||
List(a, b, c) | ||
List() | ||
List(SINGLE) | ||
List(ABC) | ||
|
||
class annots.A | ||
class annots.A | ||
SOME STRING | ||
VALUE OF CONST | ||
false | ||
13.7 | ||
VALUE | ||
List(a, b, c) | ||
List() | ||
List(SINGLE) | ||
List(ABC) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package annots; | ||
|
||
import java.lang.annotation.*; | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithClass { | ||
Class<?> arg(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithClassDefaultName { | ||
Class<?> value(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithString { | ||
String arg(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithReference { | ||
String arg(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithBoolean { | ||
boolean arg(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithFloat { | ||
float arg(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithNested { | ||
Nested arg(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface Nested { | ||
String value(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithArray { | ||
String[] value(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithEmptyArray { | ||
String[] value(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithSingleElement { | ||
String[] value(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface WithMultipleArgs { | ||
int[] ints(); | ||
float floatVal(); | ||
Nested[] annots(); | ||
Class<?> clazz(); | ||
String[] value(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface ShouldNotCrash { | ||
String value(); | ||
} | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface ShouldAlsoNotCrash { | ||
String value(); | ||
int[] ints(); | ||
} | ||
|
||
class A { | ||
static final String CONST = "VALUE OF CONST"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
object Test: | ||
def main(args: Array[String]): Unit = | ||
annots.runTest(classOf[annots.Use_0]) | ||
println() | ||
annots.runTest(classOf[annots.Use_1]) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package annots; | ||
|
||
@WithClass(arg = A.class) | ||
@WithClassDefaultName(A.class) | ||
@WithString(arg = "SOME STRING") | ||
@WithReference(arg = A.CONST) | ||
@WithBoolean(arg = false) | ||
@WithFloat(arg = 13.7f) | ||
@WithNested(arg = @Nested("VALUE")) | ||
@WithArray({ "a", "b", "c" }) | ||
@WithEmptyArray({}) | ||
@WithSingleElement("SINGLE") | ||
@WithMultipleArgs( | ||
ints = {1, 2, 3, }, | ||
annots = { @Nested("Value"), @Nested(A.CONST) }, | ||
floatVal = 13.7f, | ||
value = "ABC", | ||
clazz = A.class | ||
) | ||
@ShouldNotCrash(false ? "A" + A.CONST : "B") | ||
@ShouldAlsoNotCrash(value = "C", ints = { 1, 2, 3, 5 - 1 }) | ||
public class Use_0 {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package annots; | ||
|
||
@WithClass(arg = A.class) | ||
@WithClassDefaultName(A.class) | ||
@WithString(arg = "SOME STRING") | ||
@WithReference(arg = A.CONST) | ||
@WithBoolean(arg = false) | ||
@WithFloat(arg = 13.7f) | ||
@WithNested(arg = @Nested("VALUE")) | ||
@WithArray({"a", "b", "c"}) | ||
@WithEmptyArray({}) | ||
@WithSingleElement("SINGLE") | ||
@WithMultipleArgs( | ||
ints = { 1, 2, 3, }, | ||
annots = { @Nested("Value"), | ||
@Nested(A.CONST) }, | ||
floatVal = 13.7f, | ||
value = "ABC", | ||
clazz = A.class | ||
) | ||
public class Use_1 {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package annots | ||
|
||
def runTest(cls: Class[_]): Unit = | ||
val params = | ||
Option(cls.getAnnotation(classOf[WithClass])).map(_.arg) :: | ||
Option(cls.getAnnotation(classOf[WithClassDefaultName])).map(_.value) :: | ||
Option(cls.getAnnotation(classOf[WithString])).map(_.arg) :: | ||
Option(cls.getAnnotation(classOf[WithReference])).map(_.arg) :: | ||
Option(cls.getAnnotation(classOf[WithBoolean])).map(_.arg) :: | ||
Option(cls.getAnnotation(classOf[WithFloat])).map(_.arg) :: | ||
Option(cls.getAnnotation(classOf[WithNested])).map(_.arg.value) :: | ||
Option(cls.getAnnotation(classOf[WithArray])).map(_.value.toList) :: | ||
Option(cls.getAnnotation(classOf[WithEmptyArray])).map(_.value.toList) :: | ||
Option(cls.getAnnotation(classOf[WithSingleElement])).map(_.value.toList) :: | ||
Option(cls.getAnnotation(classOf[WithMultipleArgs])).map(_.value.toList) :: | ||
Nil | ||
params.flatten.foreach(println) |
Uh oh!
There was an error while loading. Please reload this page.