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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2000, 2019 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  - Contribution for bug 295551
 *     Jesper S Moller   - Contributions for
 *                       Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335
 *     Frits Jalvingh    - contributions for bug 533830.
 *     Red Hat Inc.        - add module-info Javadoc support
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.Arrays;
import java.util.Comparator;

import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
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.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.ImportBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.parser.NLSTag;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
import org.eclipse.jdt.internal.compiler.problem.AbortMethod;
import org.eclipse.jdt.internal.compiler.problem.AbortType;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.HashSetOfInt;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class CompilationUnitDeclaration extends ASTNode implements ProblemSeverities, ReferenceContext {

    private static final Comparator STRING_LITERAL_COMPARATOR = new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            StringLiteral literal1 = (StringLiteral) o1;
            StringLiteral literal2 = (StringLiteral) o2;
            return literal1.sourceStart - literal2.sourceStart;
        }
    };
    private static final int STRING_LITERALS_INCREMENT = 10;

    public ImportReference currentPackage;
    public ImportReference[] imports;
    public TypeDeclaration[] types;
    public ModuleDeclaration moduleDeclaration;
    public int[][] comments;

    public boolean ignoreFurtherInvestigation = false; // once pointless to investigate due to errors
    public boolean ignoreMethodBodies = false;
    public CompilationUnitScope scope;
    public ProblemReporter problemReporter;
    public CompilationResult compilationResult;

    public LocalTypeBinding[] localTypes;
    public int localTypeCount = 0;

    public boolean isPropagatingInnerClassEmulation;

    public Javadoc javadoc; // 1.5 addition for package-info.java

    public NLSTag[] nlsTags;
    private StringLiteral[] stringLiterals;
    private int stringLiteralsPtr;
    private HashSetOfInt stringLiteralsStart;

    public boolean[] validIdentityComparisonLines;

    IrritantSet[] suppressWarningIrritants; // irritant for suppressed warnings
    Annotation[] suppressWarningAnnotations;
    long[] suppressWarningScopePositions; // (start << 32) + end
    int suppressWarningsCount;
    public int functionalExpressionsCount;
    public FunctionalExpression[] functionalExpressions;

    public CompilationUnitDeclaration(ProblemReporter problemReporter, CompilationResult compilationResult,
            int sourceLength) {
        this.problemReporter = problemReporter;
        this.compilationResult = compilationResult;
        //by definition of a compilation unit....
        this.sourceStart = 0;
        this.sourceEnd = sourceLength - 1;
    }

    /*
     *   We cause the compilation task to abort to a given extent.
     */
    @Override
    public void abort(int abortLevel, CategorizedProblem problem) {
        switch (abortLevel) {
        case AbortType:
            throw new AbortType(this.compilationResult, problem);
        case AbortMethod:
            throw new AbortMethod(this.compilationResult, problem);
        default:
            throw new AbortCompilationUnit(this.compilationResult, problem);
        }
    }

    /*
     * Dispatch code analysis AND request saturation of inner emulation
     */
    public void analyseCode() {
        if (this.ignoreFurtherInvestigation)
            return;
        try {
            if (this.types != null) {
                for (int i = 0, count = this.types.length; i < count; i++) {
                    this.types[i].analyseCode(this.scope);
                }
            }
            if (this.moduleDeclaration != null) {
                this.moduleDeclaration.analyseCode(this.scope);
            }
            // request inner emulation propagation
            propagateInnerEmulationForAllLocalTypes();
        } catch (AbortCompilationUnit e) {
            this.ignoreFurtherInvestigation = true;
            return;
        }
    }

    /*
     * When unit result is about to be accepted, removed back pointers
     * to compiler structures.
     */
    public void cleanUp() {
        if (this.types != null) {
            for (int i = 0, max = this.types.length; i < max; i++) {
                cleanUp(this.types[i]);
            }
            for (int i = 0, max = this.localTypeCount; i < max; i++) {
                LocalTypeBinding localType = this.localTypes[i];
                // null out the type's scope backpointers
                localType.cleanUp(); // local members are already in the list
                localType.enclosingCase = null;
            }
        }
        if (this.functionalExpressionsCount > 0) {
            for (int i = 0, max = this.functionalExpressionsCount; i < max; i++) {
                this.functionalExpressions[i].cleanUp();
            }
        }

        this.compilationResult.recoveryScannerData = null; // recovery is already done

        ClassFile[] classFiles = this.compilationResult.getClassFiles();
        for (int i = 0, max = classFiles.length; i < max; i++) {
            // clear the classFile back pointer to the bindings
            ClassFile classFile = classFiles[i];
            // null out the classfile backpointer to a type binding
            classFile.referenceBinding = null;
            classFile.innerClassesBindings = null;
            classFile.bootstrapMethods = null;
            classFile.missingTypes = null;
            classFile.visitedTypes = null;
        }

        this.suppressWarningAnnotations = null;

        if (this.scope != null)
            this.scope.cleanUpInferenceContexts();
    }

    private void cleanUp(TypeDeclaration type) {
        if (type.memberTypes != null) {
            for (int i = 0, max = type.memberTypes.length; i < max; i++) {
                cleanUp(type.memberTypes[i]);
            }
        }
        if (type.binding != null && type.binding.isAnnotationType())
            this.compilationResult.hasAnnotations = true;
        if (type.binding != null) {
            // null out the type's scope backpointers
            type.binding.cleanUp();
        }
    }

    public void checkUnusedImports() {
        if (this.scope.imports != null) {
            for (int i = 0, max = this.scope.imports.length; i < max; i++) {
                ImportBinding importBinding = this.scope.imports[i];
                ImportReference importReference = importBinding.reference;
                if (importReference != null && ((importReference.bits & ASTNode.Used) == 0)) {
                    this.scope.problemReporter().unusedImport(importReference);
                }
            }
        }
    }

    @Override
    public CompilationResult compilationResult() {
        return this.compilationResult;
    }

    public void createPackageInfoType() {
        TypeDeclaration declaration = new TypeDeclaration(this.compilationResult);
        declaration.name = TypeConstants.PACKAGE_INFO_NAME;
        declaration.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccInterface;
        declaration.javadoc = this.javadoc;
        this.types[0] = declaration; // Assumes the first slot is meant for this type
    }

    /*
     * Finds the matching type amoung this compilation unit types.
     * Returns null if no type with this name is found.
     * The type name is a compound name
     * e.g. if we're looking for X.A.B then a type name would be {X, A, B}
     */
    public TypeDeclaration declarationOfType(char[][] typeName) {
        for (int i = 0; i < this.types.length; i++) {
            TypeDeclaration typeDecl = this.types[i].declarationOfType(typeName);
            if (typeDecl != null) {
                return typeDecl;
            }
        }
        return null;
    }

    public void finalizeProblems() {
        int problemCount = this.compilationResult.problemCount;
        CategorizedProblem[] problems = this.compilationResult.problems;
        if (this.suppressWarningsCount == 0) {
            return;
        }
        int removed = 0;
        IrritantSet[] foundIrritants = new IrritantSet[this.suppressWarningsCount];
        CompilerOptions options = this.scope.compilerOptions();
        boolean hasMandatoryErrors = false;
        nextProblem: for (int iProblem = 0, length = problemCount; iProblem < length; iProblem++) {
            CategorizedProblem problem = problems[iProblem];
            int problemID = problem.getID();
            int irritant = ProblemReporter.getIrritant(problemID);
            boolean isError = problem.isError();
            if (isError) {
                if (irritant == 0) {
                    // tolerate unused warning tokens when mandatory errors
                    hasMandatoryErrors = true;
                    continue;
                }
                if (!options.suppressOptionalErrors) {
                    continue;
                }
            }
            int start = problem.getSourceStart();
            int end = problem.getSourceEnd();
            nextSuppress: for (int iSuppress = 0, suppressCount = this.suppressWarningsCount; iSuppress < suppressCount; iSuppress++) {
                long position = this.suppressWarningScopePositions[iSuppress];
                int startSuppress = (int) (position >>> 32);
                int endSuppress = (int) position;
                if (start < startSuppress)
                    continue nextSuppress;
                if (end > endSuppress)
                    continue nextSuppress;
                if (!this.suppressWarningIrritants[iSuppress].isSet(irritant)) {
                    if (problem instanceof DefaultProblem) {
                        ((DefaultProblem) problem).reportError();
                    }
                    continue nextSuppress;
                }
                // discard suppressed warning
                removed++;
                problems[iProblem] = null;
                this.compilationResult.removeProblem(problem);
                if (foundIrritants[iSuppress] == null) {
                    foundIrritants[iSuppress] = new IrritantSet(irritant);
                } else {
                    foundIrritants[iSuppress].set(irritant);
                }
                continue nextProblem;
            }
        }
        // compact remaining problems
        if (removed > 0) {
            for (int i = 0, index = 0; i < problemCount; i++) {
                CategorizedProblem problem;
                if ((problem = problems[i]) != null) {
                    if (i > index) {
                        problems[index++] = problem;
                    } else {
                        index++;
                    }
                }
            }
        }
        // flag SuppressWarnings which had no effect (only if no (mandatory) error got detected within unit
        if (!hasMandatoryErrors) {
            int severity = options.getSeverity(CompilerOptions.UnusedWarningToken);
            if (severity != ProblemSeverities.Ignore) {
                boolean unusedWarningTokenIsWarning = (severity & ProblemSeverities.Error) == 0;
                for (int iSuppress = 0, suppressCount = this.suppressWarningsCount; iSuppress < suppressCount; iSuppress++) {
                    Annotation annotation = this.suppressWarningAnnotations[iSuppress];
                    if (annotation == null)
                        continue; // implicit annotation
                    IrritantSet irritants = this.suppressWarningIrritants[iSuppress];
                    if (unusedWarningTokenIsWarning && irritants.areAllSet())
                        continue; // @SuppressWarnings("all") also suppresses unused warning token
                    if (irritants != foundIrritants[iSuppress]) { // mismatch, some warning tokens were unused
                        MemberValuePair[] pairs = annotation.memberValuePairs();
                        pairLoop: for (int iPair = 0, pairCount = pairs.length; iPair < pairCount; iPair++) {
                            MemberValuePair pair = pairs[iPair];
                            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 iToken = 0, tokenCount = inits.length; iToken < tokenCount; iToken++) {
                                            Constant cst = inits[iToken].constant;
                                            if (cst != Constant.NotAConstant
                                                    && cst.typeID() == TypeIds.T_JavaLangString) {
                                                IrritantSet tokenIrritants = CompilerOptions
                                                        .warningTokenToIrritants(cst.stringValue());
                                                if (tokenIrritants != null) {
                                                    if (!tokenIrritants.areAllSet() // no complaint against @SuppressWarnings("all")
                                                            && (foundIrritants[iSuppress] == null
                                                                    || !foundIrritants[iSuppress]
                                                                            .isAnySet(tokenIrritants))) { // if irritant had no matching problem
                                                        if (unusedWarningTokenIsWarning) {
                                                            int start = value.sourceStart, end = value.sourceEnd;
                                                            nextSuppress: for (int jSuppress = iSuppress
                                                                    - 1; jSuppress >= 0; jSuppress--) {
                                                                long position = this.suppressWarningScopePositions[jSuppress];
                                                                int startSuppress = (int) (position >>> 32);
                                                                int endSuppress = (int) position;
                                                                if (start < startSuppress)
                                                                    continue nextSuppress;
                                                                if (end > endSuppress)
                                                                    continue nextSuppress;
                                                                if (this.suppressWarningIrritants[jSuppress]
                                                                        .areAllSet())
                                                                    break pairLoop; // suppress all?
                                                            }
                                                        }
                                                        int id = options.getIgnoredIrritant(tokenIrritants);
                                                        if (id > 0) {
                                                            String key = CompilerOptions.optionKeyFromIrritant(id);
                                                            this.scope.problemReporter()
                                                                    .problemNotAnalysed(inits[iToken], key);
                                                        } else {
                                                            this.scope.problemReporter()
                                                                    .unusedWarningToken(inits[iToken]);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    Constant cst = value.constant;
                                    if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) {
                                        IrritantSet tokenIrritants = CompilerOptions
                                                .warningTokenToIrritants(cst.stringValue());
                                        if (tokenIrritants != null) {
                                            if (!tokenIrritants.areAllSet() // no complaint against @SuppressWarnings("all")
                                                    && (foundIrritants[iSuppress] == null
                                                            || !foundIrritants[iSuppress]
                                                                    .isAnySet(tokenIrritants))) { // if irritant had no matching problem
                                                if (unusedWarningTokenIsWarning) {
                                                    int start = value.sourceStart, end = value.sourceEnd;
                                                    nextSuppress: for (int jSuppress = iSuppress
                                                            - 1; jSuppress >= 0; jSuppress--) {
                                                        long position = this.suppressWarningScopePositions[jSuppress];
                                                        int startSuppress = (int) (position >>> 32);
                                                        int endSuppress = (int) position;
                                                        if (start < startSuppress)
                                                            continue nextSuppress;
                                                        if (end > endSuppress)
                                                            continue nextSuppress;
                                                        if (this.suppressWarningIrritants[jSuppress].areAllSet())
                                                            break pairLoop; // suppress all?
                                                    }
                                                }
                                                int id = options.getIgnoredIrritant(tokenIrritants);
                                                if (id > 0) {
                                                    String key = CompilerOptions.optionKeyFromIrritant(id);
                                                    this.scope.problemReporter().problemNotAnalysed(value, key);
                                                } else {
                                                    this.scope.problemReporter().unusedWarningToken(value);
                                                }
                                            }
                                        }
                                    }
                                }
                                break pairLoop;
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Bytecode generation
     */
    public void generateCode() {
        if (this.ignoreFurtherInvestigation) {
            if (this.types != null) {
                for (int i = 0, count = this.types.length; i < count; i++) {
                    this.types[i].ignoreFurtherInvestigation = true;
                    // propagate the flag to request problem type creation
                    this.types[i].generateCode(this.scope);
                }
            }
            return;
        }
        try {
            if (this.types != null) {
                for (int i = 0, count = this.types.length; i < count; i++)
                    this.types[i].generateCode(this.scope);
            }
            if (this.moduleDeclaration != null) {
                this.moduleDeclaration.generateCode();
            }
        } catch (AbortCompilationUnit e) {
            // ignore
        }
    }

    @Override
    public CompilationUnitDeclaration getCompilationUnitDeclaration() {
        return this;
    }

    public char[] getFileName() {
        return this.compilationResult.getFileName();
    }

    public char[] getMainTypeName() {
        if (this.compilationResult.compilationUnit == null) {
            char[] fileName = this.compilationResult.getFileName();

            int start = CharOperation.lastIndexOf('/', fileName) + 1;
            if (start == 0 || start < CharOperation.lastIndexOf('\\', fileName))
                start = CharOperation.lastIndexOf('\\', fileName) + 1;

            int end = CharOperation.lastIndexOf('.', fileName);
            if (end == -1)
                end = fileName.length;

            return CharOperation.subarray(fileName, start, end);
        } else {
            return this.compilationResult.compilationUnit.getMainTypeName();
        }
    }

    public boolean isEmpty() {
        return (this.currentPackage == null) && (this.imports == null) && (this.types == null);
    }

    public boolean isPackageInfo() {
        return CharOperation.equals(getMainTypeName(), TypeConstants.PACKAGE_INFO_NAME);
    }

    public boolean isModuleInfo() {
        return CharOperation.equals(getMainTypeName(), TypeConstants.MODULE_INFO_NAME);
    }

    public boolean isSuppressed(CategorizedProblem problem) {
        if (this.suppressWarningsCount == 0)
            return false;
        int irritant = ProblemReporter.getIrritant(problem.getID());
        if (irritant == 0)
            return false;
        int start = problem.getSourceStart();
        int end = problem.getSourceEnd();
        nextSuppress: for (int iSuppress = 0, suppressCount = this.suppressWarningsCount; iSuppress < suppressCount; iSuppress++) {
            long position = this.suppressWarningScopePositions[iSuppress];
            int startSuppress = (int) (position >>> 32);
            int endSuppress = (int) position;
            if (start < startSuppress)
                continue nextSuppress;
            if (end > endSuppress)
                continue nextSuppress;
            if (this.suppressWarningIrritants[iSuppress].isSet(irritant))
                return true;
        }
        return false;
    }

    public boolean hasFunctionalTypes() {
        return this.compilationResult.hasFunctionalTypes;
    }

    @Override
    public boolean hasErrors() {
        return this.ignoreFurtherInvestigation;
    }

    @Override
    public StringBuffer print(int indent, StringBuffer output) {
        if (this.currentPackage != null) {
            printIndent(indent, output).append("package "); //$NON-NLS-1$
            this.currentPackage.print(0, output, false).append(";\n"); //$NON-NLS-1$
        }
        if (this.imports != null)
            for (int i = 0; i < this.imports.length; i++) {
                printIndent(indent, output).append("import "); //$NON-NLS-1$
                ImportReference currentImport = this.imports[i];
                if (currentImport.isStatic()) {
                    output.append("static "); //$NON-NLS-1$
                }
                currentImport.print(0, output).append(";\n"); //$NON-NLS-1$
            }
        if (this.moduleDeclaration != null) {
            this.moduleDeclaration.print(indent, output).append("\n"); //$NON-NLS-1$
        } else if (this.types != null) {
            for (int i = 0; i < this.types.length; i++) {
                this.types[i].print(indent, output).append("\n"); //$NON-NLS-1$
            }
        }
        return output;
    }

    /*
     * Force inner local types to update their innerclass emulation
     */
    public void propagateInnerEmulationForAllLocalTypes() {
        this.isPropagatingInnerClassEmulation = true;
        for (int i = 0, max = this.localTypeCount; i < max; i++) {
            LocalTypeBinding localType = this.localTypes[i];
            // only propagate for reachable local types
            if ((localType.scope.referenceType().bits & IsReachable) != 0) {
                localType.updateInnerEmulationDependents();
            }
        }
    }

    public void recordStringLiteral(StringLiteral literal, boolean fromRecovery) {
        if (this.stringLiteralsStart != null) {
            if (this.stringLiteralsStart.contains(literal.sourceStart))
                return;
            this.stringLiteralsStart.add(literal.sourceStart);
        } else if (fromRecovery) {
            this.stringLiteralsStart = new HashSetOfInt(this.stringLiteralsPtr + STRING_LITERALS_INCREMENT);
            for (int i = 0; i < this.stringLiteralsPtr; i++) {
                this.stringLiteralsStart.add(this.stringLiterals[i].sourceStart);
            }

            if (this.stringLiteralsStart.contains(literal.sourceStart))
                return;
            this.stringLiteralsStart.add(literal.sourceStart);
        }

        if (this.stringLiterals == null) {
            this.stringLiterals = new StringLiteral[STRING_LITERALS_INCREMENT];
            this.stringLiteralsPtr = 0;
        } else {
            int stackLength = this.stringLiterals.length;
            if (this.stringLiteralsPtr == stackLength) {
                System.arraycopy(this.stringLiterals, 0,
                        this.stringLiterals = new StringLiteral[stackLength + STRING_LITERALS_INCREMENT], 0,
                        stackLength);
            }
        }
        this.stringLiterals[this.stringLiteralsPtr++] = literal;
    }

    private boolean isLambdaExpressionCopyContext(ReferenceContext context) {
        if (context instanceof LambdaExpression && context != ((LambdaExpression) context).original())
            return true; // Do not record from copies. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=441929
        Scope cScope = context instanceof AbstractMethodDeclaration ? ((AbstractMethodDeclaration) context).scope
                : context instanceof TypeDeclaration ? ((TypeDeclaration) context).scope
                        : context instanceof LambdaExpression ? ((LambdaExpression) context).scope : null;
        return cScope != null ? isLambdaExpressionCopyContext(cScope.parent.referenceContext()) : false;
    }

    public void recordSuppressWarnings(IrritantSet irritants, Annotation annotation, int scopeStart, int scopeEnd,
            ReferenceContext context) {
        if (isLambdaExpressionCopyContext(context))
            return; // Do not record from copies. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=441929

        if (this.suppressWarningIrritants == null) {
            this.suppressWarningIrritants = new IrritantSet[3];
            this.suppressWarningAnnotations = new Annotation[3];
            this.suppressWarningScopePositions = new long[3];
        } else if (this.suppressWarningIrritants.length == this.suppressWarningsCount) {
            System.arraycopy(this.suppressWarningIrritants, 0,
                    this.suppressWarningIrritants = new IrritantSet[2 * this.suppressWarningsCount], 0,
                    this.suppressWarningsCount);
            System.arraycopy(this.suppressWarningAnnotations, 0,
                    this.suppressWarningAnnotations = new Annotation[2 * this.suppressWarningsCount], 0,
                    this.suppressWarningsCount);
            System.arraycopy(this.suppressWarningScopePositions, 0,
                    this.suppressWarningScopePositions = new long[2 * this.suppressWarningsCount], 0,
                    this.suppressWarningsCount);
        }
        final long scopePositions = ((long) scopeStart << 32) + scopeEnd;
        for (int i = 0, max = this.suppressWarningsCount; i < max; i++) {
            if (this.suppressWarningAnnotations[i] == annotation
                    && this.suppressWarningScopePositions[i] == scopePositions
                    && this.suppressWarningIrritants[i].hasSameIrritants(irritants)) {
                // annotation data already recorded
                return;
            }
        }
        this.suppressWarningIrritants[this.suppressWarningsCount] = irritants;
        this.suppressWarningAnnotations[this.suppressWarningsCount] = annotation;
        this.suppressWarningScopePositions[this.suppressWarningsCount++] = scopePositions;
    }

    /*
     * Keep track of all local types, so as to update their innerclass
     * emulation later on.
     */
    public void record(LocalTypeBinding localType) {
        if (this.localTypeCount == 0) {
            this.localTypes = new LocalTypeBinding[5];
        } else if (this.localTypeCount == this.localTypes.length) {
            System.arraycopy(this.localTypes, 0, (this.localTypes = new LocalTypeBinding[this.localTypeCount * 2]),
                    0, this.localTypeCount);
        }
        this.localTypes[this.localTypeCount++] = localType;
    }

    /*
     * Keep track of all lambda/method reference expressions, so as to be able to look it up later without
     * having to traverse AST. Return the "ordinal" returned by the enclosing type.
     */
    public int record(FunctionalExpression expression) {
        if (this.functionalExpressionsCount == 0) {
            this.functionalExpressions = new FunctionalExpression[5];
        } else if (this.functionalExpressionsCount == this.functionalExpressions.length) {
            System.arraycopy(this.functionalExpressions, 0,
                    (this.functionalExpressions = new FunctionalExpression[this.functionalExpressionsCount * 2]), 0,
                    this.functionalExpressionsCount);
        }
        this.functionalExpressions[this.functionalExpressionsCount++] = expression;
        return expression.enclosingScope.classScope().referenceContext.record(expression);
    }

    public void resolve() {
        int startingTypeIndex = 0;
        boolean isPackageInfo = isPackageInfo();
        boolean isModuleInfo = isModuleInfo();
        if (this.types != null && isPackageInfo) {
            // resolve synthetic type declaration
            final TypeDeclaration syntheticTypeDeclaration = this.types[0];
            // set empty javadoc to avoid missing warning (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=95286)
            if (syntheticTypeDeclaration.javadoc == null) {
                syntheticTypeDeclaration.javadoc = new Javadoc(syntheticTypeDeclaration.declarationSourceStart,
                        syntheticTypeDeclaration.declarationSourceStart);
            }
            syntheticTypeDeclaration.resolve(this.scope);
            /*
             * resolve javadoc package if any, skip this step if we don't have a valid scope due to an earlier error (bug 252555)
             * we do it now as the javadoc in the fake type won't be resolved. The peculiar usage of MethodScope to resolve the
             * package level javadoc is because the CU level resolve method   is a NOP to mimic Javadoc's behavior and can't be used
             * as such.
             */
            if (this.javadoc != null && syntheticTypeDeclaration.staticInitializerScope != null) {
                this.javadoc.resolve(syntheticTypeDeclaration.staticInitializerScope);
            }
            startingTypeIndex = 1;
        } else if (this.moduleDeclaration != null && isModuleInfo) {
            if (this.javadoc != null) {
                this.javadoc.resolve((MethodScope) this.moduleDeclaration.scope);
            } else if (this.moduleDeclaration.binding != null) {
                ProblemReporter reporter = this.scope.problemReporter();
                int severity = reporter.computeSeverity(IProblem.JavadocMissing);
                if (severity != ProblemSeverities.Ignore) {
                    reporter.javadocModuleMissing(this.moduleDeclaration.declarationSourceStart,
                            this.moduleDeclaration.bodyStart, severity);
                }
            }
        } else {
            // resolve compilation unit javadoc package if any
            if (this.javadoc != null) {
                this.javadoc.resolve(this.scope);
            }
        }
        if (this.currentPackage != null && this.currentPackage.annotations != null && !isPackageInfo) {
            this.scope.problemReporter().invalidFileNameForPackageAnnotations(this.currentPackage.annotations[0]);
        }
        try {
            if (this.types != null) {
                for (int i = startingTypeIndex, count = this.types.length; i < count; i++) {
                    this.types[i].resolve(this.scope);
                }
            }
            if (!this.compilationResult.hasMandatoryErrors())
                checkUnusedImports();
            reportNLSProblems();
        } catch (AbortCompilationUnit e) {
            this.ignoreFurtherInvestigation = true;
            return;
        }
    }

    private void reportNLSProblems() {
        if (this.nlsTags != null || this.stringLiterals != null) {
            final int stringLiteralsLength = this.stringLiteralsPtr;
            final int nlsTagsLength = this.nlsTags == null ? 0 : this.nlsTags.length;
            if (stringLiteralsLength == 0) {
                if (nlsTagsLength != 0) {
                    for (int i = 0; i < nlsTagsLength; i++) {
                        NLSTag tag = this.nlsTags[i];
                        if (tag != null) {
                            this.scope.problemReporter().unnecessaryNLSTags(tag.start, tag.end);
                        }
                    }
                }
            } else if (nlsTagsLength == 0) {
                // resize string literals
                if (this.stringLiterals.length != stringLiteralsLength) {
                    System.arraycopy(this.stringLiterals, 0,
                            (this.stringLiterals = new StringLiteral[stringLiteralsLength]), 0,
                            stringLiteralsLength);
                }
                Arrays.sort(this.stringLiterals, STRING_LITERAL_COMPARATOR);
                for (int i = 0; i < stringLiteralsLength; i++) {
                    this.scope.problemReporter().nonExternalizedStringLiteral(this.stringLiterals[i]);
                }
            } else {
                // need to iterate both arrays to find non matching elements
                if (this.stringLiterals.length != stringLiteralsLength) {
                    System.arraycopy(this.stringLiterals, 0,
                            (this.stringLiterals = new StringLiteral[stringLiteralsLength]), 0,
                            stringLiteralsLength);
                }
                Arrays.sort(this.stringLiterals, STRING_LITERAL_COMPARATOR);
                int indexInLine = 1;
                int lastLineNumber = -1;
                StringLiteral literal = null;
                int index = 0;
                int i = 0;
                stringLiteralsLoop: for (; i < stringLiteralsLength; i++) {
                    literal = this.stringLiterals[i];
                    final int literalLineNumber = literal.lineNumber;
                    if (lastLineNumber != literalLineNumber) {
                        indexInLine = 1;
                        lastLineNumber = literalLineNumber;
                    } else {
                        indexInLine++;
                    }
                    if (index < nlsTagsLength) {
                        nlsTagsLoop: for (; index < nlsTagsLength; index++) {
                            NLSTag tag = this.nlsTags[index];
                            if (tag == null)
                                continue nlsTagsLoop;
                            int tagLineNumber = tag.lineNumber;
                            if (literalLineNumber < tagLineNumber) {
                                this.scope.problemReporter().nonExternalizedStringLiteral(literal);
                                continue stringLiteralsLoop;
                            } else if (literalLineNumber == tagLineNumber) {
                                if (tag.index == indexInLine) {
                                    this.nlsTags[index] = null;
                                    index++;
                                    continue stringLiteralsLoop;
                                } else {
                                    nlsTagsLoop2: for (int index2 = index + 1; index2 < nlsTagsLength; index2++) {
                                        NLSTag tag2 = this.nlsTags[index2];
                                        if (tag2 == null)
                                            continue nlsTagsLoop2;
                                        int tagLineNumber2 = tag2.lineNumber;
                                        if (literalLineNumber == tagLineNumber2) {
                                            if (tag2.index == indexInLine) {
                                                this.nlsTags[index2] = null;
                                                continue stringLiteralsLoop;
                                            } else {
                                                continue nlsTagsLoop2;
                                            }
                                        } else {
                                            this.scope.problemReporter().nonExternalizedStringLiteral(literal);
                                            continue stringLiteralsLoop;
                                        }
                                    }
                                    this.scope.problemReporter().nonExternalizedStringLiteral(literal);
                                    continue stringLiteralsLoop;
                                }
                            } else {
                                this.scope.problemReporter().unnecessaryNLSTags(tag.start, tag.end);
                                continue nlsTagsLoop;
                            }
                        }
                    }
                    // all nls tags have been processed, so remaining string literals are not externalized
                    break stringLiteralsLoop;
                }
                for (; i < stringLiteralsLength; i++) {
                    this.scope.problemReporter().nonExternalizedStringLiteral(this.stringLiterals[i]);
                }
                if (index < nlsTagsLength) {
                    for (; index < nlsTagsLength; index++) {
                        NLSTag tag = this.nlsTags[index];
                        if (tag != null) {
                            this.scope.problemReporter().unnecessaryNLSTags(tag.start, tag.end);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void tagAsHavingErrors() {
        this.ignoreFurtherInvestigation = true;
    }

    @Override
    public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
        // Nothing to do for this context;
    }

    public void traverse(ASTVisitor visitor, CompilationUnitScope unitScope) {
        traverse(visitor, unitScope, true);
    }

    public void traverse(ASTVisitor visitor, CompilationUnitScope unitScope, boolean skipOnError) {
        if (skipOnError && this.ignoreFurtherInvestigation)
            return;
        try {
            if (visitor.visit(this, this.scope)) {
                if (this.types != null && isPackageInfo()) {
                    // resolve synthetic type declaration
                    final TypeDeclaration syntheticTypeDeclaration = this.types[0];
                    // resolve javadoc package if any
                    final MethodScope methodScope = syntheticTypeDeclaration.staticInitializerScope;
                    // Don't traverse in null scope and invite trouble a la bug 252555.
                    if (this.javadoc != null && methodScope != null) {
                        this.javadoc.traverse(visitor, methodScope);
                    }
                    // Don't traverse in null scope and invite trouble a la bug 252555.
                    if (this.currentPackage != null && methodScope != null) {
                        final Annotation[] annotations = this.currentPackage.annotations;
                        if (annotations != null) {
                            int annotationsLength = annotations.length;
                            for (int i = 0; i < annotationsLength; i++) {
                                annotations[i].traverse(visitor, methodScope);
                            }
                        }
                    }
                }
                if (this.currentPackage != null) {
                    this.currentPackage.traverse(visitor, this.scope);
                }
                if (this.imports != null) {
                    int importLength = this.imports.length;
                    for (int i = 0; i < importLength; i++) {
                        this.imports[i].traverse(visitor, this.scope);
                    }
                }
                if (this.types != null) {
                    int typesLength = this.types.length;
                    for (int i = 0; i < typesLength; i++) {
                        this.types[i].traverse(visitor, this.scope);
                    }
                }
                if (this.isModuleInfo() && this.moduleDeclaration != null) {
                    this.moduleDeclaration.traverse(visitor, this.scope);
                }
            }
            visitor.endVisit(this, this.scope);
        } catch (AbortCompilationUnit e) {
            // ignore
        }
    }

    public ModuleBinding module(LookupEnvironment environment) {
        if (this.moduleDeclaration != null) {
            ModuleBinding binding = this.moduleDeclaration.binding;
            if (binding != null)
                return binding;
        }
        if (this.compilationResult != null) {
            ICompilationUnit compilationUnit = this.compilationResult.compilationUnit;
            if (compilationUnit != null)
                return compilationUnit.module(environment);
        }
        return environment.module;
    }
}