/* * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.javac.comp; import java.util.HashMap; 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.*; import static com.sun.tools.javac.code.Kinds.*; import static com.sun.tools.javac.code.TypeTag.ARRAY; import static com.sun.tools.javac.code.TypeTag.CLASS; import static com.sun.tools.javac.tree.JCTree.Tag.*; /** Enter annotations on symbols. Annotations accumulate in a queue, * which is processed at the top level of any set of recursive calls * requesting it be processed. * *

This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice. */ public class Annotate { protected static final Context.Key annotateKey = new Context.Key<>(); public static Annotate instance(Context context) { Annotate instance = context.get(annotateKey); if (instance == null) instance = new Annotate(context); return instance; } private final Attr attr; private final TreeMaker make; private final Log log; private final Symtab syms; private final Names names; private final Resolve rs; private final Types types; private final ConstFold cfolder; private final Check chk; private final Lint lint; private final DeferredLintHandler deferredLintHandler; private final Source source; private boolean allowTypeAnnos; private boolean allowRepeatedAnnos; protected Annotate(Context context) { context.put(annotateKey, this); attr = Attr.instance(context); make = TreeMaker.instance(context); log = Log.instance(context); syms = Symtab.instance(context); names = Names.instance(context); rs = Resolve.instance(context); types = Types.instance(context); cfolder = ConstFold.instance(context); chk = Check.instance(context); source = Source.instance(context); lint = Lint.instance(context); deferredLintHandler = DeferredLintHandler.instance(context); allowRepeatedAnnos = source.allowRepeatedAnnotations(); allowTypeAnnos = source.allowTypeAnnotations(); } /* ******************************************************************** * Queue maintenance *********************************************************************/ private int enterCount = 0; ListBuffer q = new ListBuffer<>(); ListBuffer typesQ = new ListBuffer<>(); ListBuffer repeatedQ = new ListBuffer<>(); ListBuffer afterRepeatedQ = new ListBuffer<>(); ListBuffer validateQ = new ListBuffer<>(); public void earlier(Worker a) { q.prepend(a); } public void normal(Worker a) { q.append(a); } public void typeAnnotation(Worker a) { typesQ.append(a); } public void repeated(Worker a) { repeatedQ.append(a); } public void afterRepeated(Worker a) { afterRepeatedQ.append(a); } public void validate(Worker a) { validateQ.append(a); } /** Called when the Enter phase starts. */ public void enterStart() { enterCount++; } /** Called after the Enter phase completes. */ public void enterDone() { enterCount--; flush(); } /** Variant which allows for a delayed flush of annotations. * Needed by ClassReader */ public void enterDoneWithoutFlush() { enterCount--; } public void flush() { if (enterCount != 0) return; enterCount++; try { while (q.nonEmpty()) { q.next().run(); } while (typesQ.nonEmpty()) { typesQ.next().run(); } while (repeatedQ.nonEmpty()) { repeatedQ.next().run(); } while (afterRepeatedQ.nonEmpty()) { afterRepeatedQ.next().run(); } while (validateQ.nonEmpty()) { validateQ.next().run(); } } finally { enterCount--; } } /** A client that needs to run during {@link #flush()} registers an worker * into one of the queues defined in this class. The queues are: {@link #earlier(Worker)}, * {@link #normal(Worker)}, {@link #typeAnnotation(Worker)}, {@link #repeated(Worker)}, * {@link #afterRepeated(Worker)}, {@link #validate(Worker)}. * The {@link Worker#run()} method will called inside the {@link #flush()} * call. Queues are empties in the abovementioned order. */ public interface Worker { void run(); String toString(); } /** * This context contains all the information needed to synthesize new * annotations trees by the completer for repeating annotations. */ private class AnnotationContext { public final Env env; public final Map> annotated; public final Map pos; public final boolean isTypeCompound; public AnnotationContext(Env env, Map> annotated, Map pos, boolean isTypeCompound) { Assert.checkNonNull(env); Assert.checkNonNull(annotated); Assert.checkNonNull(pos); this.env = env; this.annotated = annotated; this.pos = pos; this.isTypeCompound = isTypeCompound; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("RepeatedContext["); for (Map.Entry> entry : annotated.entrySet()) { sb.append(" "); sb.append(entry.getKey()); sb.append(" = { "); sb.append(entry.getValue()); sb.append(" }"); } sb.append(" ]"); return sb.toString(); } } private static class Placeholder extends Attribute.Compound { private final Annotate.AnnotationContext ctx; private final List placeholderFor; private final Symbol on; public Placeholder(Annotate.AnnotationContext ctx, List placeholderFor, Symbol on) { super(on.type, List.>nil(), placeholderFor.head.position); this.ctx = ctx; this.placeholderFor = placeholderFor; this.on = on; } @Override public String toString() { return ""; } public List getPlaceholderFor() { return placeholderFor; } public Annotate.AnnotationContext getRepeatedContext() { return ctx; } } /* ******************************************************************** * Compute an attribute from its annotation. *********************************************************************/ /** Process a single compound annotation, returning its * Attribute. Used from MemberEnter for attaching the attributes * to the annotated symbol. */ Attribute.Compound enterAnnotation(JCAnnotation a, Type expected, 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; } Attribute.TypeCompound enterTypeAnnotation(JCAnnotation a, Type expected, 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, buf, position); a.attribute = tc; return tc; } else { // Use an existing TypeCompound return (Attribute.TypeCompound)a.attribute; } } private List> enterAttributeValues(JCAnnotation a, Type expected, 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. Type at = (a.annotationType.type != null ? a.annotationType.type : attr.attribType(a.annotationType, env)); a.type = chk.checkType(a.annotationType.pos(), at, expected); boolean isError = a.type.isErroneous(); if ((a.type.tsym.flags() & Flags.ANNOTATION) == 0 && !isError) { log.error(a.annotationType.pos(), "not.annotation.type", a.type.toString()); isError = true; } List args = a.args; if (args.length() == 1 && !args.head.hasTag(ASSIGN)) { // special case: elided "value=" assumed args.head = make.at(args.head.pos). Assign(make.Ident(names.value), args.head); } ListBuffer> buf = new ListBuffer<>(); for (List tl = args; tl.nonEmpty(); tl = tl.tail) { 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, 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, position); continue; } JCIdent left = (JCIdent)assign.lhs; Symbol method = rs.resolveQualifiedMethod(assign.rhs.pos(), env, a.type, left.name, List.nil(), null); left.sym = method; left.type = method.type; 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, position); if (!method.type.isErroneous()) buf.append(new Pair<>((MethodSymbol)method, value)); t.type = result; } return buf.toList(); } Attribute enterAttributeValue(Type expected, JCExpression tree, 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. try { expected.tsym.complete(); } catch(CompletionFailure e) { log.error(tree.pos(), "cant.resolve", Kinds.kindName(e.sym), e.sym); expected = syms.errType; } if (expected.hasTag(ARRAY)) { if (!tree.hasTag(NEWARRAY)) { tree = make.at(tree.pos). NewArray(null, List.nil(), List.of(tree)); } JCNewArray na = (JCNewArray)tree; if (na.elemtype != null) { log.error(na.elemtype.pos(), "new.not.allowed.in.annotation"); } ListBuffer buf = new ListBuffer<>(); for (List l = na.elems; l.nonEmpty(); l=l.tail) { buf.append(enterAttributeValue(types.elemtype(expected), l.head, env, position)); } na.type = expected; return new Attribute. Array(expected, buf.toArray(new Attribute[buf.length()])); } if (tree.hasTag(NEWARRAY)) { //error recovery if (!expected.isErroneous()) log.error(tree.pos(), "annotation.value.not.allowable.type"); JCNewArray na = (JCNewArray)tree; if (na.elemtype != null) { 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, position); } return new Attribute.Error(syms.errType); } if ((expected.tsym.flags() & Flags.ANNOTATION) != 0) { if (tree.hasTag(ANNOTATION)) { return enterAnnotation((JCAnnotation)tree, expected, env, position); } else { log.error(tree.pos(), "annotation.value.must.be.annotation"); expected = syms.errType; } } 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, position); return new Attribute.Error(((JCAnnotation)tree).annotationType.type); } if (expected.isPrimitive() || (types.isSameType(expected, syms.stringType) && !expected.hasTag(TypeTag.ERROR))) { Type result = attr.attribExpr(tree, env, expected); if (result.isErroneous()) return new Attribute.Error(result.getOriginalType()); if (result.constValue() == null) { log.error(tree.pos(), "attribute.value.must.be.constant"); return new Attribute.Error(expected); } result = cfolder.coerce(result, expected); return new Attribute.Constant(expected, result.constValue()); } if (expected.tsym == syms.classType.tsym) { Type result = attr.attribExpr(tree, env, expected); if (result.isErroneous()) { // Does it look like an unresolved class literal? if (TreeInfo.name(tree) == names._class && ((JCFieldAccess) tree).selected.type.isErroneous()) { Name n = (((JCFieldAccess) tree).selected).type.tsym.flatName(); return new Attribute.UnresolvedClass(expected, types.createErrorType(n, syms.unknownSymbol, syms.classType)); } else { return new Attribute.Error(result.getOriginalType()); } } // Class literals look like field accesses of a field named class // at the tree level if (TreeInfo.name(tree) != names._class) { log.error(tree.pos(), "annotation.value.must.be.class.literal"); return new Attribute.Error(syms.errType); } return new Attribute.Class(types, (((JCFieldAccess) tree).selected).type); } if (expected.hasTag(CLASS) && (expected.tsym.flags() & Flags.ENUM) != 0) { Type result = attr.attribExpr(tree, env, expected); Symbol sym = TreeInfo.symbol(tree); if (sym == null || TreeInfo.nonstaticSelect(tree) || sym.kind != Kinds.VAR || (sym.flags() & Flags.ENUM) == 0) { log.error(tree.pos(), "enum.annotation.must.be.enum.constant"); return new Attribute.Error(result.getOriginalType()); } VarSymbol enumerator = (VarSymbol) sym; return new Attribute.Enum(expected, enumerator); } //error recovery: if (!expected.isErroneous()) log.error(tree.pos(), "annotation.value.not.allowable.type"); return new Attribute.Error(attr.attribExpr(tree, env, expected)); } /* ********************************* * Support for repeating annotations ***********************************/ /* Process repeated annotations. This method returns the * synthesized container annotation or null IFF all repeating * annotation are invalid. This method reports errors/warnings. */ private T processRepeatedAnnotations(List annotations, AnnotationContext ctx, Symbol on, TypeAnnotationPosition position) { T firstOccurrence = annotations.head; List repeated = List.nil(); Type origAnnoType = null; Type arrayOfOrigAnnoType = null; Type targetContainerType = null; MethodSymbol containerValueSymbol = null; Assert.check(!annotations.isEmpty() && !annotations.tail.isEmpty()); // i.e. size() > 1 int count = 0; 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()); T currentAnno = al.head; origAnnoType = currentAnno.type; if (arrayOfOrigAnnoType == null) { arrayOfOrigAnnoType = types.makeArrayType(origAnnoType); } // Only report errors if this isn't the first occurrence I.E. count > 1 boolean reportError = count > 1; Type currentContainerType = getContainingType(currentAnno, ctx.pos.get(currentAnno), reportError); if (currentContainerType == null) { continue; } // Assert that the target Container is == for all repeated // annos of the same annotation type, the types should // come from the same Symbol, i.e. be '==' Assert.check(targetContainerType == null || currentContainerType == targetContainerType); targetContainerType = currentContainerType; containerValueSymbol = validateContainer(targetContainerType, origAnnoType, ctx.pos.get(currentAnno)); if (containerValueSymbol == null) { // Check of CA type failed // errors are already reported continue; } repeated = repeated.prepend(currentAnno); } if (!repeated.isEmpty()) { repeated = repeated.reverse(); TreeMaker m = make.at(ctx.pos.get(firstOccurrence)); Pair p = new Pair(containerValueSymbol, new Attribute.Array(arrayOfOrigAnnoType, repeated)); if (ctx.isTypeCompound) { 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), position); JCAnnotation annoTree = m.Annotation(c); if (!chk.annotationApplicable(annoTree, on)) log.error(annoTree.pos(), "invalid.repeatable.annotation.incompatible.target", targetContainerType, origAnnoType); if (!chk.validateAnnotationDeferErrors(annoTree)) log.error(annoTree.pos(), "duplicate.annotation.invalid.repeated", origAnnoType); c = enterAnnotation(annoTree, targetContainerType, ctx.env, position); c.setSynthesized(true); @SuppressWarnings("unchecked") T x = (T) c; return x; } } else { return null; // errors should have been reported elsewhere } } /** Fetches the actual Type that should be the containing annotation. */ private Type getContainingType(Attribute.Compound currentAnno, DiagnosticPosition pos, boolean reportError) { Type origAnnoType = currentAnno.type; TypeSymbol origAnnoDecl = origAnnoType.tsym; // Fetch the Repeatable annotation from the current // annotation's declaration, or null if it has none Attribute.Compound ca = origAnnoDecl.attribute(syms.repeatableType.tsym); if (ca == null) { // has no Repeatable annotation if (reportError) log.error(pos, "duplicate.annotation.missing.container", origAnnoType, syms.repeatableType); return null; } return filterSame(extractContainingType(ca, pos, origAnnoDecl), origAnnoType); } // returns null if t is same as 's', returns 't' otherwise private Type filterSame(Type t, Type s) { if (t == null || s == null) { return t; } return types.isSameType(t, s) ? null : t; } /** Extract the actual Type to be used for a containing annotation. */ private Type extractContainingType(Attribute.Compound ca, DiagnosticPosition pos, TypeSymbol annoDecl) { // The next three checks check that the Repeatable annotation // on the declaration of the annotation type that is repeating is // valid. // Repeatable must have at least one element if (ca.values.isEmpty()) { log.error(pos, "invalid.repeatable.annotation", annoDecl); return null; } Pair p = ca.values.head; Name name = p.fst.name; if (name != names.value) { // should contain only one element, named "value" log.error(pos, "invalid.repeatable.annotation", annoDecl); return null; } if (!(p.snd instanceof Attribute.Class)) { // check that the value of "value" is an Attribute.Class log.error(pos, "invalid.repeatable.annotation", annoDecl); return null; } return ((Attribute.Class)p.snd).getValue(); } /* Validate that the suggested targetContainerType Type is a valid * container type for repeated instances of originalAnnoType * annotations. Return null and report errors if this is not the * case, return the MethodSymbol of the value element in * targetContainerType if it is suitable (this is needed to * synthesize the container). */ private MethodSymbol validateContainer(Type targetContainerType, Type originalAnnoType, DiagnosticPosition pos) { MethodSymbol containerValueSymbol = null; boolean fatalError = false; // Validate that there is a (and only 1) value method Scope scope = targetContainerType.tsym.members(); int nr_value_elems = 0; boolean error = false; for(Symbol elm : scope.getElementsByName(names.value)) { nr_value_elems++; if (nr_value_elems == 1 && elm.kind == Kinds.MTH) { containerValueSymbol = (MethodSymbol)elm; } else { error = true; } } if (error) { log.error(pos, "invalid.repeatable.annotation.multiple.values", targetContainerType, nr_value_elems); return null; } else if (nr_value_elems == 0) { log.error(pos, "invalid.repeatable.annotation.no.value", targetContainerType); return null; } // validate that the 'value' element is a method // probably "impossible" to fail this if (containerValueSymbol.kind != Kinds.MTH) { log.error(pos, "invalid.repeatable.annotation.invalid.value", targetContainerType); fatalError = true; } // validate that the 'value' element has the correct return type // i.e. array of original anno Type valueRetType = containerValueSymbol.type.getReturnType(); Type expectedType = types.makeArrayType(originalAnnoType); if (!(types.isArray(valueRetType) && types.isSameType(expectedType, valueRetType))) { log.error(pos, "invalid.repeatable.annotation.value.return", targetContainerType, valueRetType, expectedType); fatalError = true; } if (error) { fatalError = true; } // The conditions for a valid containing annotation are made // in Check.validateRepeatedAnnotaton(); return fatalError ? null : containerValueSymbol; } private AnnotationContext prepareEnterAnnotations(List annotations, Env env, Symbol sym, AttributeCreator creator, 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, position); Assert.checkNonNull(c, "Failed to create annotation"); if (annotated.containsKey(a.type.tsym)) { if (!allowRepeatedAnnos) { log.error(a.pos(), "repeatable.annotations.not.supported.in.source"); allowRepeatedAnnos = true; } ListBuffer l = annotated.get(a.type.tsym); l = l.append(c); annotated.put(a.type.tsym, l); pos.put(c, a.pos()); } else { annotated.put(a.type.tsym, ListBuffer.of(c)); pos.put(c, a.pos()); } // Note: @Deprecated has no effect on local variables and parameters if (!c.type.isErroneous() && sym.owner.kind != MTH && types.isSameType(c.type, syms.deprecatedType)) { sym.flags_field |= Flags.DEPRECATED; } } return new AnnotationContext<>(env, annotated, pos, isTypeCompound); } // Gather up annotations into a map from type symbols to lists of // Compound attributes, then continue on with repeating // annotations processing private void attachAttributesLater(final List annotations, 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, position); final Map> annotated = ctx.annotated; boolean hasRepeated = false; List buf = List.nil(); for (ListBuffer lb : annotated.values()) { if (lb.size() == 1) { buf = buf.prepend(lb.first()); } else { @SuppressWarnings("unchecked") T res = (T) new Placeholder<>(ctx, lb.toList(), sym); buf = buf.prepend(res); hasRepeated = true; } } final List attrs = buf.reverse(); if (!isTypeCompound) { // Attach declaration attributes early, so // that @Repeatable and other annotations get attached. // Since the attacher uses setDeclarationAttributes, this // will be overwritten later. @SuppressWarnings("unchecked") List tempattrs = (List) attrs; sym.setDeclarationAttributes(tempattrs); } if (hasRepeated) { 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); } } }); } 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 { return AnnotationType.DECLARATION; } } private Attribute.TypeCompound toTypeCompound(Attribute.Compound a) { // It is safe to alias the position. return new Attribute.TypeCompound(a, a.position); } /** 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(List attrs) { sym.resetAnnotations(); sym.setDeclarationAttributes(attrs); } }; } private AttributeAttacher typeAnnotationsAttacher(final Symbol sym) { return new AttributeAttacher() { @Override 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, Annotate.AnnotationContext ctx, Symbol sym) { List result = List.nil(); for (T a : buf) { if (a instanceof Placeholder) { @SuppressWarnings("unchecked") T replacement = replaceOne((Placeholder) a, ctx, sym); if (null != replacement) { result = result.prepend(replacement); } } else { result = result.prepend(a); } } return result.reverse(); } private T replaceOne(Placeholder placeholder, Annotate.AnnotationContext ctx, Symbol sym) { // Process repeated annotations T validRepeated = processRepeatedAnnotations(placeholder.getPlaceholderFor(), ctx, sym, placeholder.position); if (validRepeated != null) { // Check that the container isn't manually // present along with repeated instances of // its contained annotation. ListBuffer manualContainer = ctx.annotated.get(validRepeated.type.tsym); if (manualContainer != null) { log.error(ctx.pos.get(manualContainer.first()), "invalid.repeatable.annotation.repeated.and.container.present", manualContainer.first().type.tsym); } } // A null return will delete the Placeholder return validRepeated; } /* ******************************************************************** * Annotation 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; } if (s.kind != PCK) { s.resetAnnotations(); // mark Annotations as incomplete for now } normal(new Annotate.Worker() { @Override public String toString() { return "annotate " + annotations + " onto " + s + " in " + s.owner; } @Override public void run() { annotateNow(annotations, localEnv, s, deferPos, tapos, attacher); } }); 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); try { chk.validateAnnotations(annotations, s); } finally { 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, TypeAnnotationPosition position); } // TODO: When SE8 features can be used, these can go away and be // replaced by method refs. private final AttributeCreator enterAnnotationsCreator = new AttributeCreator() { @Override public Attribute.Compound create(JCAnnotation a, Type expected, Env env, TypeAnnotationPosition position) { return enterAnnotation(a, syms.annotationType, env, position); } }; private final AttributeCreator enterTypeAnnotationsCreator = new AttributeCreator() { @Override public Attribute.TypeCompound create(JCAnnotation a, Type expected, 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, TypeAnnotationPosition position, AttributeAttacher attacher) { Assert.checkNonNull(s); attachAttributesLater(annotations, env, s, false, position, enterAnnotationsCreator, attacher); } /* * If the symbol is non-null, attach the type annotation to it. */ private void actualEnterTypeAnnotations(final List annotations, final Env env, final Symbol s, final DiagnosticPosition deferPos, final TypeAnnotationPosition position, final AttributeAttacher attacher) { Assert.checkNonNull(s); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); DiagnosticPosition prevLintPos = null; if (deferPos != null) { prevLintPos = deferredLintHandler.setPos(deferPos); } try { attachAttributesLater(annotations, env, s, true, position, enterTypeAnnotationsCreator, attacher); } finally { if (prevLintPos != null) deferredLintHandler.setPos(prevLintPos); log.useSource(prev); } } public void annotateTypeLater(final JCTree tree, final Env env, final Symbol sym, 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() { return "type annotate " + tree + " onto " + sym + " in " + sym.owner; } @Override public void run() { if (!declAnnos.isEmpty()) { sym.resetAnnotations(); // mark Annotations as incomplete for now } tree.accept(typeAnnotater(declAnnos, sym, env, deferPos, currentLambda, creator, speculative)); } }); } /** * 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 { 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 final DiagnosticPosition deferPos; private final Env env; private final List declAnnos; public TypeAnnotate(final List declAnnos, final Symbol sym, 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 visitTypeArray(JCArrayTypeTree tree) { final List oldpath = typepath; typepath = typepath.append(TypePathEntry.ARRAY); super.visitTypeArray(tree); typepath = oldpath; } @Override 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(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 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); currentLambda = oldLambda; } @Override 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 public void visitClassDef(JCClassDecl tree) { // We can only hit a classdef if it is declared within // a method. Ignore it - the class will be visited // separately later. } @Override public void visitNewClass(JCNewClass 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)); } }); } }