changeset 2173:af8a7d00913b

Implement type annotations in method and constructor references.
author wmdietl
date Sat, 09 Feb 2013 01:09:54 -0800
parents 45cb38e995d0
children df1e29438c06
files src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java src/share/classes/com/sun/tools/javac/parser/JavacParser.java src/share/classes/com/sun/tools/javac/tree/TreeInfo.java test/tools/javac/annotations/typeAnnotations/referenceinfos/Lambda.java
diffstat 4 files changed, 239 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java	Wed Feb 06 16:52:31 2013 -0600
+++ b/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java	Sat Feb 09 01:09:54 2013 -0800
@@ -668,8 +668,23 @@
                 }
 
                 case MEMBER_REFERENCE: {
-                    if (((JCMemberReference)frame).typeargs.contains(tree)) {
-                        JCMemberReference mrframe = (JCMemberReference) frame;
+                    JCMemberReference mrframe = (JCMemberReference) frame;
+
+                    if (mrframe.expr == tree) {
+                        switch (mrframe.mode) {
+                        case INVOKE:
+                            p.type = TargetType.METHOD_REFERENCE_RECEIVER;
+                            break;
+                        case NEW:
+                            p.type = TargetType.CONSTRUCTOR_REFERENCE_RECEIVER;
+                            break;
+                        default:
+                            Assert.error("Unknown method reference mode " + mrframe.mode +
+                                    " for tree " + tree + " within frame " + frame);
+                        }
+                        p.pos = frame.pos;
+                    } else if (mrframe.typeargs != null &&
+                            mrframe.typeargs.contains(tree)) {
                         int arg = mrframe.typeargs.indexOf(tree);
                         p.type_index = arg;
                         switch (mrframe.mode) {
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Feb 06 16:52:31 2013 -0600
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Sat Feb 09 01:09:54 2013 -0800
@@ -1164,7 +1164,7 @@
             } else return illegal();
             break;
         case MONKEYS_AT:
-            // Only annotated cast types are valid
+            // Only annotated cast types and method references are valid
             List<JCAnnotation> typeAnnos = typeAnnotationsOpt();
             if (typeAnnos.isEmpty()) {
                 // else there would be no '@'
@@ -1175,17 +1175,27 @@
 
             if ((mode & TYPE) == 0) {
                 // Type annotations on class literals no longer legal
-                if (!expr.hasTag(Tag.SELECT)) {
+                switch (expr.getTag()) {
+                case REFERENCE: {
+                    JCMemberReference mref = (JCMemberReference) expr;
+                    mref.expr = toP(F.at(pos).AnnotatedType(typeAnnos, mref.expr));
+                    t = mref;
+                    break;
+                }
+                case SELECT: {
+                    JCFieldAccess sel = (JCFieldAccess) expr;
+
+                    if (sel.name != names._class) {
+                        return illegal();
+                    } else {
+                        log.error(token.pos, "no.annotations.on.dot.class");
+                        return expr;
+                    }
+                }
+                default:
                     return illegal(typeAnnos.head.pos);
                 }
-                JCFieldAccess sel = (JCFieldAccess)expr;
-
-                if (sel.name != names._class) {
-                    return illegal();
-                } else {
-                    log.error(token.pos, "no.annotations.on.dot.class");
-                    return expr;
-                }
+
             } else {
                 // Type annotations targeting a cast
                 t = insertAnnotationsToMostInner(expr, typeAnnos, false);
@@ -1457,18 +1467,40 @@
     /**
      * If we see an identifier followed by a '&lt;' it could be an unbound
      * method reference or a binary expression. To disambiguate, look for a
-     * matching '&gt;' and see if the subsequent terminal is either '.' or '#'.
+     * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
      */
     @SuppressWarnings("fallthrough")
     boolean isUnboundMemberRef() {
         int pos = 0, depth = 0;
-        for (Token t = S.token(pos) ; ; t = S.token(++pos)) {
+        outer: for (Token t = S.token(pos) ; ; t = S.token(++pos)) {
             switch (t.kind) {
                 case IDENTIFIER: case UNDERSCORE: case QUES: case EXTENDS: case SUPER:
                 case DOT: case RBRACKET: case LBRACKET: case COMMA:
                 case BYTE: case SHORT: case INT: case LONG: case FLOAT:
                 case DOUBLE: case BOOLEAN: case CHAR:
+                case MONKEYS_AT:
                     break;
+
+                case LPAREN:
+                    // skip annotation values
+                    int nesting = 0;
+                    for (; ; pos++) {
+                        TokenKind tk2 = S.token(pos).kind;
+                        switch (tk2) {
+                            case EOF:
+                                return false;
+                            case LPAREN:
+                                nesting++;
+                                break;
+                            case RPAREN:
+                                nesting--;
+                                if (nesting == 0) {
+                                    continue outer;
+                                }
+                                break;
+                        }
+                    }
+
                 case LT:
                     depth++; break;
                 case GTGTGT:
@@ -1494,7 +1526,7 @@
     /**
      * If we see an identifier followed by a '&lt;' it could be an unbound
      * method reference or a binary expression. To disambiguate, look for a
-     * matching '&gt;' and see if the subsequent terminal is either '.' or '#'.
+     * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
      */
     @SuppressWarnings("fallthrough")
     ParensResult analyzeParens() {
--- a/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Wed Feb 06 16:52:31 2013 -0600
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Sat Feb 09 01:09:54 2013 -0800
@@ -235,6 +235,7 @@
         switch(tree.getTag()) {
             case TYPEAPPLY: return ((JCTypeApply)tree).getTypeArguments().isEmpty();
             case NEWCLASS: return isDiamond(((JCNewClass)tree).clazz);
+            case ANNOTATED_TYPE: return isDiamond(((JCAnnotatedType)tree).underlyingType);
             default: return false;
         }
     }
@@ -335,6 +336,8 @@
             case TYPEAPPLY:
             case TYPEARRAY:
                 return true;
+            case ANNOTATED_TYPE:
+                return isStaticSelector(((JCAnnotatedType)base).underlyingType, names);
             default:
                 return false;
         }
--- a/test/tools/javac/annotations/typeAnnotations/referenceinfos/Lambda.java	Wed Feb 06 16:52:31 2013 -0600
+++ b/test/tools/javac/annotations/typeAnnotations/referenceinfos/Lambda.java	Sat Feb 09 01:09:54 2013 -0800
@@ -33,6 +33,180 @@
 public class Lambda {
 
     @TADescriptions({
+        @TADescription(annotation = "TA", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE),
+        @TADescription(annotation = "TB", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE)
+    })
+    public String returnMethodRef1() {
+        return
+                "class Lambda {" +
+                "  public String getName() { return \"Lambda!\"; }" +
+                "}" +
+
+                "class Test {" +
+                "  java.util.function.Function<Lambda, String> lambda() {" +
+                "    return @TA @TB Lambda::getName;" +
+                "  }" +
+                "}";
+    }
+
+    @TADescriptions({
+        @TADescription(annotation = "TA", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE),
+        @TADescription(annotation = "TB", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 0 }),
+        @TADescription(annotation = "TC", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 0 }),
+        @TADescription(annotation = "TD", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 1 }),
+        @TADescription(annotation = "TE", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 1 })
+    })
+    public String returnMethodRef2() {
+        return
+                "class Lambda<S, T> {" +
+                "  public String getName() { return \"Lambda!\"; }" +
+                "}" +
+
+                "class Test {" +
+                "  java.util.function.Function<Lambda<Integer, Float>, String> lambda() {" +
+                "    return @TA Lambda<@TB @TC Integer, @TD @TE Float>::getName;" +
+                "  }" +
+                "}";
+    }
+
+    @TADescriptions({
+        @TADescription(annotation = "CTA", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE),
+        @TADescription(annotation = "CTB", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 0 }),
+        @TADescription(annotation = "CTC", type = METHOD_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 1 })
+    })
+    public String returnMethodRef3() {
+        return
+                "class Lambda<S, T> {" +
+                "  public String getName() { return \"Lambda!\"; }" +
+                "}" +
+
+                "@Target(ElementType.TYPE_USE)" +
+                "@interface CTA {" +
+                "  String value();" +
+                "}" +
+
+                "@Target(ElementType.TYPE_USE)" +
+                "@interface CTB {" +
+                "  int age();" +
+                "}" +
+
+                "@Target(ElementType.TYPE_USE)" +
+                "@interface CTC {" +
+                "  String name();" +
+                "}" +
+
+                "class Test {" +
+                "  java.util.function.Function<Lambda<Integer, Float>, String> lambda() {" +
+                "    return @CTA(\"x\") Lambda<@CTB(age = 5) Integer, @CTC(name = \"y\") Float>::getName;" +
+                "  }" +
+                "}";
+    }
+
+
+    @TADescriptions({
+        @TADescription(annotation = "TA", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE),
+        @TADescription(annotation = "TB", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE)
+    })
+    public String returnConstructorRef1() {
+        return
+                "class Lambda {" +
+                "  Lambda() { }" +
+                "}" +
+
+                "class Test {" +
+                "  Runnable lambda() {" +
+                "    return @TA @TB Lambda::new;" +
+                "  }" +
+                "}";
+    }
+
+    @TADescriptions({
+        @TADescription(annotation = "TA", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE),
+        @TADescription(annotation = "TB", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 0 }),
+        @TADescription(annotation = "TC", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 0 }),
+        @TADescription(annotation = "TD", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 1 }),
+        @TADescription(annotation = "TE", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 1 })
+    })
+    public String returnConstructorRef2() {
+        return
+                "class Lambda<S, T> {" +
+                "  Lambda() { }" +
+                "}" +
+
+                "class Test {" +
+                "  Runnable lambda() {" +
+                "    return @TA Lambda<@TB @TC Integer, @TD @TE Float>::new;" +
+                "  }" +
+                "}";
+    }
+
+    @TADescriptions({
+        @TADescription(annotation = "CTA", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE),
+        @TADescription(annotation = "CTB", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 0 }),
+        @TADescription(annotation = "CTC", type = CONSTRUCTOR_REFERENCE_RECEIVER,
+                offset = ReferenceInfoUtil.IGNORE_VALUE,
+                genericLocation = { 3, 1 })
+    })
+    public String returnConstructorRef3() {
+        return
+                "class Lambda<S, T> {" +
+                "  Lambda() { }" +
+                "}" +
+
+                "@Target(ElementType.TYPE_USE)" +
+                "@interface CTA {" +
+                "  String value();" +
+                "}" +
+
+                "@Target(ElementType.TYPE_USE)" +
+                "@interface CTB {" +
+                "  int age();" +
+                "}" +
+
+                "@Target(ElementType.TYPE_USE)" +
+                "@interface CTC {" +
+                "  String name();" +
+                "}" +
+
+                "class Test {" +
+                "  Runnable lambda() {" +
+                "    return @CTA(\"x\") Lambda<@CTB(age = 5) Integer, @CTC(name = \"y\") Float>::new;" +
+                "  }" +
+                "}";
+    }
+
+
+    @TADescriptions({
         @TADescription(annotation = "TA", type = METHOD_REFERENCE_TYPE_ARGUMENT,
                  offset = ReferenceInfoUtil.IGNORE_VALUE,
                  typeIndex = 0),