org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.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
 *******************************************************************************/
package org.eclipse.jdt.internal.core;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.Compiler;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.ISourceType;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.core.util.CommentRecorderParser;
import org.eclipse.jdt.internal.core.util.Util;

/**
 * Responsible for resolving types inside a compilation unit being reconciled,
 * reporting the discovered problems to a given IProblemRequestor.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class CompilationUnitProblemFinder extends Compiler {

    /**
     * Answer a new CompilationUnitVisitor using the given name environment and compiler options.
     * The environment and options will be in effect for the lifetime of the compiler.
     * When the compiler is run, compilation results are sent to the given requestor.
     *
     *  @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
     *      Environment used by the compiler in order to resolve type and package
     *      names. The name environment implements the actual connection of the compiler
     *      to the outside world (e.g. in batch mode the name environment is performing
     *      pure file accesses, reuse previous build state or connection to repositories).
     *      Note: the name environment is responsible for implementing the actual classpath
     *            rules.
     *
     *  @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
     *      Configurable part for problem handling, allowing the compiler client to
     *      specify the rules for handling problems (stop on first error or accumulate
     *      them all) and at the same time perform some actions such as opening a dialog
     *      in UI when compiling interactively.
     *      @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
     *
     *   @param compilerOptions The compiler options to use for the resolution.
     *
     *  @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
     *      Component which will receive and persist all compilation results and is intended
     *      to consume them as they are produced. Typically, in a batch compiler, it is
     *      responsible for writing out the actual .class files to the file system.
     *      @see org.eclipse.jdt.internal.compiler.CompilationResult
     *
     *  @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
     *      Factory used inside the compiler to create problem descriptors. It allows the
     *      compiler client to supply its own representation of compilation problems in
     *      order to avoid object conversions. Note that the factory is not supposed
     *      to accumulate the created problems, the compiler will gather them all and hand
     *      them back as part of the compilation unit result.
     */
    protected CompilationUnitProblemFinder(INameEnvironment environment, IErrorHandlingPolicy policy,
            CompilerOptions compilerOptions, ICompilerRequestor requestor, IProblemFactory problemFactory) {

        super(environment, policy, compilerOptions, requestor, problemFactory);
    }

    /**
     * Add additional source types
     */
    @Override
    public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding,
            AccessRestriction accessRestriction) {
        // ensure to jump back to toplevel type for first one (could be a member)
        while (sourceTypes[0].getEnclosingType() != null) {
            sourceTypes[0] = sourceTypes[0].getEnclosingType();
        }

        CompilationResult result = new CompilationResult(sourceTypes[0].getFileName(), 1, 1,
                this.options.maxProblemsPerUnit);

        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=305259, build the compilation unit in its own sand box.
        final long savedComplianceLevel = this.options.complianceLevel;
        final long savedSourceLevel = this.options.sourceLevel;

        LookupEnvironment environment = packageBinding.environment;
        if (environment == null)
            environment = this.lookupEnvironment;

        try {
            IJavaProject project = ((SourceTypeElementInfo) sourceTypes[0]).getHandle().getJavaProject();
            this.options.complianceLevel = CompilerOptions
                    .versionToJdkLevel(project.getOption(JavaCore.COMPILER_COMPLIANCE, true));
            this.options.sourceLevel = CompilerOptions
                    .versionToJdkLevel(project.getOption(JavaCore.COMPILER_SOURCE, true));

            // need to hold onto this
            CompilationUnitDeclaration unit = SourceTypeConverter.buildCompilationUnit(sourceTypes, //sourceTypes[0] is always toplevel here
                    SourceTypeConverter.FIELD_AND_METHOD // need field and methods
                            | SourceTypeConverter.MEMBER_TYPE // need member types
                            | SourceTypeConverter.FIELD_INITIALIZATION, // need field initialization
                    environment.problemReporter, result);

            if (unit != null) {
                environment.buildTypeBindings(unit, accessRestriction);
                environment.completeTypeBindings(unit);
            }
        } finally {
            this.options.complianceLevel = savedComplianceLevel;
            this.options.sourceLevel = savedSourceLevel;
        }
    }

    @Override
    public void accept(IModule module, LookupEnvironment environment) {
        IModuleDescription handle = null;
        if (module instanceof ModuleDescriptionInfo) {
            handle = ((ModuleDescriptionInfo) module).getHandle();
        }
        if (handle == null) {
            super.accept(module, environment);
            return;
        }
        CompilationResult result = new CompilationResult(TypeConstants.MODULE_INFO_FILE_NAME, 1, 1,
                this.options.maxProblemsPerUnit);

        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=305259, build the compilation unit in its own sand box.
        final long savedComplianceLevel = this.options.complianceLevel;
        final long savedSourceLevel = this.options.sourceLevel;

        if (environment == null)
            environment = this.lookupEnvironment;

        try {
            IJavaProject project = handle.getJavaProject();
            this.options.complianceLevel = CompilerOptions
                    .versionToJdkLevel(project.getOption(JavaCore.COMPILER_COMPLIANCE, true));
            this.options.sourceLevel = CompilerOptions
                    .versionToJdkLevel(project.getOption(JavaCore.COMPILER_SOURCE, true));

            // need to hold onto this
            CompilationUnitDeclaration unit = SourceTypeConverter.buildModularCompilationUnit(module,
                    environment.problemReporter, result);

            if (unit != null) {
                environment.buildTypeBindings(unit, null);
                environment.completeTypeBindings(unit);
            }
        } finally {
            this.options.complianceLevel = savedComplianceLevel;
            this.options.sourceLevel = savedSourceLevel;
        }
    }

    protected static CompilerOptions getCompilerOptions(Map settings, boolean creatingAST,
            boolean statementsRecovery) {
        CompilerOptions compilerOptions = new CompilerOptions(settings);
        compilerOptions.performMethodsFullRecovery = statementsRecovery;
        compilerOptions.performStatementsRecovery = statementsRecovery;
        compilerOptions.parseLiteralExpressionsAsConstants = !creatingAST; /*parse literal expressions as constants only if not creating a DOM AST*/
        if (creatingAST)
            compilerOptions.storeAnnotations = true; /* store annotations in the bindings if creating a DOM AST */
        return compilerOptions;
    }

    /*
     *  Low-level API performing the actual compilation
     */
    protected static IErrorHandlingPolicy getHandlingPolicy() {
        return DefaultErrorHandlingPolicies.proceedWithAllProblems();
    }

    /*
     * Answer the component to which will be handed back compilation results from the compiler
     */
    protected static ICompilerRequestor getRequestor() {
        return new ICompilerRequestor() {
            @Override
            public void acceptResult(CompilationResult compilationResult) {
                // default requestor doesn't handle compilation results back
            }
        };
    }

    private static boolean isTestSource(IJavaProject project, ICompilationUnit cu) {
        try {
            IClasspathEntry[] resolvedClasspath = project.getResolvedClasspath(true);
            final IPath resourcePath = cu.getResource().getFullPath();
            for (IClasspathEntry e : resolvedClasspath) {
                if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                    if (e.isTest()) {
                        if (e.getPath().isPrefixOf(resourcePath)) {
                            return true;
                        }
                    }
                }
            }
        } catch (JavaModelException e) {
            Util.log(e, "Exception while determining if compilation unit \"" + cu.getElementName() //$NON-NLS-1$
                    + "\" is test source"); //$NON-NLS-1$
        }
        return false;
    }

    /*
     * Can return null if the process was aborted or canceled 
     */
    public static CompilationUnitDeclaration process(CompilationUnit unitElement, SourceElementParser parser,
            WorkingCopyOwner workingCopyOwner, HashMap problems, boolean creatingAST, int reconcileFlags,
            IProgressMonitor monitor) throws JavaModelException {

        JavaProject project = (JavaProject) unitElement.getJavaProject();
        CancelableNameEnvironment environment = null;
        CancelableProblemFactory problemFactory = null;
        CompilationUnitProblemFinder problemFinder = null;
        CompilationUnitDeclaration unit = null;
        try {
            environment = new CancelableNameEnvironment(project, workingCopyOwner, monitor,
                    !isTestSource(unitElement.getJavaProject(), unitElement));
            problemFactory = new CancelableProblemFactory(monitor);
            CompilerOptions compilerOptions = getCompilerOptions(project.getOptions(true), creatingAST,
                    ((reconcileFlags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0));
            boolean ignoreMethodBodies = (reconcileFlags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0;
            compilerOptions.ignoreMethodBodies = ignoreMethodBodies;
            problemFinder = new CompilationUnitProblemFinder(environment, getHandlingPolicy(), compilerOptions,
                    getRequestor(), problemFactory);
            boolean analyzeAndGenerateCode = true;
            if (ignoreMethodBodies) {
                analyzeAndGenerateCode = false;
            }
            try {
                if (parser != null) {
                    problemFinder.parser = parser;
                    unit = parser.parseCompilationUnit(unitElement, true/*full parse*/, monitor);
                    problemFinder.resolve(unit, unitElement, true, // verify methods
                            analyzeAndGenerateCode, // analyze code
                            analyzeAndGenerateCode); // generate code
                } else {
                    unit = problemFinder.resolve(unitElement, true, // verify methods
                            analyzeAndGenerateCode, // analyze code
                            analyzeAndGenerateCode); // generate code
                }
            } catch (AbortCompilation e) {
                problemFinder.handleInternalException(e, unit);
            }
            if (unit != null) {
                CompilationResult unitResult = unit.compilationResult;
                CategorizedProblem[] unitProblems = unitResult.getCUProblems();
                int length = unitProblems == null ? 0 : unitProblems.length;
                if (length > 0) {
                    CategorizedProblem[] categorizedProblems = new CategorizedProblem[length];
                    System.arraycopy(unitProblems, 0, categorizedProblems, 0, length);
                    problems.put(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, categorizedProblems);
                }
                unitProblems = unitResult.getTasks();
                length = unitProblems == null ? 0 : unitProblems.length;
                if (length > 0) {
                    CategorizedProblem[] categorizedProblems = new CategorizedProblem[length];
                    System.arraycopy(unitProblems, 0, categorizedProblems, 0, length);
                    problems.put(IJavaModelMarker.TASK_MARKER, categorizedProblems);
                }
                if (NameLookup.VERBOSE) {
                    System.out.println(
                            Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInSourcePackage: " //$NON-NLS-1$
                                    + environment.nameLookup.timeSpentInSeekTypesInSourcePackage + "ms"); //$NON-NLS-1$
                    System.out.println(
                            Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInBinaryPackage: " //$NON-NLS-1$
                                    + environment.nameLookup.timeSpentInSeekTypesInBinaryPackage + "ms"); //$NON-NLS-1$
                }
            }
        } catch (OperationCanceledException e) {
            // catch this exception so as to not enter the catch(RuntimeException e) below
            throw e;
        } catch (RuntimeException e) {
            // avoid breaking other tools due to internal compiler failure (40334)
            String lineDelimiter = unitElement.findRecommendedLineSeparator();
            StringBuffer message = new StringBuffer("Exception occurred during problem detection:"); //$NON-NLS-1$
            message.append(lineDelimiter);
            message.append(
                    "----------------------------------- SOURCE BEGIN -------------------------------------"); //$NON-NLS-1$
            message.append(lineDelimiter);
            message.append(unitElement.getSource());
            message.append(lineDelimiter);
            message.append("----------------------------------- SOURCE END -------------------------------------"); //$NON-NLS-1$
            Util.log(e, message.toString());
            throw new JavaModelException(e, IJavaModelStatusConstants.COMPILER_FAILURE);
        } finally {
            if (environment != null)
                environment.setMonitor(null); // don't hold a reference to this external object
            if (problemFactory != null)
                problemFactory.monitor = null; // don't hold a reference to this external object
            // NB: unit.cleanUp() is done by caller
            if (problemFinder != null && !creatingAST)
                problemFinder.lookupEnvironment.reset();
        }
        return unit;
    }

    public static CompilationUnitDeclaration process(CompilationUnit unitElement, WorkingCopyOwner workingCopyOwner,
            HashMap problems, boolean creatingAST, int reconcileFlags, IProgressMonitor monitor)
            throws JavaModelException {

        return process(unitElement, null/*use default Parser*/, workingCopyOwner, problems, creatingAST,
                reconcileFlags, monitor);
    }

    /* (non-Javadoc)
     * Fix for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=60689.
     * @see org.eclipse.jdt.internal.compiler.Compiler#initializeParser()
     */
    @Override
    public void initializeParser() {
        this.parser = new CommentRecorderParser(this.problemReporter,
                this.options.parseLiteralExpressionsAsConstants);
    }
}