blob: 1530b596ec9ebedb30fed8ae7325ef6677262e4a [file] [log] [blame]
/*******************************************************************************
* 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;
}
}