org.eclipse.jdt.internal.compiler.ast.Annotation.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.ast.Annotation.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contributions for
 *                        bug 186342 - [compiler][null] Using annotations for null checking
 *                        bug 365662 - [compiler][null] warn on contradictory and redundant null annotations
 *                        bug 331649 - [compiler][null] consider null annotations for fields
 *                        Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
 *                        Bug 415043 - [1.8][null] Follow-up re null type annotations after bug 392099
 *                        Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations
 *                        Bug 415850 - [1.8] Ensure RunJDTCoreTests can cope with null annotations enabled
 *                        Bug 417295 - [1.8[[null] Massage type annotated null analysis to gel well with deep encoded type bindings.
 *                        Bug 424728 - [1.8][null] Unexpected error: The nullness annotation 'XXXX' is not applicable at this location
 *                        Bug 392245 - [1.8][compiler][null] Define whether / how @NonNullByDefault applies to TYPE_USE locations
 *                        Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault
 *                        Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
 *                        Bug 457210 - [1.8][compiler][null] Wrong Nullness errors given on full build build but not on incremental build?
 *                        Bug 469584 - ClassCastException in Annotation.detectStandardAnnotation (320) 
 *        Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
 *                          Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
 *                          Bug 409517 - [1.8][compiler] Type annotation problems on more elaborate array references
 *                          Bug 415397 - [1.8][compiler] Type Annotations on wildcard type argument dropped
 *                          Bug 414384 - [1.8] type annotation on abbreviated inner class is not marked as inner type
 *      Jesper S Moller <jesper@selskabet.org> -  Contributions for
 *                          Bug 412153 - [1.8][compiler] Check validity of annotations which may be repeatable
 *                          Bug 412151 - [1.8][compiler] Check repeating annotation's collection type
 *                          Bug 412149 - [1.8][compiler] Emit repeated annotations into the designated container
 *                          Bug 419209 - [1.8] Repeating container annotations should be rejected in the presence of annotation it contains
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.Stack;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature;
import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.IrritantSet;
import org.eclipse.jdt.internal.compiler.lookup.*;

/**
 * Annotation
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public abstract class Annotation extends Expression {

    Annotation persistibleAnnotation = this; // Emit this into class file, unless this is a repeating annotation, in which case package this into the designated container.

    /**
     * Return the location for the corresponding annotation inside the type reference, <code>null</code> if none.
     */
    public static int[] getLocations(final Expression reference, final Annotation annotation) {

        class LocationCollector extends ASTVisitor {
            Stack typePathEntries;
            Annotation searchedAnnotation;
            boolean continueSearch = true;

            public LocationCollector(Annotation currentAnnotation) {
                this.typePathEntries = new Stack();
                this.searchedAnnotation = currentAnnotation;
            }

            private int[] computeNestingDepth(TypeReference typeReference) {
                TypeBinding type = typeReference.resolvedType == null ? null
                        : typeReference.resolvedType.leafComponentType();
                int[] nestingDepths = new int[typeReference.getAnnotatableLevels()];
                if (type != null && type.isNestedType()) {
                    int depth = 0;
                    TypeBinding currentType = type;
                    while (currentType != null) {
                        depth += (currentType.isStatic()) ? 0 : 1;
                        currentType = currentType.enclosingType();
                    }
                    // Work backwards computing whether a INNER_TYPE entry is required for each level
                    int counter = nestingDepths.length - 1;
                    while (type != null && counter >= 0) {
                        nestingDepths[counter--] = depth;
                        depth -= type.isStatic() ? 0 : 1;
                        type = type.enclosingType();
                    }
                }
                return nestingDepths;
            }

            private void inspectAnnotations(Annotation[] annotations) {
                for (int i = 0, length = annotations == null ? 0 : annotations.length; this.continueSearch
                        && i < length; i++) {
                    if (annotations[i] == this.searchedAnnotation)
                        this.continueSearch = false;
                }
            }

            private void inspectArrayDimensions(Annotation[][] annotationsOnDimensions, int dimensions) {
                for (int i = 0; this.continueSearch && i < dimensions; i++) {
                    Annotation[] annotations = annotationsOnDimensions == null ? null : annotationsOnDimensions[i];
                    inspectAnnotations(annotations);
                    if (!this.continueSearch)
                        return;
                    this.typePathEntries.push(TYPE_PATH_ELEMENT_ARRAY);
                }
            }

            private void inspectTypeArguments(TypeReference[] typeReferences) {
                for (int i = 0, length = typeReferences == null ? 0 : typeReferences.length; this.continueSearch
                        && i < length; i++) {
                    int size = this.typePathEntries.size();
                    this.typePathEntries.add(new int[] { 3, i });
                    typeReferences[i].traverse(this, (BlockScope) null);
                    if (!this.continueSearch)
                        return;
                    this.typePathEntries.setSize(size);
                }
            }

            public boolean visit(TypeReference typeReference, BlockScope scope) {
                if (this.continueSearch) {
                    inspectArrayDimensions(typeReference.getAnnotationsOnDimensions(), typeReference.dimensions());
                    if (this.continueSearch) {
                        int[] nestingDepths = computeNestingDepth(typeReference);
                        Annotation[][] annotations = typeReference.annotations;
                        TypeReference[][] typeArguments = typeReference.getTypeArguments();
                        int levels = typeReference.getAnnotatableLevels();
                        int size = this.typePathEntries.size();
                        for (int i = levels - 1; this.continueSearch && i >= 0; i--) { // traverse outwards, see comment below about type annotations from SE7 locations.
                            this.typePathEntries.setSize(size);
                            for (int j = 0, depth = nestingDepths[i]; j < depth; j++)
                                this.typePathEntries.add(TYPE_PATH_INNER_TYPE);
                            if (annotations != null)
                                inspectAnnotations(annotations[i]);
                            if (this.continueSearch && typeArguments != null) {
                                inspectTypeArguments(typeArguments[i]);
                            }
                        }
                    }
                }
                return false; // if annotation is not found in the type reference, it must be one from SE7 location, typePathEntries captures the proper path entries for them. 
            }

            @Override
            public boolean visit(SingleTypeReference typeReference, BlockScope scope) {
                return visit((TypeReference) typeReference, scope);
            }

            @Override
            public boolean visit(ArrayTypeReference typeReference, BlockScope scope) {
                return visit((TypeReference) typeReference, scope);
            }

            @Override
            public boolean visit(ParameterizedSingleTypeReference typeReference, BlockScope scope) {
                return visit((TypeReference) typeReference, scope);
            }

            @Override
            public boolean visit(QualifiedTypeReference typeReference, BlockScope scope) {
                return visit((TypeReference) typeReference, scope);
            }

            @Override
            public boolean visit(ArrayQualifiedTypeReference typeReference, BlockScope scope) {
                return visit((TypeReference) typeReference, scope);
            }

            @Override
            public boolean visit(ParameterizedQualifiedTypeReference typeReference, BlockScope scope) {
                return visit((TypeReference) typeReference, scope);
            }

            @Override
            public boolean visit(Wildcard typeReference, BlockScope scope) {
                visit((TypeReference) typeReference, scope);
                if (this.continueSearch) {
                    TypeReference bound = typeReference.bound;
                    if (bound != null) {
                        int size = this.typePathEntries.size();
                        this.typePathEntries.push(TYPE_PATH_ANNOTATION_ON_WILDCARD_BOUND);
                        bound.traverse(this, scope);
                        if (this.continueSearch)
                            this.typePathEntries.setSize(size);
                    }
                }
                return false;
            }

            @Override
            public boolean visit(ArrayAllocationExpression allocationExpression, BlockScope scope) {
                if (this.continueSearch) {
                    inspectArrayDimensions(allocationExpression.getAnnotationsOnDimensions(),
                            allocationExpression.dimensions.length);
                    if (this.continueSearch) {
                        allocationExpression.type.traverse(this, scope);
                    }
                    if (this.continueSearch)
                        throw new IllegalStateException();
                }
                return false;
            }

            @Override
            public String toString() {
                StringBuffer buffer = new StringBuffer();
                buffer.append("search location for ") //$NON-NLS-1$
                        .append(this.searchedAnnotation).append("\ncurrent type_path entries : "); //$NON-NLS-1$
                for (int i = 0, maxi = this.typePathEntries.size(); i < maxi; i++) {
                    int[] typePathEntry = (int[]) this.typePathEntries.get(i);
                    buffer.append('(').append(typePathEntry[0]).append(',').append(typePathEntry[1]).append(')');
                }
                return String.valueOf(buffer);
            }
        }
        if (reference == null)
            return null;
        LocationCollector collector = new LocationCollector(annotation);
        reference.traverse(collector, (BlockScope) null);
        if (collector.typePathEntries.isEmpty()) {
            return null;
        }
        int size = collector.typePathEntries.size();
        int[] result = new int[size * 2];
        int offset = 0;
        for (int i = 0; i < size; i++) {
            int[] pathElement = (int[]) collector.typePathEntries.get(i);
            result[offset++] = pathElement[0];
            result[offset++] = pathElement[1];
        }
        return result;
    }

    final static MemberValuePair[] NoValuePairs = new MemberValuePair[0];

    static final int[] TYPE_PATH_ELEMENT_ARRAY = new int[] { 0, 0 };
    static final int[] TYPE_PATH_INNER_TYPE = new int[] { 1, 0 };
    static final int[] TYPE_PATH_ANNOTATION_ON_WILDCARD_BOUND = new int[] { 2, 0 };

    public int declarationSourceEnd;
    public Binding recipient;

    public TypeReference type;
    /**
     *  The representation of this annotation in the type system.
     */
    protected AnnotationBinding compilerAnnotation = null;

    public static long getRetentionPolicy(char[] policyName) {
        if (policyName == null || policyName.length == 0)
            return 0;
        switch (policyName[0]) {
        case 'C':
            if (CharOperation.equals(policyName, TypeConstants.UPPER_CLASS))
                return TagBits.AnnotationClassRetention;
            break;
        case 'S':
            if (CharOperation.equals(policyName, TypeConstants.UPPER_SOURCE))
                return TagBits.AnnotationSourceRetention;
            break;
        case 'R':
            if (CharOperation.equals(policyName, TypeConstants.UPPER_RUNTIME))
                return TagBits.AnnotationRuntimeRetention;
            break;
        }
        return 0; // unknown
    }

    public static long getTargetElementType(char[] elementName) {
        if (elementName == null || elementName.length == 0)
            return 0;
        switch (elementName[0]) {
        case 'A':
            if (CharOperation.equals(elementName, TypeConstants.UPPER_ANNOTATION_TYPE))
                return TagBits.AnnotationForAnnotationType;
            break;
        case 'C':
            if (CharOperation.equals(elementName, TypeConstants.UPPER_CONSTRUCTOR))
                return TagBits.AnnotationForConstructor;
            break;
        case 'F':
            if (CharOperation.equals(elementName, TypeConstants.UPPER_FIELD))
                return TagBits.AnnotationForField;
            break;
        case 'L':
            if (CharOperation.equals(elementName, TypeConstants.UPPER_LOCAL_VARIABLE))
                return TagBits.AnnotationForLocalVariable;
            break;
        case 'M':
            if (CharOperation.equals(elementName, TypeConstants.UPPER_METHOD))
                return TagBits.AnnotationForMethod;
            else if (CharOperation.equals(elementName, TypeConstants.UPPER_MODULE))
                return TagBits.AnnotationForModule;
            break;
        case 'P':
            if (CharOperation.equals(elementName, TypeConstants.UPPER_PARAMETER))
                return TagBits.AnnotationForParameter;
            else if (CharOperation.equals(elementName, TypeConstants.UPPER_PACKAGE))
                return TagBits.AnnotationForPackage;
            break;
        case 'T':
            if (CharOperation.equals(elementName, TypeConstants.TYPE))
                return TagBits.AnnotationForType;
            if (CharOperation.equals(elementName, TypeConstants.TYPE_USE_TARGET))
                return TagBits.AnnotationForTypeUse;
            if (CharOperation.equals(elementName, TypeConstants.TYPE_PARAMETER_TARGET))
                return TagBits.AnnotationForTypeParameter;
            break;
        }
        return 0; // unknown
    }

    public ElementValuePair[] computeElementValuePairs() {
        return Binding.NO_ELEMENT_VALUE_PAIRS;
    }

    /**
     * Compute the bit pattern for recognized standard annotations the compiler may need to act upon.
     * The lower bits (Binding.NullnessDefaultMASK) do not belong in tagBits, but in defaultNullness.
     */
    private long detectStandardAnnotation(Scope scope, ReferenceBinding annotationType,
            MemberValuePair valueAttribute) {
        long tagBits = 0;
        switch (annotationType.id) {
        // retention annotation
        case TypeIds.T_JavaLangAnnotationRetention:
            if (valueAttribute != null) {
                Expression expr = valueAttribute.value;
                if ((expr.bits & Binding.VARIABLE) == Binding.FIELD && expr instanceof Reference) { // anything but Reference would be a type error anyway
                    FieldBinding field = ((Reference) expr).fieldBinding();
                    if (field != null && field.declaringClass.id == T_JavaLangAnnotationRetentionPolicy) {
                        tagBits |= getRetentionPolicy(field.name);
                    }
                }
            }
            break;
        // target annotation
        case TypeIds.T_JavaLangAnnotationTarget:
            tagBits |= TagBits.AnnotationTarget; // target specified (could be empty)
            if (valueAttribute != null) {
                Expression expr = valueAttribute.value;
                if (expr instanceof ArrayInitializer) {
                    ArrayInitializer initializer = (ArrayInitializer) expr;
                    final Expression[] expressions = initializer.expressions;
                    if (expressions != null) {
                        for (int i = 0, length = expressions.length; i < length; i++) {
                            Expression initExpr = expressions[i];
                            if ((initExpr.bits & Binding.VARIABLE) == Binding.FIELD) {
                                FieldBinding field = ((Reference) initExpr).fieldBinding();
                                if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) {
                                    long element = getTargetElementType(field.name);
                                    if ((tagBits & element) != 0) {
                                        scope.problemReporter().duplicateTargetInTargetAnnotation(annotationType,
                                                (NameReference) initExpr);
                                    } else {
                                        tagBits |= element;
                                    }
                                }
                            }
                        }
                    }
                } else if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) {
                    FieldBinding field = ((Reference) expr).fieldBinding();
                    if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) {
                        tagBits |= getTargetElementType(field.name);
                    }
                }
            }
            break;
        // marker annotations
        case TypeIds.T_JavaLangDeprecated:
            tagBits |= TagBits.AnnotationDeprecated;
            if (scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK9) {
                for (MemberValuePair memberValuePair : memberValuePairs()) {
                    if (CharOperation.equals(memberValuePair.name, TypeConstants.FOR_REMOVAL)) {
                        if (memberValuePair.value instanceof TrueLiteral)
                            tagBits |= TagBits.AnnotationTerminallyDeprecated;
                        break;
                    }
                }
            }
            break;
        case TypeIds.T_JavaLangAnnotationDocumented:
            tagBits |= TagBits.AnnotationDocumented;
            break;
        case TypeIds.T_JavaLangAnnotationInherited:
            tagBits |= TagBits.AnnotationInherited;
            break;
        case TypeIds.T_JavaLangOverride:
            tagBits |= TagBits.AnnotationOverride;
            break;
        case TypeIds.T_JavaLangFunctionalInterface:
            tagBits |= TagBits.AnnotationFunctionalInterface;
            break;
        case TypeIds.T_JavaLangAnnotationRepeatable:
            tagBits |= TagBits.AnnotationRepeatable;
            break;
        case TypeIds.T_JavaLangSuppressWarnings:
            tagBits |= TagBits.AnnotationSuppressWarnings;
            break;
        case TypeIds.T_JavaLangSafeVarargs:
            tagBits |= TagBits.AnnotationSafeVarargs;
            break;
        case TypeIds.T_JavaLangInvokeMethodHandlePolymorphicSignature:
            tagBits |= TagBits.AnnotationPolymorphicSignature;
            break;
        }
        if (annotationType.hasNullBit(TypeIds.BitNullableAnnotation)) {
            tagBits |= TagBits.AnnotationNullable;
        } else if (annotationType.hasNullBit(TypeIds.BitNonNullAnnotation)) {
            tagBits |= TagBits.AnnotationNonNull;
        } else if (annotationType.hasNullBit(TypeIds.BitNonNullByDefaultAnnotation)) {
            tagBits |= determineNonNullByDefaultTagBits(annotationType, valueAttribute);
        }

        return tagBits;
    }

    private long determineNonNullByDefaultTagBits(ReferenceBinding annotationType, MemberValuePair valueAttribute) {
        long tagBits = 0;
        Object value = null;
        if (valueAttribute != null) {
            if (valueAttribute.compilerElementPair != null)
                value = valueAttribute.compilerElementPair.value;
        } else { // fetch default value  - TODO: cache it?
            MethodBinding[] methods = annotationType.methods();
            if (methods != null && methods.length == 1)
                value = methods[0].getDefaultValue();
            else
                tagBits |= Binding.DefaultLocationsForTrueValue; // custom unconfigurable NNBD
        }
        if (value instanceof BooleanConstant) {
            // boolean value is used for declaration annotations, signal using the annotation tag bit:
            tagBits |= ((BooleanConstant) value).booleanValue() ? Binding.DefaultLocationsForTrueValue
                    : Binding.NULL_UNSPECIFIED_BY_DEFAULT;
        } else if (value != null) {
            // non-boolean value signals type annotations, evaluate from DefaultLocation[] to bitvector a la Binding#NullnessDefaultMASK:
            tagBits |= nullLocationBitsFromAnnotationValue(value);
        } else {
            int result = BinaryTypeBinding.evaluateTypeQualifierDefault(annotationType);
            if (result != 0) {
                return result;
            }
        }
        return tagBits;
    }

    /**
     * Convert the value() attribute of @NonNullByDefault into a bitvector a la {@link Binding#NullnessDefaultMASK}.
     * This method understands value encodings from source and binary types.
     * 
     * <b>pre:</b> null annotation analysis is enabled
     */
    public static int nullLocationBitsFromAnnotationValue(Object value) {
        if (value instanceof Object[]) {
            if (((Object[]) value).length == 0) { // ({})
                return Binding.NULL_UNSPECIFIED_BY_DEFAULT;
            } else { // ({vals...})
                int bits = 0;
                for (Object single : (Object[]) value)
                    bits |= evaluateDefaultNullnessLocation(single);
                return bits;
            }
        } else { // (val)
            return evaluateDefaultNullnessLocation(value);
        }
    }

    private static int evaluateDefaultNullnessLocation(Object value) {
        char[] name = null;
        if (value instanceof FieldBinding) {
            name = ((FieldBinding) value).name;
        } else if (value instanceof EnumConstantSignature) {
            name = ((EnumConstantSignature) value).getEnumConstantName();
        } else if (value instanceof ElementValuePair.UnresolvedEnumConstant) {
            name = ((ElementValuePair.UnresolvedEnumConstant) value).getEnumConstantName();
        } else if (value instanceof BooleanConstant) {
            return ((BooleanConstant) value).booleanValue() ? Binding.DefaultLocationsForTrueValue
                    : Binding.NULL_UNSPECIFIED_BY_DEFAULT;
        }
        if (name != null) {
            switch (name.length) {
            case 5:
                if (CharOperation.equals(name, TypeConstants.DEFAULT_LOCATION__FIELD))
                    return Binding.DefaultLocationField;
                break;
            case 9:
                if (CharOperation.equals(name, TypeConstants.DEFAULT_LOCATION__PARAMETER))
                    return Binding.DefaultLocationParameter;
                break;
            case 10:
                if (CharOperation.equals(name, TypeConstants.DEFAULT_LOCATION__TYPE_BOUND))
                    return Binding.DefaultLocationTypeBound;
                break;
            case 11:
                if (CharOperation.equals(name, TypeConstants.DEFAULT_LOCATION__RETURN_TYPE))
                    return Binding.DefaultLocationReturnType;
                break;
            case 13:
                if (CharOperation.equals(name, TypeConstants.DEFAULT_LOCATION__TYPE_ARGUMENT))
                    return Binding.DefaultLocationTypeArgument;
                break;
            case 14:
                if (CharOperation.equals(name, TypeConstants.DEFAULT_LOCATION__TYPE_PARAMETER))
                    return Binding.DefaultLocationTypeParameter;
                if (CharOperation.equals(name, TypeConstants.DEFAULT_LOCATION__ARRAY_CONTENTS))
                    return Binding.DefaultLocationArrayContents;
                break;
            }
        }
        return 0;
    }

    public static int nullLocationBitsFromElementTypeAnnotationValue(Object value) {
        if (value instanceof Object[]) {
            if (((Object[]) value).length == 0) { // ({})
                return Binding.NULL_UNSPECIFIED_BY_DEFAULT;
            } else { // ({vals...})
                int bits = 0;
                for (Object single : (Object[]) value)
                    bits |= evaluateElementTypeNullnessLocation(single);
                return bits;
            }
        } else { // (val)
            return evaluateElementTypeNullnessLocation(value);
        }
    }

    private static int evaluateElementTypeNullnessLocation(Object value) {
        char[] name = null;
        if (value instanceof FieldBinding) {
            name = ((FieldBinding) value).name;
        } else if (value instanceof EnumConstantSignature) {
            name = ((EnumConstantSignature) value).getEnumConstantName();
        } else if (value instanceof ElementValuePair.UnresolvedEnumConstant) {
            name = ((ElementValuePair.UnresolvedEnumConstant) value).getEnumConstantName();
        }
        if (name != null) {
            switch (name.length) {
            case 5:
                if (CharOperation.equals(name, TypeConstants.UPPER_FIELD))
                    return Binding.DefaultLocationField;
                break;
            case 6:
                if (CharOperation.equals(name, TypeConstants.UPPER_METHOD))
                    return Binding.DefaultLocationReturnType;
                break;
            case 9:
                if (CharOperation.equals(name, TypeConstants.UPPER_PARAMETER))
                    return Binding.DefaultLocationParameter;
                break;
            }
        }
        return 0;
    }

    static String getRetentionName(long tagBits) {
        if ((tagBits & TagBits.AnnotationRuntimeRetention) == TagBits.AnnotationRuntimeRetention) {
            // TagBits.AnnotationRuntimeRetention combines both TagBits.AnnotationClassRetention & TagBits.AnnotationSourceRetention
            return new String(UPPER_RUNTIME);
        } else if ((tagBits & TagBits.AnnotationSourceRetention) != 0) {
            return new String(UPPER_SOURCE);
        } else {
            return new String(TypeConstants.UPPER_CLASS);
        }
    }

    private static long getAnnotationRetention(ReferenceBinding binding) {
        long retention = binding.getAnnotationTagBits() & TagBits.AnnotationRetentionMASK;
        // Retention defaults to CLASS
        return retention != 0 ? retention : TagBits.AnnotationClassRetention;
    }

    public void checkRepeatableMetaAnnotation(BlockScope scope) {

        // `this' is the @Repeatable meta annotation, its recipient is the *repeatable* annotation type - we are at the declaration site, not the repeating use site.

        ReferenceBinding repeatableAnnotationType = (ReferenceBinding) this.recipient; // know it to be an annotation type. On target miss we don't get here

        MemberValuePair[] valuePairs = this.memberValuePairs();
        if (valuePairs == null || valuePairs.length != 1)
            return;

        Object value = valuePairs[0].compilerElementPair.value;
        if (!(value instanceof ReferenceBinding))
            return; // Has deeper problems, will bark elsewhere.
        ReferenceBinding containerAnnotationType = (ReferenceBinding) value;
        if (!containerAnnotationType.isAnnotationType())
            return; // Has deeper problems, will bark elsewhere.

        repeatableAnnotationType.setContainerAnnotationType(containerAnnotationType); // For now. May be reset later to PRB in case of problems.
        checkContainerAnnotationType(valuePairs[0], scope, containerAnnotationType, repeatableAnnotationType,
                false); // false => not use site, i.e declaration site error reporting requested.
    }

    public static void checkContainerAnnotationType(ASTNode culpritNode, BlockScope scope,
            ReferenceBinding containerAnnotationType, ReferenceBinding repeatableAnnotationType, boolean useSite) {
        MethodBinding[] annotationMethods = containerAnnotationType.methods();
        boolean sawValue = false;
        for (int i = 0, length = annotationMethods.length; i < length; ++i) {
            MethodBinding method = annotationMethods[i];
            if (CharOperation.equals(method.selector, TypeConstants.VALUE)) {
                sawValue = true;
                if (method.returnType.isArrayType() && method.returnType.dimensions() == 1) {
                    ArrayBinding array = (ArrayBinding) method.returnType;
                    if (TypeBinding.equalsEquals(array.elementsType(), repeatableAnnotationType))
                        continue;
                }
                repeatableAnnotationType.tagAsHavingDefectiveContainerType();
                scope.problemReporter().containerAnnotationTypeHasWrongValueType(culpritNode,
                        containerAnnotationType, repeatableAnnotationType, method.returnType);
            } else {
                // Not the value() - must have default (or else isn't suitable as container)
                if ((method.modifiers & ClassFileConstants.AccAnnotationDefault) == 0) {
                    repeatableAnnotationType.tagAsHavingDefectiveContainerType();
                    scope.problemReporter().containerAnnotationTypeHasNonDefaultMembers(culpritNode,
                            containerAnnotationType, method.selector);
                }
            }
        }
        if (!sawValue) {
            repeatableAnnotationType.tagAsHavingDefectiveContainerType();
            scope.problemReporter().containerAnnotationTypeMustHaveValue(culpritNode, containerAnnotationType);
        }

        if (useSite)
            checkContainingAnnotationTargetAtUse((Annotation) culpritNode, scope, containerAnnotationType,
                    repeatableAnnotationType);
        else
            checkContainerAnnotationTypeTarget(culpritNode, scope, containerAnnotationType,
                    repeatableAnnotationType);

        long annotationTypeBits = getAnnotationRetention(repeatableAnnotationType);
        long containerTypeBits = getAnnotationRetention(containerAnnotationType);
        // Due to clever layout of the bits, we can compare the absolute value directly
        if (containerTypeBits < annotationTypeBits) {
            repeatableAnnotationType.tagAsHavingDefectiveContainerType();
            scope.problemReporter().containerAnnotationTypeHasShorterRetention(culpritNode,
                    repeatableAnnotationType, getRetentionName(annotationTypeBits), containerAnnotationType,
                    getRetentionName(containerTypeBits));
        }

        if ((repeatableAnnotationType.getAnnotationTagBits() & TagBits.AnnotationDocumented) != 0
                && (containerAnnotationType.getAnnotationTagBits() & TagBits.AnnotationDocumented) == 0) {
            repeatableAnnotationType.tagAsHavingDefectiveContainerType();
            scope.problemReporter().repeatableAnnotationTypeIsDocumented(culpritNode, repeatableAnnotationType,
                    containerAnnotationType);
        }

        if ((repeatableAnnotationType.getAnnotationTagBits() & TagBits.AnnotationInherited) != 0
                && (containerAnnotationType.getAnnotationTagBits() & TagBits.AnnotationInherited) == 0) {
            repeatableAnnotationType.tagAsHavingDefectiveContainerType();
            scope.problemReporter().repeatableAnnotationTypeIsInherited(culpritNode, repeatableAnnotationType,
                    containerAnnotationType);
        }
    }

    // This is for error reporting for bad targets at annotation type declaration site (as opposed to the repeat site)
    private static void checkContainerAnnotationTypeTarget(ASTNode culpritNode, Scope scope,
            ReferenceBinding containerType, ReferenceBinding repeatableAnnotationType) {
        long tagBits = repeatableAnnotationType.getAnnotationTagBits();
        if ((tagBits & TagBits.AnnotationTargetMASK) == 0)
            tagBits = TagBits.SE7AnnotationTargetMASK; // absence of @Target meta-annotation implies all SE7 targets not all targets.

        long containerAnnotationTypeTypeTagBits = containerType.getAnnotationTagBits();
        if ((containerAnnotationTypeTypeTagBits & TagBits.AnnotationTargetMASK) == 0)
            containerAnnotationTypeTypeTagBits = TagBits.SE7AnnotationTargetMASK;

        final long targets = tagBits & TagBits.AnnotationTargetMASK;
        final long containerAnnotationTypeTargets = containerAnnotationTypeTypeTagBits
                & TagBits.AnnotationTargetMASK;

        if ((containerAnnotationTypeTargets & ~targets) != 0) {
            class MissingTargetBuilder {
                StringBuffer targetBuffer = new StringBuffer();

                void check(long targetMask, char[] targetName) {
                    if ((containerAnnotationTypeTargets & targetMask & ~targets) != 0) {
                        // if targetMask equals TagBits.AnnotationForType implies
                        // TagBits.AnnotationForType is part of containerAnnotationTypeTargets
                        if (targetMask == TagBits.AnnotationForType
                                && (targets & TagBits.AnnotationForTypeUse) != 0) {
                            return;
                        }
                        add(targetName);
                    }
                }

                void checkAnnotationType(char[] targetName) {
                    if ((containerAnnotationTypeTargets & TagBits.AnnotationForAnnotationType) != 0 && ((targets
                            & (TagBits.AnnotationForAnnotationType | TagBits.AnnotationForType))) == 0) {
                        add(targetName);
                    }
                }

                private void add(char[] targetName) {
                    if (this.targetBuffer.length() != 0) {
                        this.targetBuffer.append(", "); //$NON-NLS-1$
                    }
                    this.targetBuffer.append(targetName);
                }

                @Override
                public String toString() {
                    return this.targetBuffer.toString();
                }

                public boolean hasError() {
                    return this.targetBuffer.length() != 0;
                }
            }
            MissingTargetBuilder builder = new MissingTargetBuilder();

            builder.check(TagBits.AnnotationForType, TypeConstants.TYPE);
            builder.check(TagBits.AnnotationForField, TypeConstants.UPPER_FIELD);
            builder.check(TagBits.AnnotationForMethod, TypeConstants.UPPER_METHOD);
            builder.check(TagBits.AnnotationForParameter, TypeConstants.UPPER_PARAMETER);
            builder.check(TagBits.AnnotationForConstructor, TypeConstants.UPPER_CONSTRUCTOR);
            builder.check(TagBits.AnnotationForLocalVariable, TypeConstants.UPPER_LOCAL_VARIABLE);
            builder.checkAnnotationType(TypeConstants.UPPER_ANNOTATION_TYPE);
            builder.check(TagBits.AnnotationForPackage, TypeConstants.UPPER_PACKAGE);
            builder.check(TagBits.AnnotationForTypeParameter, TypeConstants.TYPE_PARAMETER_TARGET);
            builder.check(TagBits.AnnotationForTypeUse, TypeConstants.TYPE_USE_TARGET);
            builder.check(TagBits.AnnotationForModule, TypeConstants.UPPER_MODULE);
            if (builder.hasError()) {
                repeatableAnnotationType.tagAsHavingDefectiveContainerType();
                scope.problemReporter().repeatableAnnotationTypeTargetMismatch(culpritNode,
                        repeatableAnnotationType, containerType, builder.toString());
            }
        }
    }

    // This is for error reporting for bad targets at the repeated annotation use site (as opposed to repeatable annotation type declaration site) - Leads to better message.
    public static void checkContainingAnnotationTargetAtUse(Annotation repeatingAnnotation, BlockScope scope,
            TypeBinding containerAnnotationType, TypeBinding repeatingAnnotationType) {
        // check (meta)target compatibility
        if (!repeatingAnnotationType.isValidBinding()) {
            // no need to check annotation usage if missing
            return;
        }
        if (isAnnotationTargetAllowed(repeatingAnnotation, scope, containerAnnotationType,
                repeatingAnnotation.recipient.kind()) != AnnotationTargetAllowed.YES) {
            scope.problemReporter().disallowedTargetForContainerAnnotation(repeatingAnnotation,
                    containerAnnotationType);
        }
    }

    public AnnotationBinding getCompilerAnnotation() {
        return this.compilerAnnotation;
    }

    public boolean isRuntimeInvisible() {
        final TypeBinding annotationBinding = this.resolvedType;
        if (annotationBinding == null) {
            return false;
        }
        long metaTagBits = annotationBinding.getAnnotationTagBits(); // could be forward reference

        // we need to filter out only "pure" type use and type parameter annotations, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=392119
        if ((metaTagBits & (TagBits.AnnotationForTypeParameter | TagBits.AnnotationForTypeUse)) != 0) {
            if ((metaTagBits & TagBits.SE7AnnotationTargetMASK) == 0) { // not a hybrid target. 
                return false;
            }
        }

        if ((metaTagBits & TagBits.AnnotationRetentionMASK) == 0)
            return true; // by default the retention is CLASS

        return (metaTagBits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationClassRetention;
    }

    public boolean isRuntimeTypeInvisible() {
        final TypeBinding annotationBinding = this.resolvedType;
        if (annotationBinding == null) {
            return false;
        }
        long metaTagBits = annotationBinding.getAnnotationTagBits(); // could be forward reference

        if ((metaTagBits & (TagBits.AnnotationTargetMASK)) == 0) { // explicit target required for JSR308 style annotations.
            return false;
        }
        if ((metaTagBits & (TagBits.AnnotationForTypeParameter | TagBits.AnnotationForTypeUse)) == 0) {
            return false;
        }

        if ((metaTagBits & TagBits.AnnotationRetentionMASK) == 0)
            return true; // by default the retention is CLASS

        return (metaTagBits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationClassRetention;
    }

    public boolean isRuntimeTypeVisible() {
        final TypeBinding annotationBinding = this.resolvedType;
        if (annotationBinding == null) {
            return false;
        }
        long metaTagBits = annotationBinding.getAnnotationTagBits();

        if ((metaTagBits & (TagBits.AnnotationTargetMASK)) == 0) { // explicit target required for JSR308 style annotations.
            return false;
        }
        if ((metaTagBits & (TagBits.AnnotationForTypeParameter | TagBits.AnnotationForTypeUse)) == 0) {
            return false;
        }
        if ((metaTagBits & TagBits.AnnotationRetentionMASK) == 0)
            return false; // by default the retention is CLASS

        return (metaTagBits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationRuntimeRetention;
    }

    public boolean isRuntimeVisible() {
        final TypeBinding annotationBinding = this.resolvedType;
        if (annotationBinding == null) {
            return false;
        }
        long metaTagBits = annotationBinding.getAnnotationTagBits();
        // we need to filter out only "pure" type use and type parameter annotations, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=392119
        if ((metaTagBits & (TagBits.AnnotationForTypeParameter | TagBits.AnnotationForTypeUse)) != 0) {
            if ((metaTagBits & TagBits.SE7AnnotationTargetMASK) == 0) { // not a hybrid target.
                return false;
            }
        }
        if ((metaTagBits & TagBits.AnnotationRetentionMASK) == 0)
            return false; // by default the retention is CLASS

        return (metaTagBits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationRuntimeRetention;
    }

    public abstract MemberValuePair[] memberValuePairs();

    @Override
    public StringBuffer printExpression(int indent, StringBuffer output) {
        output.append('@');
        this.type.printExpression(0, output);
        return output;
    }

    public void recordSuppressWarnings(Scope scope, int startSuppresss, int endSuppress,
            boolean isSuppressingWarnings) {
        IrritantSet suppressWarningIrritants = null;
        MemberValuePair[] pairs = memberValuePairs();
        pairLoop: for (int i = 0, length = pairs.length; i < length; i++) {
            MemberValuePair pair = pairs[i];
            if (CharOperation.equals(pair.name, TypeConstants.VALUE)) {
                Expression value = pair.value;
                if (value instanceof ArrayInitializer) {
                    ArrayInitializer initializer = (ArrayInitializer) value;
                    Expression[] inits = initializer.expressions;
                    if (inits != null) {
                        for (int j = 0, initsLength = inits.length; j < initsLength; j++) {
                            Constant cst = inits[j].constant;
                            if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) {
                                IrritantSet irritants = CompilerOptions.warningTokenToIrritants(cst.stringValue());
                                if (irritants != null) {
                                    if (suppressWarningIrritants == null) {
                                        suppressWarningIrritants = new IrritantSet(irritants);
                                    } else if (suppressWarningIrritants.set(irritants) == null) {
                                        scope.problemReporter().unusedWarningToken(inits[j]);
                                    }
                                } else {
                                    scope.problemReporter().unhandledWarningToken(inits[j]);
                                }
                            }
                        }
                    }
                } else {
                    Constant cst = value.constant;
                    if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) {
                        IrritantSet irritants = CompilerOptions.warningTokenToIrritants(cst.stringValue());
                        if (irritants != null) {
                            suppressWarningIrritants = new IrritantSet(irritants);
                            // TODO: should check for unused warning token against enclosing annotation as well ?
                        } else {
                            scope.problemReporter().unhandledWarningToken(value);
                        }
                    }
                }
                break pairLoop;
            }
        }
        if (isSuppressingWarnings && suppressWarningIrritants != null) {
            scope.referenceCompilationUnit().recordSuppressWarnings(suppressWarningIrritants, this, startSuppresss,
                    endSuppress, scope.referenceContext());
        }
    }

    @Override
    public TypeBinding resolveType(BlockScope scope) {

        if (this.compilerAnnotation != null)
            return this.resolvedType;
        this.constant = Constant.NotAConstant;

        TypeBinding typeBinding;
        if (this.resolvedType == null) {
            typeBinding = this.type.resolveType(scope);
            if (typeBinding == null) {
                this.resolvedType = new ProblemReferenceBinding(this.type.getTypeName(), null,
                        ProblemReasons.NotFound);
                return null;
            }
            this.resolvedType = typeBinding;
        } else {
            typeBinding = this.resolvedType;
        }
        // ensure type refers to an annotation type
        if (!typeBinding.isAnnotationType() && typeBinding.isValidBinding()) {
            scope.problemReporter().notAnnotationType(typeBinding, this.type);
            return null;
        }

        ReferenceBinding annotationType = (ReferenceBinding) this.resolvedType;
        MethodBinding[] methods = annotationType.methods();
        // clone valuePairs to keep track of unused ones
        MemberValuePair[] originalValuePairs = memberValuePairs();
        MemberValuePair valueAttribute = null; // remember the first 'value' pair
        MemberValuePair[] pairs;
        int pairsLength = originalValuePairs.length;
        if (pairsLength > 0) {
            System.arraycopy(originalValuePairs, 0, pairs = new MemberValuePair[pairsLength], 0, pairsLength);
        } else {
            pairs = originalValuePairs;
        }

        nextMember: for (int i = 0, requiredLength = methods.length; i < requiredLength; i++) {
            MethodBinding method = methods[i];
            char[] selector = method.selector;
            boolean foundValue = false;
            nextPair: for (int j = 0; j < pairsLength; j++) {
                MemberValuePair pair = pairs[j];
                if (pair == null)
                    continue nextPair;
                char[] name = pair.name;
                if (CharOperation.equals(name, selector)) {
                    if (valueAttribute == null && CharOperation.equals(name, TypeConstants.VALUE)) {
                        valueAttribute = pair;
                    }
                    pair.binding = method;
                    pair.resolveTypeExpecting(scope, method.returnType);
                    pairs[j] = null; // consumed
                    foundValue = true;

                    // check duplicates
                    boolean foundDuplicate = false;
                    for (int k = j + 1; k < pairsLength; k++) {
                        MemberValuePair otherPair = pairs[k];
                        if (otherPair == null)
                            continue;
                        if (CharOperation.equals(otherPair.name, selector)) {
                            foundDuplicate = true;
                            scope.problemReporter().duplicateAnnotationValue(annotationType, otherPair);
                            otherPair.binding = method;
                            otherPair.resolveTypeExpecting(scope, method.returnType);
                            pairs[k] = null;
                        }
                    }
                    if (foundDuplicate) {
                        scope.problemReporter().duplicateAnnotationValue(annotationType, pair);
                        continue nextMember;
                    }
                }
            }
            if (!foundValue && (method.modifiers & ClassFileConstants.AccAnnotationDefault) == 0
                    && (this.bits & IsRecovered) == 0 && annotationType.isValidBinding()) {
                scope.problemReporter().missingValueForAnnotationMember(this, selector);
            }
        }
        // check unused pairs
        for (int i = 0; i < pairsLength; i++) {
            if (pairs[i] != null) {
                if (annotationType.isValidBinding()) {
                    scope.problemReporter().undefinedAnnotationValue(annotationType, pairs[i]);
                }
                pairs[i].resolveTypeExpecting(scope, null); // resilient
            }
        }
        this.compilerAnnotation = scope.environment().createAnnotation((ReferenceBinding) this.resolvedType,
                computeElementValuePairs());
        // recognize standard annotations ?
        long tagBits = detectStandardAnnotation(scope, annotationType, valueAttribute);
        int defaultNullness = (int) (tagBits & Binding.NullnessDefaultMASK);
        tagBits &= ~Binding.NullnessDefaultMASK;
        CompilerOptions compilerOptions = scope.compilerOptions();
        if ((tagBits & TagBits.AnnotationDeprecated) != 0
                && compilerOptions.complianceLevel >= ClassFileConstants.JDK9
                && !compilerOptions.storeAnnotations) {
            this.recipient.setAnnotations(new AnnotationBinding[] { this.compilerAnnotation }, true); // force storing enhanced deprecation
        }

        // record annotation positions in the compilation result
        scope.referenceCompilationUnit().recordSuppressWarnings(IrritantSet.NLS, null, this.sourceStart,
                this.declarationSourceEnd, scope.referenceContext());
        if (this.recipient != null) {
            int kind = this.recipient.kind();
            if (tagBits != 0 || defaultNullness != 0) {
                // tag bits onto recipient
                switch (kind) {
                case Binding.MODULE:
                    SourceModuleBinding module = (SourceModuleBinding) this.recipient;
                    module.tagBits |= tagBits;
                    if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
                        ModuleDeclaration moduleDeclaration = module.scope.referenceContext.moduleDeclaration;
                        recordSuppressWarnings(scope, 0, moduleDeclaration.declarationSourceEnd,
                                compilerOptions.suppressWarnings);
                    }
                    module.defaultNullness |= defaultNullness;
                    break;
                case Binding.PACKAGE:
                    ((PackageBinding) this.recipient).tagBits |= tagBits;
                    break;
                case Binding.TYPE:
                case Binding.GENERIC_TYPE:
                    SourceTypeBinding sourceType = (SourceTypeBinding) this.recipient;
                    if ((tagBits & TagBits.AnnotationRepeatable) == 0 || sourceType.isAnnotationType()) // don't set AnnotationRepeatable on non-annotation types.
                        sourceType.tagBits |= tagBits;
                    if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
                        TypeDeclaration typeDeclaration = sourceType.scope.referenceContext;
                        int start;
                        if (scope.referenceCompilationUnit().types[0] == typeDeclaration) {
                            start = 0;
                        } else {
                            start = typeDeclaration.declarationSourceStart;
                        }
                        recordSuppressWarnings(scope, start, typeDeclaration.declarationSourceEnd,
                                compilerOptions.suppressWarnings);
                    }
                    sourceType.defaultNullness |= defaultNullness;
                    break;
                case Binding.METHOD:
                    MethodBinding sourceMethod = (MethodBinding) this.recipient;
                    sourceMethod.tagBits |= tagBits;
                    if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
                        sourceType = (SourceTypeBinding) sourceMethod.declaringClass;
                        AbstractMethodDeclaration methodDeclaration = sourceType.scope.referenceContext
                                .declarationOf(sourceMethod);
                        recordSuppressWarnings(scope, methodDeclaration.declarationSourceStart,
                                methodDeclaration.declarationSourceEnd, compilerOptions.suppressWarnings);
                    }
                    long nullBits = sourceMethod.tagBits & TagBits.AnnotationNullMASK;
                    if (nullBits == TagBits.AnnotationNullMASK) {
                        scope.problemReporter().contradictoryNullAnnotations(this);
                        sourceMethod.tagBits &= ~TagBits.AnnotationNullMASK; // avoid secondary problems
                    }
                    if (nullBits != 0 && sourceMethod.isConstructor()) {
                        if (compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8)
                            scope.problemReporter().nullAnnotationUnsupportedLocation(this);
                        // for declaration annotations the inapplicability will be reported below
                        sourceMethod.tagBits &= ~TagBits.AnnotationNullMASK;
                    }
                    sourceMethod.defaultNullness |= defaultNullness;
                    break;
                case Binding.FIELD:
                    FieldBinding sourceField = (FieldBinding) this.recipient;
                    sourceField.tagBits |= tagBits;
                    if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
                        sourceType = (SourceTypeBinding) sourceField.declaringClass;
                        FieldDeclaration fieldDeclaration = sourceType.scope.referenceContext
                                .declarationOf(sourceField);
                        recordSuppressWarnings(scope, fieldDeclaration.declarationSourceStart,
                                fieldDeclaration.declarationSourceEnd, compilerOptions.suppressWarnings);
                    }
                    if (defaultNullness != 0) {
                        sourceType = (SourceTypeBinding) sourceField.declaringClass;
                        FieldDeclaration fieldDeclaration = sourceType.scope.referenceContext
                                .declarationOf(sourceField);
                        // test merged value of defaultNullness contributed by this annotation and previous annotations on same target is redundant w.r.t. containing value
                        // (for targets other than fields the resulting value is tested only once after processing all annotations, but this is hard to do for fields)
                        Binding target = scope.parent.checkRedundantDefaultNullness(
                                defaultNullness | scope.localNonNullByDefaultValue(fieldDeclaration.sourceStart),
                                fieldDeclaration.sourceStart);
                        scope.recordNonNullByDefault(fieldDeclaration.binding, defaultNullness, this,
                                fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd);
                        if (target != null) {
                            scope.problemReporter().nullDefaultAnnotationIsRedundant(fieldDeclaration,
                                    new Annotation[] { this }, target);
                        }
                    }
                    // fields don't yet have their type resolved, in 1.8 null annotations
                    // will be transfered from the field to its type during STB.resolveTypeFor().
                    if ((sourceField.tagBits & TagBits.AnnotationNullMASK) == TagBits.AnnotationNullMASK) {
                        scope.problemReporter().contradictoryNullAnnotations(this);
                        sourceField.tagBits &= ~TagBits.AnnotationNullMASK; // avoid secondary problems
                    }
                    break;
                case Binding.LOCAL:
                    LocalVariableBinding variable = (LocalVariableBinding) this.recipient;
                    variable.tagBits |= tagBits;
                    if ((variable.tagBits & TagBits.AnnotationNullMASK) == TagBits.AnnotationNullMASK) {
                        scope.problemReporter().contradictoryNullAnnotations(this);
                        variable.tagBits &= ~TagBits.AnnotationNullMASK; // avoid secondary problems
                    }
                    if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
                        LocalDeclaration localDeclaration = variable.declaration;
                        recordSuppressWarnings(scope, localDeclaration.declarationSourceStart,
                                localDeclaration.declarationSourceEnd, compilerOptions.suppressWarnings);
                    }
                    // note: defaultNullness for local declarations has been already been handled earlier by handleNonNullByDefault() 
                    break;
                }
            }
            if (kind == Binding.TYPE) {
                SourceTypeBinding sourceType = (SourceTypeBinding) this.recipient;
                if (CharOperation.equals(sourceType.sourceName, TypeConstants.PACKAGE_INFO_NAME))
                    kind = Binding.PACKAGE;
            }
            checkAnnotationTarget(this, scope, annotationType, kind, this.recipient,
                    tagBits & TagBits.AnnotationNullMASK);
        }
        return this.resolvedType;
    }

    public long handleNonNullByDefault(BlockScope scope) {
        TypeBinding typeBinding = this.resolvedType;
        if (typeBinding == null) {
            typeBinding = this.type.resolveType(scope);
            if (typeBinding == null) {
                return 0;
            }
            this.resolvedType = typeBinding;
        }
        if (!typeBinding.isAnnotationType()) {
            return 0;
        }

        ReferenceBinding annotationType = (ReferenceBinding) typeBinding;

        if (!annotationType.hasNullBit(TypeIds.BitNonNullByDefaultAnnotation)) {
            return 0;
        }

        MethodBinding[] methods = annotationType.methods();
        // clone valuePairs to keep track of unused ones
        MemberValuePair[] pairs = memberValuePairs();
        MemberValuePair valueAttribute = null; // remember the first 'value' pair
        int pairsLength = pairs.length;

        for (int i = 0, requiredLength = methods.length; i < requiredLength; i++) {
            MethodBinding method = methods[i];
            char[] selector = method.selector;
            nextPair: for (int j = 0; j < pairsLength; j++) {
                MemberValuePair pair = pairs[j];
                if (pair == null)
                    continue nextPair;
                char[] name = pair.name;
                if (CharOperation.equals(name, selector)) {
                    if (valueAttribute == null && CharOperation.equals(name, TypeConstants.VALUE)) {
                        valueAttribute = pair;
                        pair.binding = method;
                        pair.resolveTypeExpecting(scope, method.returnType);
                    }
                }
            }
        }
        // recognize standard annotations ?
        long tagBits = determineNonNullByDefaultTagBits(annotationType, valueAttribute);
        return (int) (tagBits & Binding.NullnessDefaultMASK);
    }

    public enum AnnotationTargetAllowed {
        YES, TYPE_ANNOTATION_ON_QUALIFIED_NAME, NO;
    }

    private static AnnotationTargetAllowed isAnnotationTargetAllowed(Binding recipient, BlockScope scope,
            TypeBinding annotationType, int kind, long metaTagBits) {
        switch (kind) {
        case Binding.PACKAGE:
            if ((metaTagBits & TagBits.AnnotationForPackage) != 0)
                return AnnotationTargetAllowed.YES;
            else if (scope.compilerOptions().sourceLevel <= ClassFileConstants.JDK1_6) {
                SourceTypeBinding sourceType = (SourceTypeBinding) recipient;
                if (CharOperation.equals(sourceType.sourceName, TypeConstants.PACKAGE_INFO_NAME))
                    return AnnotationTargetAllowed.YES;
            }
            break;
        case Binding.TYPE_USE:
            if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) {
                // jsr 308
                return AnnotationTargetAllowed.YES;
            }
            if (scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8) {
                // already reported as syntax error; don't report secondary problems
                return AnnotationTargetAllowed.YES;
            }
            break;
        case Binding.TYPE:
        case Binding.GENERIC_TYPE:
            if (((ReferenceBinding) recipient).isAnnotationType()) {
                if ((metaTagBits & (TagBits.AnnotationForAnnotationType | TagBits.AnnotationForType
                        | TagBits.AnnotationForTypeUse)) != 0)
                    return AnnotationTargetAllowed.YES;
            } else if ((metaTagBits & (TagBits.AnnotationForType | TagBits.AnnotationForTypeUse)) != 0) {
                return AnnotationTargetAllowed.YES;
            } else if ((metaTagBits & TagBits.AnnotationForPackage) != 0) {
                if (CharOperation.equals(((ReferenceBinding) recipient).sourceName,
                        TypeConstants.PACKAGE_INFO_NAME))
                    return AnnotationTargetAllowed.YES;
            }
            break;
        case Binding.METHOD:
            MethodBinding methodBinding = (MethodBinding) recipient;
            if (methodBinding.isConstructor()) {
                if ((metaTagBits & (TagBits.AnnotationForConstructor | TagBits.AnnotationForTypeUse)) != 0)
                    return AnnotationTargetAllowed.YES;
            } else if ((metaTagBits & TagBits.AnnotationForMethod) != 0) {
                return AnnotationTargetAllowed.YES;
            } else if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) {
                SourceTypeBinding sourceType = (SourceTypeBinding) methodBinding.declaringClass;
                MethodDeclaration methodDecl = (MethodDeclaration) sourceType.scope.referenceContext
                        .declarationOf(methodBinding);
                if (isTypeUseCompatible(methodDecl.returnType, scope)) {
                    return AnnotationTargetAllowed.YES;
                } else {
                    return AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME;
                }
            }
            break;
        case Binding.FIELD:
            if ((metaTagBits & TagBits.AnnotationForField) != 0) {
                return AnnotationTargetAllowed.YES;
            } else if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) {
                FieldBinding sourceField = (FieldBinding) recipient;
                SourceTypeBinding sourceType = (SourceTypeBinding) sourceField.declaringClass;
                FieldDeclaration fieldDeclaration = sourceType.scope.referenceContext.declarationOf(sourceField);
                if (isTypeUseCompatible(fieldDeclaration.type, scope)) {
                    return AnnotationTargetAllowed.YES;
                } else {
                    return AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME;
                }
            }
            break;
        case Binding.LOCAL:
            LocalVariableBinding localVariableBinding = (LocalVariableBinding) recipient;
            if ((localVariableBinding.tagBits & TagBits.IsArgument) != 0) {
                if ((metaTagBits & TagBits.AnnotationForParameter) != 0) {
                    return AnnotationTargetAllowed.YES;
                } else if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) {
                    if (isTypeUseCompatible(localVariableBinding.declaration.type, scope)) {
                        return AnnotationTargetAllowed.YES;
                    } else {
                        return AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME;
                    }
                }
            } else if ((annotationType.tagBits & TagBits.AnnotationForLocalVariable) != 0) {
                return AnnotationTargetAllowed.YES;
            } else if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) {
                if (localVariableBinding.declaration.isTypeNameVar(scope)) {
                    return AnnotationTargetAllowed.NO;
                } else if (isTypeUseCompatible(localVariableBinding.declaration.type, scope)) {
                    return AnnotationTargetAllowed.YES;
                } else {
                    return AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME;
                }
            }
            break;
        case Binding.TYPE_PARAMETER: // jsr308
            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=391196
            if ((metaTagBits & (TagBits.AnnotationForTypeParameter | TagBits.AnnotationForTypeUse)) != 0) {
                return AnnotationTargetAllowed.YES;
            }
            break;
        case Binding.MODULE:
            if ((metaTagBits & (TagBits.AnnotationForModule)) != 0) {
                return AnnotationTargetAllowed.YES;
            }
            break;
        }
        return AnnotationTargetAllowed.NO;
    }

    public static boolean isAnnotationTargetAllowed(BlockScope scope, TypeBinding annotationType,
            Binding recipient) {
        long metaTagBits = annotationType.getAnnotationTagBits(); // could be forward reference
        if ((metaTagBits & TagBits.AnnotationTargetMASK) == 0) {
            return true;
        }
        return isAnnotationTargetAllowed(recipient, scope, annotationType, recipient.kind(),
                metaTagBits) == AnnotationTargetAllowed.YES;
    }

    static AnnotationTargetAllowed isAnnotationTargetAllowed(Annotation annotation, BlockScope scope,
            TypeBinding annotationType, int kind) {

        long metaTagBits = annotationType.getAnnotationTagBits(); // could be forward reference
        if ((metaTagBits & TagBits.AnnotationTargetMASK) == 0) {
            // does not specify any target restriction - all locations supported in Java 7 and before are possible
            // TBD - revisit for modules - as per 9.6.4.1, annotation without target is applicable for module declaration
            // which is listed as a declaration context, but javac does not allow this
            if (kind == Binding.TYPE_PARAMETER || kind == Binding.TYPE_USE) {
                scope.problemReporter().explitAnnotationTargetRequired(annotation);
            }
            return AnnotationTargetAllowed.YES;
        }

        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=391201
        if ((metaTagBits & TagBits.SE7AnnotationTargetMASK) == 0
                && (metaTagBits & (TagBits.AnnotationForTypeUse | TagBits.AnnotationForTypeParameter)) != 0) {
            if (scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8) {
                switch (kind) {
                case Binding.PACKAGE:
                case Binding.TYPE:
                case Binding.GENERIC_TYPE:
                case Binding.METHOD:
                case Binding.FIELD:
                case Binding.LOCAL:
                    scope.problemReporter().invalidUsageOfTypeAnnotations(annotation);
                }
            }
        }
        return isAnnotationTargetAllowed(annotation.recipient, scope, annotationType, kind, metaTagBits);
    }

    static void checkAnnotationTarget(Annotation annotation, BlockScope scope, ReferenceBinding annotationType,
            int kind, Binding recipient, long tagBitsToRevert) {
        // check (meta)target compatibility
        if (!annotationType.isValidBinding()) {
            // no need to check annotation usage if missing
            return;
        }
        AnnotationTargetAllowed annotationTargetAllowed = isAnnotationTargetAllowed(annotation, scope,
                annotationType, kind);
        if (annotationTargetAllowed != AnnotationTargetAllowed.YES) {
            if (annotationTargetAllowed == AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME) {
                scope.problemReporter().typeAnnotationAtQualifiedName(annotation);
            } else {
                scope.problemReporter().disallowedTargetForAnnotation(annotation);
            }
            if (recipient instanceof TypeBinding)
                ((TypeBinding) recipient).tagBits &= ~tagBitsToRevert;
        }
    }

    /**
     * Check to see if a repeating annotation is in fact of a container annotation type for an annotation which is also present at the same target.
     * @param scope The scope (for error reporting)
     * @param repeatedAnnotationType Type of annotation which has been repeated (to check for possibly being a container for a repeatable annotation)
     * @param sourceAnnotations The annotations to check
     */
    public static void checkForInstancesOfRepeatableWithRepeatingContainerAnnotation(BlockScope scope,
            ReferenceBinding repeatedAnnotationType, Annotation[] sourceAnnotations) {
        // Fail fast if the repeating annotation type can't be a container, anyway
        MethodBinding[] valueMethods = repeatedAnnotationType.getMethods(TypeConstants.VALUE);
        if (valueMethods.length != 1)
            return; // No violations possible

        TypeBinding methodReturnType = valueMethods[0].returnType;
        // value must be an array 
        if (!methodReturnType.isArrayType() || methodReturnType.dimensions() != 1)
            return;

        ArrayBinding array = (ArrayBinding) methodReturnType;
        TypeBinding elementsType = array.elementsType();
        if (!elementsType.isRepeatableAnnotationType())
            return; // Can't be a problem, then

        for (int i = 0; i < sourceAnnotations.length; ++i) {
            Annotation annotation = sourceAnnotations[i];
            if (TypeBinding.equalsEquals(elementsType, annotation.resolvedType)) {
                scope.problemReporter().repeatableAnnotationWithRepeatingContainer(annotation,
                        repeatedAnnotationType);
                return; // One is enough for this annotation type
            }
        }
    }

    // Check and answer if an attempt to annotate a package is being made. Error should be reported by caller.
    public static boolean isTypeUseCompatible(TypeReference reference, Scope scope) {
        if (reference != null && !(reference instanceof SingleTypeReference)) {
            Binding binding = scope.getPackage(reference.getTypeName());
            // In case of ProblemReferenceBinding, don't report additional error
            if (binding instanceof PackageBinding) {
                return false;
            }
        }
        return true;
    }

    // Complain if an attempt to annotate the enclosing type of a static member type is being made.
    public static void isTypeUseCompatible(TypeReference reference, Scope scope, Annotation[] annotations) {
        if (annotations == null || reference == null || reference.getAnnotatableLevels() == 1)
            return;
        if (scope.environment().globalOptions.sourceLevel < ClassFileConstants.JDK1_8)
            return;

        TypeBinding resolvedType = reference.resolvedType == null ? null
                : reference.resolvedType.leafComponentType();
        if (resolvedType == null || !resolvedType.isNestedType())
            return;

        nextAnnotation: for (int i = 0, annotationsLength = annotations.length; i < annotationsLength; i++) {
            Annotation annotation = annotations[i];
            long metaTagBits = annotation.resolvedType.getAnnotationTagBits();
            if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0
                    && (metaTagBits & TagBits.SE7AnnotationTargetMASK) == 0) {
                ReferenceBinding currentType = (ReferenceBinding) resolvedType;
                while (currentType.isNestedType()) {
                    if (currentType.isStatic()) {
                        QualifiedTypeReference.rejectAnnotationsOnStaticMemberQualififer(scope, currentType,
                                new Annotation[] { annotation });
                        continue nextAnnotation;
                    } else {
                        if (annotation.hasNullBit(TypeIds.BitNonNullAnnotation | TypeIds.BitNullableAnnotation)) {
                            scope.problemReporter().nullAnnotationAtQualifyingType(annotation);
                            continue nextAnnotation;
                        }
                    }
                    currentType = currentType.enclosingType();
                }
            }
        }
    }

    public boolean hasNullBit(int bit) {
        return this.resolvedType instanceof ReferenceBinding
                && ((ReferenceBinding) this.resolvedType).hasNullBit(bit);
    }

    @Override
    public abstract void traverse(ASTVisitor visitor, BlockScope scope);

    @Override
    public abstract void traverse(ASTVisitor visitor, ClassScope scope);

    public Annotation getPersistibleAnnotation() {
        return this.persistibleAnnotation; // will be this for non-repeating annotation, the container for the first of the repeating ones and null for the followers.
    }

    public void setPersistibleAnnotation(ContainerAnnotation container) {
        this.persistibleAnnotation = container; // will be a legitimate container for the first of the repeating ones and null for the followers.
    }
}