org.eclipse.jdt.internal.compiler.flow.ExceptionHandlingFlowContext Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
import java.util.ArrayList;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.codegen.ObjectCache;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
/**
* Reflects the context of code analysis, keeping track of enclosing
* try statements, exception handlers, etc...
*/
public class ExceptionHandlingFlowContext extends FlowContext {
public final static int BitCacheSize = 32; // 32 bits per int
public ReferenceBinding[] handledExceptions;
int[] isReached;
int[] isNeeded;
UnconditionalFlowInfo[] initsOnExceptions;
ObjectCache indexes = new ObjectCache();
boolean isMethodContext;
public UnconditionalFlowInfo initsOnReturn;
public FlowContext initializationParent; // special parent relationship only for initialization purpose
// for dealing with anonymous constructor thrown exceptions
public ArrayList extendedExceptions;
public ExceptionHandlingFlowContext(
FlowContext parent,
ASTNode associatedNode,
ReferenceBinding[] handledExceptions,
FlowContext initializationParent,
BlockScope scope,
UnconditionalFlowInfo flowInfo) {
super(parent, associatedNode);
this.isMethodContext = scope == scope.methodScope();
this.handledExceptions = handledExceptions;
int count = handledExceptions.length, cacheSize = (count / ExceptionHandlingFlowContext.BitCacheSize) + 1;
this.isReached = new int[cacheSize]; // none is reached by default
this.isNeeded = new int[cacheSize]; // none is needed by default
this.initsOnExceptions = new UnconditionalFlowInfo[count];
boolean markExceptionsAndThrowableAsReached =
!this.isMethodContext || scope.compilerOptions().reportUnusedDeclaredThrownExceptionExemptExceptionAndThrowable;
for (int i = 0; i < count; i++) {
ReferenceBinding handledException = handledExceptions[i];
this.indexes.put(handledException, i); // key type -> value index
if (handledException.isUncheckedException(true)) {
if (markExceptionsAndThrowableAsReached ||
handledException.id != TypeIds.T_JavaLangThrowable &&
handledException.id != TypeIds.T_JavaLangException) {
this.isReached[i / ExceptionHandlingFlowContext.BitCacheSize] |= 1 << (i % ExceptionHandlingFlowContext.BitCacheSize);
}
this.initsOnExceptions[i] = flowInfo.unconditionalCopy();
} else {
this.initsOnExceptions[i] = FlowInfo.DEAD_END;
}
}
if (!this.isMethodContext) {
System.arraycopy(this.isReached, 0, this.isNeeded, 0, cacheSize);
}
this.initsOnReturn = FlowInfo.DEAD_END;
this. initializationParent = initializationParent;
}
public void complainIfUnusedExceptionHandlers(AbstractMethodDeclaration method) {
MethodScope scope = method.scope;
// can optionally skip overriding methods
if ((method.binding.modifiers & (ExtraCompilerModifiers.AccOverriding | ExtraCompilerModifiers.AccImplementing)) != 0
&& !scope.compilerOptions().reportUnusedDeclaredThrownExceptionWhenOverriding) {
return;
}
// report errors for unreachable exception handlers
TypeBinding[] docCommentReferences = null;
int docCommentReferencesLength = 0;
if (scope.compilerOptions().
reportUnusedDeclaredThrownExceptionIncludeDocCommentReference &&
method.javadoc != null &&
method.javadoc.exceptionReferences != null &&
(docCommentReferencesLength = method.javadoc.exceptionReferences.length) > 0) {
docCommentReferences = new TypeBinding[docCommentReferencesLength];
for (int i = 0; i < docCommentReferencesLength; i++) {
docCommentReferences[i] = method.javadoc.exceptionReferences[i].resolvedType;
}
}
nextHandledException: for (int i = 0, count = this.handledExceptions.length; i < count; i++) {
int index = this.indexes.get(this.handledExceptions[i]);
if ((this.isReached[index / ExceptionHandlingFlowContext.BitCacheSize] & 1 << (index % ExceptionHandlingFlowContext.BitCacheSize)) == 0) {
for (int j = 0; j < docCommentReferencesLength; j++) {
if (docCommentReferences[j] == this.handledExceptions[i]) {
continue nextHandledException;
}
}
scope.problemReporter().unusedDeclaredThrownException(
this.handledExceptions[index],
method,
method.thrownExceptions[index]);
}
}
}
public void complainIfUnusedExceptionHandlers(BlockScope scope,TryStatement tryStatement) {
// report errors for unreachable exception handlers
for (int i = 0, count = this.handledExceptions.length; i < count; i++) {
int index = this.indexes.get(this.handledExceptions[i]);
int cacheIndex = index / ExceptionHandlingFlowContext.BitCacheSize;
int bitMask = 1 << (index % ExceptionHandlingFlowContext.BitCacheSize);
if ((this.isReached[cacheIndex] & bitMask) == 0) {
scope.problemReporter().unreachableCatchBlock(
this.handledExceptions[index],
tryStatement.catchArguments[index].type);
} else {
if ((this.isNeeded[cacheIndex] & bitMask) == 0) {
scope.problemReporter().hiddenCatchBlock(
this.handledExceptions[index],
tryStatement.catchArguments[index].type);
}
}
}
}
public String individualToString() {
StringBuffer buffer = new StringBuffer("Exception flow context"); //$NON-NLS-1$
int length = this.handledExceptions.length;
for (int i = 0; i < length; i++) {
int cacheIndex = i / ExceptionHandlingFlowContext.BitCacheSize;
int bitMask = 1 << (i % ExceptionHandlingFlowContext.BitCacheSize);
buffer.append('[').append(this.handledExceptions[i].readableName());
if ((this.isReached[cacheIndex] & bitMask) != 0) {
if ((this.isNeeded[cacheIndex] & bitMask) == 0) {
buffer.append("-masked"); //$NON-NLS-1$
} else {
buffer.append("-reached"); //$NON-NLS-1$
}
} else {
buffer.append("-not reached"); //$NON-NLS-1$
}
buffer.append('-').append(this.initsOnExceptions[i].toString()).append(']');
}
buffer.append("[initsOnReturn -").append(this.initsOnReturn.toString()).append(']'); //$NON-NLS-1$
return buffer.toString();
}
public UnconditionalFlowInfo initsOnException(ReferenceBinding exceptionType) {
int index;
if ((index = this.indexes.get(exceptionType)) < 0) {
return FlowInfo.DEAD_END;
}
return this.initsOnExceptions[index];
}
public UnconditionalFlowInfo initsOnReturn(){
return this.initsOnReturn;
}
/*
* Compute a merged list of unhandled exception types (keeping only the most generic ones).
* This is necessary to add synthetic thrown exceptions for anonymous type constructors (JLS 8.6).
*/
public void mergeUnhandledException(TypeBinding newException){
if (this.extendedExceptions == null){
this.extendedExceptions = new ArrayList(5);
for (int i = 0; i < this.handledExceptions.length; i++){
this.extendedExceptions.add(this.handledExceptions[i]);
}
}
boolean isRedundant = false;
for(int i = this.extendedExceptions.size()-1; i >= 0; i--){
switch(Scope.compareTypes(newException, (TypeBinding)this.extendedExceptions.get(i))){
case Scope.MORE_GENERIC :
this.extendedExceptions.remove(i);
break;
case Scope.EQUAL_OR_MORE_SPECIFIC :
isRedundant = true;
break;
case Scope.NOT_RELATED :
break;
}
}
if (!isRedundant){
this.extendedExceptions.add(newException);
}
}
public void recordHandlingException(
ReferenceBinding exceptionType,
UnconditionalFlowInfo flowInfo,
TypeBinding raisedException,
ASTNode invocationSite,
boolean wasAlreadyDefinitelyCaught) {
int index = this.indexes.get(exceptionType);
int cacheIndex = index / ExceptionHandlingFlowContext.BitCacheSize;
int bitMask = 1 << (index % ExceptionHandlingFlowContext.BitCacheSize);
if (!wasAlreadyDefinitelyCaught) {
this.isNeeded[cacheIndex] |= bitMask;
}
this.isReached[cacheIndex] |= bitMask;
this.initsOnExceptions[index] =
(this.initsOnExceptions[index].tagBits & FlowInfo.UNREACHABLE) == 0 ?
this.initsOnExceptions[index].mergedWith(flowInfo):
flowInfo.unconditionalCopy();
}
public void recordReturnFrom(UnconditionalFlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
if ((this.initsOnReturn.tagBits & FlowInfo.UNREACHABLE) == 0) {
this.initsOnReturn = this.initsOnReturn.mergedWith(flowInfo);
}
else {
this.initsOnReturn = (UnconditionalFlowInfo) flowInfo.copy();
}
}
}
/**
* Exception handlers (with no finally block) are also included with subroutine
* only once (in case parented with true InsideSubRoutineFlowContext).
* Standard management of subroutines need to also operate on intermediate
* exception handlers.
* @see org.eclipse.jdt.internal.compiler.flow.FlowContext#subroutine()
*/
public SubRoutineStatement subroutine() {
if (this.associatedNode instanceof SubRoutineStatement) {
// exception handler context may be child of InsideSubRoutineFlowContext, which maps to same handler
if (this.parent.subroutine() == this.associatedNode)
return null;
return (SubRoutineStatement) this.associatedNode;
}
return null;
}
}