| /******************************************************************************* |
| * Copyright (c) 2000, 2016 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 |
| * Technical University Berlin - extended API and implementation |
| * Stephan Herrmann - Contribution for |
| * Bug 458396 - NPE in CodeStream.invoke() |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.problem; |
| |
| 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.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; |
| import org.eclipse.jdt.internal.compiler.IProblemFactory; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.WrapperKind; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.util.Util; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.GuardPredicateDeclaration; |
| |
| /* |
| * Compiler error handler, responsible to determine whether |
| * a problem is actually a warning or an error; also will |
| * decide whether the compilation task can be processed further or not. |
| * |
| * Behavior : will request its current policy if need to stop on |
| * first error, and if should proceed (persist) with problems. |
| */ |
| |
| public class ProblemHandler { |
| |
| public final static String[] NoArgument = CharOperation.NO_STRINGS; |
| |
| public IErrorHandlingPolicy policy; |
| public final IProblemFactory problemFactory; |
| public final CompilerOptions options; |
| |
| /* When temporarily switching policies, store here the original root policy (for temporary resume). */ |
| private IErrorHandlingPolicy rootPolicy; |
| |
| protected boolean suppressTagging = false; |
| |
| //{ObjectTeams: support for passing a rechecker: |
| public IProblemRechecker rechecker; |
| // SH} |
| |
| /* |
| * Problem handler can be supplied with a policy to specify |
| * its behavior in error handling. Also see static methods for |
| * built-in policies. |
| * |
| */ |
| public ProblemHandler(IErrorHandlingPolicy policy, CompilerOptions options, IProblemFactory problemFactory) { |
| this.policy = policy; |
| this.problemFactory = problemFactory; |
| this.options = options; |
| } |
| /* |
| * Given the current configuration, answers which category the problem |
| * falls into: |
| * Error | Warning | Ignore |
| */ |
| public int computeSeverity(int problemId){ |
| |
| return ProblemSeverities.Error; // by default all problems are errors |
| } |
| public CategorizedProblem createProblem( |
| char[] fileName, |
| int problemId, |
| String[] problemArguments, |
| String[] messageArguments, |
| int severity, |
| int problemStartPosition, |
| int problemEndPosition, |
| int lineNumber, |
| int columnNumber) { |
| |
| return this.problemFactory.createProblem( |
| fileName, |
| problemId, |
| problemArguments, |
| messageArguments, |
| severity, |
| problemStartPosition, |
| problemEndPosition, |
| lineNumber, |
| columnNumber); |
| } |
| public CategorizedProblem createProblem( |
| char[] fileName, |
| int problemId, |
| String[] problemArguments, |
| int elaborationId, |
| String[] messageArguments, |
| int severity, |
| int problemStartPosition, |
| int problemEndPosition, |
| int lineNumber, |
| int columnNumber) { |
| return this.problemFactory.createProblem( |
| fileName, |
| problemId, |
| problemArguments, |
| elaborationId, |
| messageArguments, |
| severity, |
| problemStartPosition, |
| problemEndPosition, |
| lineNumber, |
| columnNumber); |
| } |
| public void handle( |
| int problemId, |
| String[] problemArguments, |
| int elaborationId, |
| String[] messageArguments, |
| int severity, |
| int problemStartPosition, |
| int problemEndPosition, |
| ReferenceContext referenceContext, |
| CompilationResult unitResult) { |
| //{ObjectTeams: wrap once more to reset rechecker after handling: |
| try { |
| protectedHandle(problemId, problemArguments, elaborationId, messageArguments, severity, problemStartPosition, problemEndPosition, referenceContext, unitResult); |
| } finally { |
| this.rechecker = null; |
| } |
| } |
| private void protectedHandle( |
| int problemId, |
| String[] problemArguments, |
| int elaborationId, |
| String[] messageArguments, |
| int severity, |
| int problemStartPosition, |
| int problemEndPosition, |
| ReferenceContext referenceContext, |
| CompilationResult unitResult) |
| { |
| // SH} |
| |
| if (severity == ProblemSeverities.Ignore) |
| return; |
| |
| boolean mandatory = (severity & (ProblemSeverities.Error | ProblemSeverities.Optional)) == ProblemSeverities.Error; |
| if ((severity & ProblemSeverities.InternalError) == 0 && this.policy.ignoreAllErrors()) { |
| // Error is not to be exposed, but clients may need still notification as to whether there are silently-ignored-errors. |
| // if no reference context, we need to abort from the current compilation process |
| if (referenceContext == null) { |
| if ((severity & ProblemSeverities.Error) != 0) { // non reportable error is fatal |
| CategorizedProblem problem = this.createProblem(null, problemId, problemArguments, elaborationId, messageArguments, severity, 0, 0, 0, 0); |
| throw new AbortCompilation(null, problem); |
| } else { |
| return; // ignore non reportable warning |
| } |
| } |
| if (mandatory) |
| referenceContext.tagAsHavingIgnoredMandatoryErrors(problemId); |
| return; |
| } |
| |
| if ((severity & ProblemSeverities.Optional) != 0 && problemId != IProblem.Task && !this.options.ignoreSourceFolderWarningOption) { |
| //{ObjectTeams: NPE prevention: |
| if (unitResult != null) { |
| // orig: |
| ICompilationUnit cu = unitResult.getCompilationUnit(); |
| try{ |
| if (cu != null && cu.ignoreOptionalProblems()) |
| return; |
| // workaround for illegal implementation of ICompilationUnit, see https://bugs.eclipse.org/372351 |
| } catch (AbstractMethodError ex) { |
| // continue |
| } |
| // :giro |
| } |
| // SH} |
| } |
| |
| //{ObjectTeams: several kinds of filtering: |
| // some problems cannot be decided at the point of reporting, require rechecking: |
| boolean requireRecheck = this.rechecker != null; |
| // avoid unnecessary duplication of diagnostics: |
| boolean markGenerated = false; |
| // ignore errors in generated/copied methods if errors have already been reported |
| if (referenceContext instanceof AbstractMethodDeclaration) { |
| AbstractMethodDeclaration method = (AbstractMethodDeclaration)referenceContext; |
| if ( method.isCopied |
| || ( method.isGenerated |
| && (method.isMappingWrapper == WrapperKind.NONE) |
| && !(method instanceof GuardPredicateDeclaration))) |
| { |
| // safer to ignore only if some error has already been reported |
| boolean alreadyReported = ((severity & ProblemSeverities.Error) != 0) |
| ? method.compilationResult().hasErrors() |
| : method.compilationResult().hasWarnings(); |
| if (alreadyReported && !requireRecheck) { // recheckable problems should not be skipped, rechecker has better judgment |
| if ((severity & ProblemSeverities.Fatal) != 0) |
| referenceContext.tagAsHavingErrors(); |
| return; |
| } |
| } else if (method.binding != null) { |
| ReferenceBinding clazz = method.binding.declaringClass; |
| if (clazz != null && clazz.isRole() && !clazz.isRegularInterface()) { |
| // has the same problem already been reported (re the other part; ifc/class)? |
| if (unitResult.problemCount > 0 && unitResult.problems != null) |
| for (CategorizedProblem prob : unitResult.problems) |
| if ( prob != null |
| && prob.getID() == problemId |
| && prob.getSourceStart() == problemStartPosition |
| && prob.getSourceEnd() == problemEndPosition) |
| { |
| if ((severity & ProblemSeverities.Error) != 0) { |
| method.tagAsHavingErrors(); |
| return; // don't record error again. |
| } |
| // still need the warning to match against @SuppressWarnings, |
| // but don't report it to the user |
| markGenerated = true; |
| } |
| } |
| } |
| } |
| // ignore most errors in purely copied classes: |
| if ( referenceContext instanceof TypeDeclaration |
| && ((TypeDeclaration)referenceContext).isPurelyCopied) |
| { |
| switch (problemId) { |
| // do report issues that could be caused by multiple inheritance: |
| // super-type issues: |
| case IProblem.RoleClassOverridesInterface: |
| case IProblem.RoleInterfaceOverridesClass: |
| case IProblem.OverridingFinalRole: |
| case IProblem.IncompatibleSuperclasses: |
| case IProblem.RoleShadowsVisibleType: |
| case IProblem.RegularOverridesTeam: |
| case IProblem.IncomparableTSupers: |
| // playedBy issues: |
| case IProblem.OverlappingRoleHierarchies: |
| case IProblem.IllegalPlayedByRedefinition: |
| case IProblem.IncompatibleBaseclasses: |
| case IProblem.OverridingPlayedBy: |
| case IProblem.BaseclassIsRoleOfTheSameTeam: |
| case IProblem.RoleBindingPotentiallyAmbiguous: |
| case IProblem.AmbiguousLiftingMayBreakClients: |
| case IProblem.AbstractPotentiallyRelevantRole: |
| case IProblem.AbstractRelevantRole: |
| break; |
| // also report build problems: |
| case IProblem.MissingCopiedRole: |
| break; |
| default: |
| return; |
| } |
| } |
| // SMAP-related position mapping may produce positions outside the file, crop them now: |
| if (unitResult != null && problemStartPosition > unitResult.sourceEndPos) { |
| problemStartPosition = 0; |
| problemEndPosition = 0; |
| } |
| // SH} |
| |
| // if no reference context, we need to abort from the current compilation process |
| if (referenceContext == null) { |
| if ((severity & ProblemSeverities.Error) != 0) { // non reportable error is fatal |
| CategorizedProblem problem = this.createProblem(null, problemId, problemArguments, elaborationId, messageArguments, severity, 0, 0, 0, 0); |
| throw new AbortCompilation(null, problem); |
| } else { |
| return; // ignore non reportable warning |
| } |
| } |
| |
| int[] lineEnds; |
| int lineNumber = problemStartPosition >= 0 |
| ? Util.getLineNumber(problemStartPosition, lineEnds = unitResult.getLineSeparatorPositions(), 0, lineEnds.length-1) |
| : 0; |
| int columnNumber = problemStartPosition >= 0 |
| ? Util.searchColumnNumber(unitResult.getLineSeparatorPositions(), lineNumber, problemStartPosition) |
| : 0; |
| CategorizedProblem problem = |
| this.createProblem( |
| unitResult.getFileName(), |
| problemId, |
| problemArguments, |
| elaborationId, |
| messageArguments, |
| severity, |
| problemStartPosition, |
| problemEndPosition, |
| lineNumber, |
| columnNumber); |
| //{ObjectTeams: transfer more info to the problem: |
| if (problem instanceof DefaultProblem) { |
| DefaultProblem defaultProblem = (DefaultProblem)problem; |
| defaultProblem.rechecker = this.rechecker; |
| if (markGenerated) |
| defaultProblem.markGenerated(); |
| } |
| // SH} |
| |
| if (problem == null) return; // problem couldn't be created, ignore |
| |
| switch (severity & ProblemSeverities.Error) { |
| case ProblemSeverities.Error : |
| record(problem, unitResult, referenceContext, mandatory); |
| if ((severity & ProblemSeverities.Fatal) != 0) { |
| // don't abort or tag as error if the error is suppressed |
| if (!referenceContext.hasErrors() && !mandatory && this.options.suppressOptionalErrors) { |
| CompilationUnitDeclaration unitDecl = referenceContext.getCompilationUnitDeclaration(); |
| if (unitDecl != null && unitDecl.isSuppressed(problem)) { |
| return; |
| } |
| } |
| //{ObjectTeams: problems with a rechecker are considered "optional", don't tag the reference context |
| if (!requireRecheck) |
| // SH} |
| if (!this.suppressTagging || this.options.treatOptionalErrorAsFatal) { |
| referenceContext.tagAsHavingErrors(); |
| } |
| // should abort ? |
| int abortLevel; |
| if ((abortLevel = this.policy.stopOnFirstError() ? ProblemSeverities.AbortCompilation : severity & ProblemSeverities.Abort) != 0) { |
| referenceContext.abort(abortLevel, problem); |
| } |
| } |
| break; |
| case ProblemSeverities.Warning : |
| record(problem, unitResult, referenceContext, false); |
| break; |
| } |
| } |
| /** |
| * Standard problem handling API, the actual severity (warning/error/ignore) is deducted |
| * from the problem ID and the current compiler options. |
| */ |
| public void handle( |
| int problemId, |
| String[] problemArguments, |
| String[] messageArguments, |
| int problemStartPosition, |
| int problemEndPosition, |
| ReferenceContext referenceContext, |
| CompilationResult unitResult) { |
| |
| this.handle( |
| problemId, |
| problemArguments, |
| 0, // no message elaboration |
| messageArguments, |
| computeSeverity(problemId), // severity inferred using the ID |
| problemStartPosition, |
| problemEndPosition, |
| referenceContext, |
| unitResult); |
| } |
| public void record(CategorizedProblem problem, CompilationResult unitResult, ReferenceContext referenceContext, boolean mandatoryError) { |
| unitResult.record(problem, referenceContext, mandatoryError); |
| } |
| /** @return old policy. */ |
| public IErrorHandlingPolicy switchErrorHandlingPolicy(IErrorHandlingPolicy newPolicy) { |
| if (this.rootPolicy == null) |
| this.rootPolicy = this.policy; |
| IErrorHandlingPolicy presentPolicy = this.policy; |
| this.policy = newPolicy; |
| return presentPolicy; |
| } |
| /** |
| * Temporarily suspend a temporary error handling policy. |
| * @return old policy. |
| */ |
| public IErrorHandlingPolicy suspendTempErrorHandlingPolicy() { |
| IErrorHandlingPolicy presentPolicy = this.policy; |
| if (this.rootPolicy != null) |
| this.policy = this.rootPolicy; |
| return presentPolicy; |
| } |
| /** |
| * Resume from a corresponding {@link #suspendTempErrorHandlingPolicy()}. |
| * @param previousPolicy the result value of the matching suspend call |
| */ |
| public void resumeTempErrorHandlingPolicy(IErrorHandlingPolicy previousPolicy) { |
| this.policy = previousPolicy; |
| } |
| } |