--- old/src/share/classes/com/sun/tools/javac/comp/Annotate.java 2014-05-09 16:27:52.480226480 -0400 +++ new/src/share/classes/com/sun/tools/javac/comp/Annotate.java 2014-05-09 16:27:52.380222236 -0400 @@ -29,12 +29,14 @@ import java.util.LinkedHashMap; import java.util.Map; +import javax.lang.model.type.TypeKind; import javax.tools.JavaFileObject; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.code.TypeAnnotationPosition.*; import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.JCTree.*; @@ -265,10 +267,12 @@ */ Attribute.Compound enterAnnotation(JCAnnotation a, Type expected, - Env env) { - List> elems = - enterAttributeValues(a, expected, env); - Attribute.Compound ac = new Attribute.Compound(a.type, elems); + Env env, + TypeAnnotationPosition position) { + List> buf = + enterAttributeValues(a, expected, env, position); + Attribute.Compound ac = + new Attribute.Compound(a.type, buf, position); a.attribute = ac; return ac; @@ -276,19 +280,15 @@ Attribute.TypeCompound enterTypeAnnotation(JCAnnotation a, Type expected, - Env env) { - List> elems = - enterAttributeValues(a, expected, env); + Env env, + TypeAnnotationPosition position) { + List> buf = + enterAttributeValues(a, expected, env, position); if (a.attribute == null || !(a.attribute instanceof Attribute.TypeCompound)) { // Create a new TypeCompound - Attribute.TypeCompound tc = - new Attribute.TypeCompound(a.type, elems, - // TODO: Eventually, we will get rid of this use of - // unknown, because we'll get a position from - // MemberEnter (task 8027262). - TypeAnnotationPosition.unknown); + new Attribute.TypeCompound(a.type, buf, position); a.attribute = tc; return tc; } else { @@ -300,7 +300,8 @@ private List> enterAttributeValues(JCAnnotation a, Type expected, - Env env) { + Env env, + TypeAnnotationPosition position) { // The annotation might have had its type attributed (but not // checked) by attr.attribAnnotationTypes during MemberEnter, // in which case we do not need to do it again. @@ -325,13 +326,13 @@ JCExpression t = tl.head; if (!t.hasTag(ASSIGN)) { log.error(t.pos(), "annotation.value.must.be.name.value"); - enterAttributeValue(t.type = syms.errType, t, env); + enterAttributeValue(t.type = syms.errType, t, env, position); continue; } JCAssign assign = (JCAssign)t; if (!assign.lhs.hasTag(IDENT)) { log.error(t.pos(), "annotation.value.must.be.name.value"); - enterAttributeValue(t.type = syms.errType, t, env); + enterAttributeValue(t.type = syms.errType, t, env, position); continue; } JCIdent left = (JCIdent)assign.lhs; @@ -346,7 +347,7 @@ if (method.owner != a.type.tsym && !isError) log.error(left.pos(), "no.annotation.member", left.name, a.type); Type result = method.type.getReturnType(); - Attribute value = enterAttributeValue(result, assign.rhs, env); + Attribute value = enterAttributeValue(result, assign.rhs, env, position); if (!method.type.isErroneous()) buf.append(new Pair<>((MethodSymbol)method, value)); t.type = result; @@ -356,7 +357,8 @@ Attribute enterAttributeValue(Type expected, JCExpression tree, - Env env) { + Env env, + TypeAnnotationPosition position) { //first, try completing the attribution value sym - if a completion //error is thrown, we should recover gracefully, and display an //ordinary resolution diagnostic. @@ -378,8 +380,7 @@ ListBuffer buf = new ListBuffer<>(); for (List l = na.elems; l.nonEmpty(); l=l.tail) { buf.append(enterAttributeValue(types.elemtype(expected), - l.head, - env)); + l.head, env, position)); } na.type = expected; return new Attribute. @@ -393,15 +394,13 @@ log.error(na.elemtype.pos(), "new.not.allowed.in.annotation"); } for (List l = na.elems; l.nonEmpty(); l=l.tail) { - enterAttributeValue(syms.errType, - l.head, - env); + enterAttributeValue(syms.errType, l.head, env, position); } return new Attribute.Error(syms.errType); } if ((expected.tsym.flags() & Flags.ANNOTATION) != 0) { if (tree.hasTag(ANNOTATION)) { - return enterAnnotation((JCAnnotation)tree, expected, env); + return enterAnnotation((JCAnnotation)tree, expected, env, position); } else { log.error(tree.pos(), "annotation.value.must.be.annotation"); expected = syms.errType; @@ -410,7 +409,7 @@ if (tree.hasTag(ANNOTATION)) { //error recovery if (!expected.isErroneous()) log.error(tree.pos(), "annotation.not.valid.for.type", expected); - enterAnnotation((JCAnnotation)tree, syms.errType, env); + enterAnnotation((JCAnnotation)tree, syms.errType, env, position); return new Attribute.Error(((JCAnnotation)tree).annotationType.type); } if (expected.isPrimitive() || @@ -479,7 +478,8 @@ */ private T processRepeatedAnnotations(List annotations, AnnotationContext ctx, - Symbol on) { + Symbol on, + TypeAnnotationPosition position) { T firstOccurrence = annotations.head; List repeated = List.nil(); Type origAnnoType = null; @@ -491,12 +491,8 @@ !annotations.tail.isEmpty()); // i.e. size() > 1 int count = 0; - for (List al = annotations; - !al.isEmpty(); - al = al.tail) - { + for (List al = annotations; !al.isEmpty(); al = al.tail) { count++; - // There must be more than a single anno in the annotation list Assert.check(count > 1 || !al.tail.isEmpty()); @@ -536,26 +532,16 @@ new Pair(containerValueSymbol, new Attribute.Array(arrayOfOrigAnnoType, repeated)); if (ctx.isTypeCompound) { - /* TODO: the following code would be cleaner: - Attribute.TypeCompound at = new Attribute.TypeCompound(targetContainerType, List.of(p), - ((Attribute.TypeCompound)annotations.head).position); - JCTypeAnnotation annoTree = m.TypeAnnotation(at); - at = enterTypeAnnotation(annoTree, targetContainerType, ctx.env); - */ - // However, we directly construct the TypeCompound to keep the - // direct relation to the contained TypeCompounds. - Attribute.TypeCompound at = new Attribute.TypeCompound(targetContainerType, List.of(p), - ((Attribute.TypeCompound)annotations.head).position); - - // TODO: annotation applicability checks from below? - + Attribute.TypeCompound at = new Attribute.TypeCompound(targetContainerType, List.of(p), position); at.setSynthesized(true); @SuppressWarnings("unchecked") T x = (T) at; return x; } else { - Attribute.Compound c = new Attribute.Compound(targetContainerType, List.of(p)); + Attribute.Compound c = new Attribute.Compound(targetContainerType, + List.of(p), + position); JCAnnotation annoTree = m.Annotation(c); if (!chk.annotationApplicable(annoTree, on)) @@ -564,7 +550,7 @@ if (!chk.validateAnnotationDeferErrors(annoTree)) log.error(annoTree.pos(), "duplicate.annotation.invalid.repeated", origAnnoType); - c = enterAnnotation(annoTree, targetContainerType, ctx.env); + c = enterAnnotation(annoTree, targetContainerType, ctx.env, position); c.setSynthesized(true); @SuppressWarnings("unchecked") @@ -576,6 +562,7 @@ } } + /** Fetches the actual Type that should be the containing annotation. */ private Type getContainingType(Attribute.Compound currentAnno, DiagnosticPosition pos, @@ -710,13 +697,14 @@ Env env, Symbol sym, AttributeCreator creator, - boolean isTypeCompound) { + boolean isTypeCompound, + TypeAnnotationPosition position) { Map> annotated = new LinkedHashMap<>(); Map pos = new HashMap<>(); for (List al = annotations; !al.isEmpty(); al = al.tail) { JCAnnotation a = al.head; - T c = creator.create(a, syms.annotationType, env); + T c = creator.create(a, syms.annotationType, env, position); Assert.checkNonNull(c, "Failed to create annotation"); @@ -743,7 +731,7 @@ } return new AnnotationContext<>(env, annotated, pos, - isTypeCompound); + isTypeCompound); } // Gather up annotations into a map from type symbols to lists of @@ -754,10 +742,12 @@ final Env env, final Symbol sym, final boolean isTypeCompound, + final TypeAnnotationPosition position, final AttributeCreator creator, final AttributeAttacher attacher) { final AnnotationContext ctx = - prepareEnterAnnotations(annotations, env, sym, creator, isTypeCompound); + prepareEnterAnnotations(annotations, env, sym, creator, + isTypeCompound, position); final Map> annotated = ctx.annotated; boolean hasRepeated = false; @@ -781,51 +771,303 @@ // that @Repeatable and other annotations get attached. // Since the attacher uses setDeclarationAttributes, this // will be overwritten later. - attacher.attach(sym, attrs); + @SuppressWarnings("unchecked") + List tempattrs = (List) attrs; + sym.setDeclarationAttributes(tempattrs); } + if (hasRepeated) { - repeated(new Annotate.Worker() { - @Override - public String toString() { - return "repeated annotation pass of: " + sym + " in: " + sym.owner; + replacePlaceholdersAndAttach(attrs, ctx, env, sym, attacher); + } else { + attachAttributesAfterRepeated(attrs, env, attacher); + } + } + + private + void replacePlaceholdersAndAttach(final List attrs, + final AnnotationContext ctx, + final Env env, + final Symbol sym, + final AttributeAttacher attacher) { + repeated(new Annotate.Worker() { + @Override + public String toString() { + return "repeated annotation pass of: " + sym + " in: " + sym.owner; + } + + @Override + public void run() { + JavaFileObject oldSource = + log.useSource(env.toplevel.sourcefile); + try { + final List replaced = + replacePlaceholders(attrs, ctx, sym); + attachAttributesAfterRepeated(replaced, env, attacher); + } finally { + log.useSource(oldSource); } + } + }); + } - @Override - public void run() { - JavaFileObject oldSource = - log.useSource(env.toplevel.sourcefile); - try { - attacher.attach(sym, replacePlaceholders(attrs, ctx, sym)); - } finally { - log.useSource(oldSource); - } + private + void attachAttributesAfterRepeated(final List attrs, + final Env env, + final AttributeAttacher attacher) { + afterRepeated(new Worker() { + @Override + public String toString() { + return "attach pass for: " + attrs; + } + + @Override + public void run() { + JavaFileObject oldSource = + log.useSource(env.toplevel.sourcefile); + try { + attacher.attach(attrs); + } finally { + log.useSource(oldSource); } - }); + } + }); + } + + public interface AttributeAttacher { + public void attach(List attrs); + } + + public interface Reporter { + public void report(List attrs); + } + + public enum AnnotationType { DECLARATION, TYPE, BOTH } + + /** + * Determine whether an annotation is a declaration annotation, + * a type annotation, or both. + */ + public AnnotationType annotationType(Attribute.Compound a, Symbol s) { + Attribute.Compound atTarget = + a.type.tsym.attribute(syms.annotationTargetType.tsym); + if (atTarget == null) { + return inferTargetMetaInfo(a, s); + } + Attribute atValue = atTarget.member(names.value); + if (!(atValue instanceof Attribute.Array)) { + Assert.error("annotationType(): bad @Target argument " + atValue + + " (" + atValue.getClass() + ")"); + return AnnotationType.DECLARATION; // error recovery + } + Attribute.Array arr = (Attribute.Array) atValue; + boolean isDecl = false, isType = false; + for (Attribute app : arr.values) { + if (!(app instanceof Attribute.Enum)) { + Assert.error("annotationType(): unrecognized Attribute kind " + app + + " (" + app.getClass() + ")"); + isDecl = true; + continue; + } + Attribute.Enum e = (Attribute.Enum) app; + if (e.value.name == names.TYPE) { + if (s.kind == Kinds.TYP) + isDecl = true; + } else if (e.value.name == names.FIELD) { + if (s.kind == Kinds.VAR && + s.owner.kind != Kinds.MTH) + isDecl = true; + } else if (e.value.name == names.METHOD) { + if (s.kind == Kinds.MTH && + !s.isConstructor()) + isDecl = true; + } else if (e.value.name == names.PARAMETER) { + if (s.kind == Kinds.VAR && + s.owner.kind == Kinds.MTH && + (s.flags() & Flags.PARAMETER) != 0) + isDecl = true; + } else if (e.value.name == names.CONSTRUCTOR) { + if (s.kind == Kinds.MTH && + s.isConstructor()) + isDecl = true; + } else if (e.value.name == names.LOCAL_VARIABLE) { + if (s.kind == Kinds.VAR && + s.owner.kind == Kinds.MTH && + (s.flags() & Flags.PARAMETER) == 0) + isDecl = true; + } else if (e.value.name == names.ANNOTATION_TYPE) { + if (s.kind == Kinds.TYP && + (s.flags() & Flags.ANNOTATION) != 0) + isDecl = true; + } else if (e.value.name == names.PACKAGE) { + if (s.kind == Kinds.PCK) + isDecl = true; + } else if (e.value.name == names.TYPE_USE) { + if (s.kind == Kinds.TYP || + s.kind == Kinds.VAR || + (s.kind == Kinds.MTH && !s.isConstructor() && + !s.type.getReturnType().hasTag(TypeTag.VOID)) || + (s.kind == Kinds.MTH && s.isConstructor())) + isType = true; + } else if (e.value.name == names.TYPE_PARAMETER) { + /* Irrelevant in this case */ + // TYPE_PARAMETER doesn't aid in distinguishing between + // Type annotations and declaration annotations on an + // Element + } else { + Assert.error("annotationType(): unrecognized Attribute name " + e.value.name + + " (" + e.value.name.getClass() + ")"); + isDecl = true; + } + } + if (isDecl && isType) { + return AnnotationType.BOTH; + } else if (isType) { + return AnnotationType.TYPE; } else { - attacher.attach(sym, attrs); + return AnnotationType.DECLARATION; } } - private interface AttributeAttacher { - public void attach(Symbol sym, List attrs); + private Attribute.TypeCompound toTypeCompound(Attribute.Compound a) { + // It is safe to alias the position. + return new Attribute.TypeCompound(a, a.position); } - private final AttributeAttacher declAnnotationsAttacher = - new AttributeAttacher() { + /** Infer the target annotation kind, if none is given. + * We only infer declaration annotations. + */ + private static AnnotationType inferTargetMetaInfo(Attribute.Compound a, Symbol s) { + return AnnotationType.DECLARATION; + } + + private AttributeAttacher + declAnnotationsAttacher(final Symbol sym) { + return new AttributeAttacher() { @Override - public void attach(Symbol sym, List attrs) { + public void attach(List attrs) { sym.resetAnnotations(); sym.setDeclarationAttributes(attrs); } }; + } - private final AttributeAttacher typeAnnotationsAttacher = - new AttributeAttacher() { + private AttributeAttacher + typeAnnotationsAttacher(final Symbol sym) { + return new AttributeAttacher() { @Override - public void attach(Symbol sym, List attrs) { - sym.appendUniqueTypeAttributes(attrs); + public void attach(List attrs) { + if (!attrs.isEmpty()) { + attachTypeAnnotations(sym, attrs); + } } }; + } + + private void reportIllegalScoping(List attrs, + int pos) { + switch (attrs.size()) { + case 0: + // Don't issue an error if all type annotations are + // also declaration annotations. + // If the annotations are also declaration annotations, they are + // illegal as type annotations but might be legal as declaration annotations. + // The normal declaration annotation checks make sure that the use is valid. + break; + case 1: + //System.err.println("Reporting illegal scoping"); + log.error(pos, "cant.type.annotate.scoping.1", attrs); + break; + default: + //System.err.println("Reporting illegal scoping"); + log.error(pos, "cant.type.annotate.scoping", attrs); + } + } + + private Reporter + illegalScopingReporter(final int pos) { + return new Reporter() { + @Override + public void report(List attrs) { + reportIllegalScoping(attrs, pos); + } + }; + } + + private AttributeAttacher + classifyingAttacher(final Symbol sym) { + return classifyingAttacher(sym, declAnnotationsAttacher(sym), + typeAnnotationsAttacher(sym), + null); + } + + + private AttributeAttacher + classifyingAttacher(final Symbol sym, + final AttributeAttacher declAttacher, + final AttributeAttacher typeAttacher, + final Reporter reporter) { + return new AttributeAttacher() { + @Override + public void attach(List attrs) { + ListBuffer declAnnos = new ListBuffer<>(); + ListBuffer typeAnnos = new ListBuffer<>(); + ListBuffer onlyTypeAnnos = new ListBuffer<>(); + + for (Attribute.Compound a : attrs) { + Assert.check(!(a instanceof Placeholder), + "Placeholders found in annotations being attached!"); + switch (annotationType(a, sym)) { + case DECLARATION: + declAnnos.append(a); + break; + case BOTH: { + declAnnos.append(a); + Attribute.TypeCompound ta = toTypeCompound(a); + Assert.checkNonNull(ta.position); + typeAnnos.append(ta); + break; + } + case TYPE: { + Attribute.TypeCompound ta = toTypeCompound(a); + Assert.checkNonNull(ta.position); + typeAnnos.append(ta); + // Also keep track which annotations are only type annotations + onlyTypeAnnos.append(ta); + break; + } + default: + throw new AssertionError("Unknown annotation type"); + } + } + + if (declAttacher != null) + declAttacher.attach(declAnnos.toList()); + + if (typeAttacher != null) + typeAttacher.attach(typeAnnos.toList()); + + if (reporter != null) + reporter.report(onlyTypeAnnos.toList()); + } + }; + } + + public void attachTypeAnnotations(Symbol sym, List attrs) { + sym.appendUniqueTypeAttributes(attrs); + + // For type annotations on variables in methods, make + // sure they are attached to the owner too. + switch(sym.getKind()) { + case PARAMETER: + case LOCAL_VARIABLE: + case RESOURCE_VARIABLE: + case EXCEPTION_PARAMETER: + // Make sure all type annotations from the symbol are also + // on the owner. + sym.owner.appendUniqueTypeAttributes(attrs); + break; + } + } private List replacePlaceholders(List buf, @@ -835,7 +1077,7 @@ for (T a : buf) { if (a instanceof Placeholder) { @SuppressWarnings("unchecked") - T replacement = replaceOne((Placeholder) a, ctx, sym); + T replacement = replaceOne((Placeholder) a, ctx, sym); if (null != replacement) { result = result.prepend(replacement); @@ -854,7 +1096,7 @@ // Process repeated annotations T validRepeated = processRepeatedAnnotations(placeholder.getPlaceholderFor(), - ctx, sym); + ctx, sym, placeholder.position); if (validRepeated != null) { // Check that the container isn't manually @@ -875,11 +1117,35 @@ * Annotation processing *********************************************************************/ - /** Queue annotations for later processing. */ + void annotateLater(final List annotations, + final Env localEnv, + final Symbol s) { + annotateLater(annotations, localEnv, s, null); + } + void annotateLater(final List annotations, final Env localEnv, final Symbol s, final DiagnosticPosition deferPos) { + annotateLater(annotations, localEnv, s, deferPos, null, + declAnnotationsAttacher(s)); + } + + void annotateLater(final List annotations, + final Env localEnv, + final Symbol s, + final DiagnosticPosition deferPos, + final TypeAnnotationPosition tapos) { + annotateLater(annotations, localEnv, s, deferPos, tapos, + classifyingAttacher(s)); + } + + void annotateLater(final List annotations, + final Env localEnv, + final Symbol s, + final DiagnosticPosition deferPos, + final TypeAnnotationPosition tapos, + final AttributeAttacher attacher) { if (annotations.isEmpty()) { return; } @@ -894,30 +1160,48 @@ @Override public void run() { - Assert.check(s.kind == PCK || s.annotationsPendingCompletion()); - JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); - DiagnosticPosition prevLintPos = - deferPos != null - ? deferredLintHandler.setPos(deferPos) - : deferredLintHandler.immediate(); - Lint prevLint = deferPos != null ? null : chk.setLint(lint); - try { - if (s.hasAnnotations() && - annotations.nonEmpty()) - log.error(annotations.head.pos, - "already.annotated", - kindName(s), s); - actualEnterAnnotations(annotations, localEnv, s); - } finally { - if (prevLint != null) - chk.setLint(prevLint); - deferredLintHandler.setPos(prevLintPos); - log.useSource(prev); - } + annotateNow(annotations, localEnv, s, deferPos, + tapos, attacher); } }); - validate(new Annotate.Worker() { //validate annotations + validate(annotationValidator(annotations, localEnv, s)); + } + + private void annotateNow(final List annotations, + final Env localEnv, + final Symbol s, + final DiagnosticPosition deferPos, + final TypeAnnotationPosition position, + final AttributeAttacher attacher) { + if (annotations.isEmpty()) { + return; + } + Assert.check(s.kind == PCK || s.annotationsPendingCompletion()); + JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); + DiagnosticPosition prevLintPos = deferPos != null ? + deferredLintHandler.setPos(deferPos) : + deferredLintHandler.immediate(); + Lint prevLint = deferPos != null ? null : chk.setLint(lint); + try { + if (s.hasAnnotations() && + annotations.nonEmpty()) + log.error(annotations.head.pos, + "already.annotated", + kindName(s), s); + actualEnterAnnotations(annotations, localEnv, s, position, attacher); + } finally { + if (prevLint != null) + chk.setLint(prevLint); + deferredLintHandler.setPos(prevLintPos); + log.useSource(prev); + } + } + + private Annotate.Worker annotationValidator(final List annotations, + final Env localEnv, + final Symbol s) { + return new Annotate.Worker() { //validate annotations @Override public void run() { JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); @@ -927,11 +1211,30 @@ log.useSource(prev); } } - }); + }; + } + + private Annotate.Worker typeAnnotationValidator(final List annotations, + final Env localEnv, + final boolean isTypeParameter) { + return new Annotate.Worker() { //validate annotations + @Override + public void run() { + JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); + try { + chk.validateTypeAnnotations(annotations, isTypeParameter); + } finally { + log.useSource(prev); + } + } + }; } private interface AttributeCreator { - public T create(JCAnnotation a, Type expected, Env env); + public T create(JCAnnotation a, + Type expected, + Env env, + TypeAnnotationPosition position); } // TODO: When SE8 features can be used, these can go away and be @@ -941,8 +1244,9 @@ @Override public Attribute.Compound create(JCAnnotation a, Type expected, - Env env) { - return enterAnnotation(a, syms.annotationType, env); + Env env, + TypeAnnotationPosition position) { + return enterAnnotation(a, syms.annotationType, env, position); } }; private final AttributeCreator enterTypeAnnotationsCreator = @@ -950,19 +1254,21 @@ @Override public Attribute.TypeCompound create(JCAnnotation a, Type expected, - Env env) { - return enterTypeAnnotation(a, syms.annotationType, env); + Env env, + TypeAnnotationPosition position) { + return enterTypeAnnotation(a, syms.annotationType, env, position); } }; /** Enter a set of annotations. */ private void actualEnterAnnotations(List annotations, Env env, - Symbol s) { - Assert.checkNonNull(s, "Symbol argument to actualEnterAnnotations is null"); - attachAttributesLater(annotations, env, s, false, - enterAnnotationsCreator, - declAnnotationsAttacher); + Symbol s, + TypeAnnotationPosition position, + AttributeAttacher attacher) { + Assert.checkNonNull(s); + attachAttributesLater(annotations, env, s, false, position, + enterAnnotationsCreator, attacher); } /* @@ -971,8 +1277,10 @@ private void actualEnterTypeAnnotations(final List annotations, final Env env, final Symbol s, - final DiagnosticPosition deferPos) { - Assert.checkNonNull(s, "Symbol argument to actualEnterTypeAnnotations is nul/"); + final DiagnosticPosition deferPos, + final TypeAnnotationPosition position, + final AttributeAttacher attacher) { + Assert.checkNonNull(s); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); DiagnosticPosition prevLintPos = null; @@ -980,9 +1288,8 @@ prevLintPos = deferredLintHandler.setPos(deferPos); } try { - attachAttributesLater(annotations, env, s, true, - enterTypeAnnotationsCreator, - typeAnnotationsAttacher); + attachAttributesLater(annotations, env, s, true, position, + enterTypeAnnotationsCreator, attacher); } finally { if (prevLintPos != null) deferredLintHandler.setPos(prevLintPos); @@ -993,8 +1300,26 @@ public void annotateTypeLater(final JCTree tree, final Env env, final Symbol sym, - final DiagnosticPosition deferPos) { + final DiagnosticPosition deferPos, + final JCLambda currentLambda, + final PositionCreator creator, + final boolean speculative) { + annotateTypeLater(tree, List.nil(), env, sym, + deferPos, currentLambda, creator, speculative); + } + + public void annotateTypeLater(final JCTree tree, + final List declAnnos, + final Env env, + final Symbol sym, + final DiagnosticPosition deferPos, + final JCLambda currentLambda, + final PositionCreator creator, + final boolean speculative) { Assert.checkNonNull(sym); + Assert.checkNonNull(declAnnos); + Assert.checkNonNull(creator); + normal(new Annotate.Worker() { @Override public String toString() { @@ -1002,77 +1327,621 @@ } @Override public void run() { - tree.accept(new TypeAnnotate(env, sym, deferPos)); + if (!declAnnos.isEmpty()) { + sym.resetAnnotations(); // mark Annotations as incomplete for now + } + + tree.accept(typeAnnotater(declAnnos, sym, env, deferPos, + currentLambda, creator, speculative)); } }); } /** - * We need to use a TreeScanner, because it is not enough to visit the top-level - * annotations. We also need to visit type arguments, etc. + * A client passed into various visitors that takes a type path as + * an argument and performs an action (typically creating a + * TypeAnnotationPosition and then creating a {@code Worker} and + * adding it to a queue. */ + public abstract class PositionCreator { + public TypeAnnotationPosition create() { + return create(List.nil(), null, 0); + } + + public TypeAnnotationPosition createNonNull(List path, + JCLambda lambda, + int typeIndex) { + final TypeAnnotationPosition out = create(path, lambda, typeIndex); + + if (out != null) + return out; + else + throw new AssertionError("No annotation creator registered"); + } + + public abstract TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex); + } + + // For when we don't have a creator. Creates null. + public final PositionCreator noCreator = + new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return null; + } + }; + + // Create class extension positions + public final PositionCreator extendsCreator = + new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.classExtends(path, lambda, -1); + } + }; + + // Create interface implementation positions + public PositionCreator implementsCreator(final int idx) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.classExtends(path, lambda, idx, -1); + } + }; + } + + // Create method parameter positions + public final PositionCreator paramCreator(final int idx) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.methodParameter(path, lambda, idx, -1); + } + }; + } + + // Create class type parameter positions + public PositionCreator typeParamCreator(final int idx) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.typeParameter(path, lambda, idx, -1); + } + }; + } + + public PositionCreator typeParamBoundCreator(final JCTypeParameter typaram, + final int param_idx, + final int bound_idx) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + final int real_bound_idx = + typaram.bounds.head.type.isInterface() ? bound_idx + 1 : bound_idx; + return TypeAnnotationPosition + .typeParameterBound(path, lambda, param_idx, real_bound_idx, -1); + } + }; + } + + // Create field positions + public final PositionCreator fieldCreator = + new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.field(path, lambda, -1); + } + }; + + // Create local variable positions + public PositionCreator localVarCreator(final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.localVariable(path, lambda, pos); + } + }; + } + + public PositionCreator resourceVarCreator(final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.resourceVariable(path, lambda, pos); + } + }; + } + + public PositionCreator exceptionParamCreator(final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.exceptionParameter(path, lambda, + typeIndex, pos); + } + }; + } + + public PositionCreator methodTypeParamCreator(final int idx) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.methodTypeParameter(path, lambda, idx, -1); + } + }; + } + + public PositionCreator methodRefTypeArgCreator(final int idx, + final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.methodRefTypeArg(path, lambda, idx, pos); + } + }; + } + + public PositionCreator constructorRefTypeArgCreator(final int idx, + final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition + .constructorRefTypeArg(path, lambda, idx, pos); + } + }; + } + + public PositionCreator methodInvokeTypeArgCreator(final int idx, + final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.methodInvocationTypeArg(path, lambda, idx, pos); + } + }; + } + + public PositionCreator constructorInvokeTypeArgCreator(final int idx, + final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.constructorInvocationTypeArg(path, lambda, idx, pos); + } + }; + } + + public PositionCreator methodTypeParamBoundCreator(final JCTypeParameter typaram, + final int param_idx, + final int bound_idx) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + final int real_bound_idx = + typaram.bounds.head.type.isInterface() ? bound_idx + 1 : bound_idx; + return TypeAnnotationPosition + .methodTypeParameterBound(path, lambda, param_idx, real_bound_idx, -1); + } + }; + } + + public PositionCreator throwCreator(final int idx) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.methodThrows(path, lambda, idx, -1); + } + }; + } + + public final PositionCreator returnCreator = + new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.methodReturn(path, lambda, -1); + } + }; + + public PositionCreator receiverCreator = + new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.methodReceiver(path, lambda, -1); + } + }; + + public PositionCreator methodRefCreator(final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.methodRef(path, lambda, pos); + } + }; + } + + public PositionCreator constructorRefCreator(final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.constructorRef(path, lambda, pos); + } + }; + } + + public PositionCreator instanceOfCreator(final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.instanceOf(path, lambda, pos); + } + }; + } + + public PositionCreator newObjCreator(final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.newObj(path, lambda, pos); + } + }; + } + + public PositionCreator castCreator(final int pos) { + return new PositionCreator() { + @Override + public TypeAnnotationPosition create(List path, + JCLambda lambda, + int typeIndex) { + return TypeAnnotationPosition.typeCast(path, lambda, typeIndex, pos); + } + }; + } + + private static List addInners(Type type, + List typepath) { + Type encl = type.getEnclosingType(); + while (encl != null && encl.getKind() != TypeKind.NONE && + encl.getKind() != TypeKind.ERROR) { + typepath = typepath.append(TypePathEntry.INNER_TYPE); + encl = encl.getEnclosingType(); + } + return typepath; + } + + public TypeAnnotate typeAnnotater(final List declAnnos, + final Symbol sym, + final Env env, + final DiagnosticPosition deferPos, + final JCLambda currentLambda, + final PositionCreator creator, + final boolean speculative) { + if (!speculative) { + return new TypeAnnotate(declAnnos, sym, env, deferPos, + currentLambda, creator, + declAnnotationsAttacher(sym), + typeAnnotationsAttacher(sym)); + } else { + return new TypeAnnotate(declAnnos, sym, env, deferPos, + currentLambda, creator, null, null); + } + } + private class TypeAnnotate extends TreeScanner { - private final Env env; + protected PositionCreator creator; + private List typepath = List.nil(); + private JCLambda currentLambda; + private int type_index = 0; + private boolean innermost; + // These attachers are for declaration annotations + private AttributeAttacher declAttacher; + private AttributeAttacher typeAttacher; + private Reporter reporter; private final Symbol sym; - private DiagnosticPosition deferPos; + private final DiagnosticPosition deferPos; + private final Env env; + private final List declAnnos; - public TypeAnnotate(final Env env, + public TypeAnnotate(final List declAnnos, final Symbol sym, - final DiagnosticPosition deferPos) { - - this.env = env; + final Env env, + final DiagnosticPosition deferPos, + final JCLambda currentLambda, + final PositionCreator creator, + final AttributeAttacher declAttacher, + final AttributeAttacher typeAttacher) { + this.declAnnos = declAnnos; this.sym = sym; + this.env = env; this.deferPos = deferPos; + this.currentLambda = currentLambda; + this.creator = creator; + this.innermost = true; + this.declAttacher = declAttacher; + this.typeAttacher = typeAttacher; + this.reporter = null; + } + + private void doDeclAnnos() { + if (!declAnnos.isEmpty()) { + final TypeAnnotationPosition tapos = + creator.createNonNull(typepath, currentLambda, type_index); + annotateNow(declAnnos, env, sym, deferPos, tapos, + classifyingAttacher(sym, declAttacher, + typeAttacher, reporter)); + validate(annotationValidator(declAnnos, env, sym)); + } + } + + private void doTypeAnnos(List annos, + boolean isTypeParameter) { + if (!annos.isEmpty()) { + final AttributeAttacher currTypeAttacher = typeAttacher; + final Reporter currReporter = reporter; + final AttributeAttacher attacher = + new AttributeAttacher() { + @Override + public void attach(List attrs) { + if (currTypeAttacher != null) + currTypeAttacher.attach(attrs); + if (currReporter != null) + currReporter.report(attrs); + } + }; + final TypeAnnotationPosition tapos = + creator.createNonNull(typepath, currentLambda, type_index); + actualEnterTypeAnnotations(annos, env, sym, deferPos, tapos, + attacher); + validate(typeAnnotationValidator(annos, env, isTypeParameter)); + } + } + + @Override + public void visitTypeIdent(final JCPrimitiveTypeTree tree) { + if (innermost) { + final AttributeAttacher oldTypeAttacher = typeAttacher; + typeAttacher = + new AttributeAttacher() { + @Override + public void attach(List attrs) { + if (null != oldTypeAttacher) + oldTypeAttacher.attach(attrs); + + if (!attrs.isEmpty()) { + tree.type = tree.type.annotatedType(attrs); + } + } + }; + doDeclAnnos(); + typeAttacher = oldTypeAttacher; + } + } + + @Override + public void visitIdent(final JCIdent tree) { + if (innermost) { + final AttributeAttacher oldTypeAttacher = typeAttacher; + final Reporter oldReporter = reporter; + typeAttacher = + new AttributeAttacher() { + @Override + public void attach(List attrs) { + if (null != oldTypeAttacher) + oldTypeAttacher.attach(attrs); + + if (!attrs.isEmpty() && + !tree.type.hasTag(TypeTag.PACKAGE)) { + tree.type = tree.type.annotatedType(attrs); + } + } + }; + if (tree.type != null) { + final List oldpath = typepath; + typepath = addInners(tree.type, typepath); + doDeclAnnos(); + typepath = oldpath; + } else { + doDeclAnnos(); + } + reporter = oldReporter; + typeAttacher = oldTypeAttacher; + } + } + + @Override + public void visitAnnotatedType(JCAnnotatedType tree) { + Assert.checkNonNull(tree.getUnderlyingType().type); + final boolean oldinnermost = innermost; + innermost = false; + scan(tree.annotations); + innermost = oldinnermost; + scan(tree.underlyingType); + final Reporter oldReporter = reporter; + final List oldpath = typepath; + typepath = addInners(tree.getUnderlyingType().type, typepath); + doTypeAnnos(tree.annotations, false); + typepath = oldpath; + reporter = oldReporter; } @Override - public void visitAnnotatedType(final JCAnnotatedType tree) { - actualEnterTypeAnnotations(tree.annotations, env, sym, deferPos); - super.visitAnnotatedType(tree); + public void visitTypeArray(JCArrayTypeTree tree) { + final List oldpath = typepath; + typepath = typepath.append(TypePathEntry.ARRAY); + super.visitTypeArray(tree); + typepath = oldpath; } @Override - public void visitTypeParameter(final JCTypeParameter tree) { - actualEnterTypeAnnotations(tree.annotations, env, sym, deferPos); - super.visitTypeParameter(tree); + public void visitTypeApply(JCTypeApply tree) { + Assert.checkNonNull(tree.getType().type); + final List oldpath = typepath; + scan(tree.clazz); + + + if (tree.getType() != null && tree.getType().type != null) { + typepath = addInners(tree.getType().type, typepath); + } + final boolean oldinnermost = innermost; + innermost = false; + int i = 0; + for (List l = tree.arguments; l.nonEmpty(); + l = l.tail, i++) { + final JCExpression arg = l.head; + final List noargpath = typepath; + typepath = typepath.append(new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, i)); + scan(arg); + typepath = noargpath; + } + typepath = oldpath; + innermost = oldinnermost; } @Override - public void visitNewArray(final JCNewArray tree) { - actualEnterTypeAnnotations(tree.annotations, env, sym, deferPos); - for (List dimAnnos : tree.dimAnnotations) - actualEnterTypeAnnotations(dimAnnos, env, sym, deferPos); - super.visitNewArray(tree); + public void visitNewArray(JCNewArray tree) { + final List oldpath = typepath; + final PositionCreator oldcreator = creator; + creator = newObjCreator(tree.pos); + doTypeAnnos(tree.annotations, false); + + for (int i = 0; i < tree.dimAnnotations.size(); i++) { + final List dimAnnos = tree.dimAnnotations.get(i); + doTypeAnnos(dimAnnos, false); + // This is right. As per the type annotations spec, + // the first array dimension has no arrays in the type + // path, the second has one, and so on, and the + // element type has n for n dimensions. + typepath = typepath.append(TypePathEntry.ARRAY); + } + + // The element type is sometimes null, in the case of + // array literals. + scan(tree.elemtype); + typepath = oldpath; + creator = oldcreator; } @Override - public void visitMethodDef(final JCMethodDecl tree) { - scan(tree.mods); - scan(tree.restype); - scan(tree.typarams); - scan(tree.recvparam); + public void visitWildcard(JCWildcard tree) { + final List oldpath = typepath; + typepath = typepath.append(TypePathEntry.WILDCARD); + super.visitWildcard(tree); + typepath = oldpath; + } + + @Override + public void visitTypeParameter(JCTypeParameter tree) { + scan(tree.annotations); + Assert.checkNonNull(tree.type); + doTypeAnnos(tree.annotations, true); + } + + @Override + public void visitLambda(JCLambda tree) { + final JCLambda oldLambda = currentLambda; + currentLambda = tree; + scan(tree.body); scan(tree.params); - scan(tree.thrown); - scan(tree.defaultValue); - // Do not annotate the body, just the signature. - // scan(tree.body); + currentLambda = oldLambda; + } @Override - public void visitVarDef(final JCVariableDecl tree) { - DiagnosticPosition prevPos = deferPos; - deferPos = tree.pos(); - try { - if (sym != null && sym.kind == Kinds.VAR) { - // Don't visit a parameter once when the sym is the method - // and once when the sym is the parameter. - scan(tree.mods); - scan(tree.vartype); - } - scan(tree.init); - } finally { - deferPos = prevPos; + public void visitTypeIntersection(JCTypeIntersection tree) { + final boolean oldinnermost = innermost; + for (List l = tree.bounds; l.nonEmpty(); + l = l.tail, type_index++) { + scan(l.head); + // Set innermost to false after the first element + innermost = false; + } + innermost = oldinnermost; + } + + @Override + public void visitTypeUnion(JCTypeUnion tree) { + final boolean oldinnermost = innermost; + for (List l = tree.alternatives; l.nonEmpty(); + l = l.tail, type_index++) { + scan(l.head); + // Set innermost to false after the first element + innermost = false; } + innermost = oldinnermost; + } + + @Override + public void visitSelect(JCFieldAccess tree) { + Symbol sym = tree.sym; + //System.err.println("visitSelect " + tree); + final AttributeAttacher oldTypeAttacher = typeAttacher; + final Reporter oldReporter = reporter; + // If we're selecting from an interface or a static class, + // set up attachers that will only attach declaration + // annotations and will report type annotations as errors. + Type selectedTy = tree.selected.type; + if (sym != null && (sym.isStatic() || sym.isInterface() || + selectedTy.hasTag(TypeTag.PACKAGE))) { + typeAttacher = null; + reporter = illegalScopingReporter(tree.pos); + } + super.visitSelect(tree); + typeAttacher = oldTypeAttacher; + reporter = oldReporter; + } + + // These methods stop the visitor from continuing on when it + // sees a definition. + @Override + public void visitVarDef(final JCVariableDecl tree) { } @Override @@ -1084,11 +1953,57 @@ @Override public void visitNewClass(JCNewClass tree) { - if (tree.def == null) { - // For an anonymous class instantiation the class - // will be visited separately. - super.visitNewClass(tree); - } + // This will be visited by Attr later, so don't do + // anything. + } + } + + private class TypeAnnotateExpr extends TypeAnnotate { + public TypeAnnotateExpr(final Symbol sym, + final Env env, + final DiagnosticPosition deferPos, + final JCLambda currentLambda, + final PositionCreator creator) { + super(List.nil(), sym, env, deferPos, + currentLambda, creator, null, null); + } + + @Override + public void visitTypeCast(final JCTypeCast tree) { + final PositionCreator oldcreator = creator; + creator = castCreator(tree.pos); + super.visitTypeCast(tree); + creator = oldcreator; + } + + @Override + public void visitTypeTest(JCInstanceOf tree) { + final PositionCreator oldcreator = creator; + creator = instanceOfCreator(tree.pos); + super.visitTypeTest(tree); + creator = oldcreator; } } + + public void typeAnnotateExprLater(final JCTree tree, + final Env env, + final Symbol sym, + final DiagnosticPosition deferPos, + final JCLambda currentLambda, + final PositionCreator creator) { + Assert.checkNonNull(sym); + Assert.checkNonNull(creator); + + normal(new Annotate.Worker() { + @Override + public String toString() { + return "type annotate " + tree + " onto " + sym + " in " + sym.owner; + } + @Override + public void run() { + tree.accept(new TypeAnnotateExpr(sym, env, deferPos, + currentLambda, creator)); + } + }); + } }