// Report an error if necessary (if even more unreachable than previously reported // complaintLevel = 0 if was reachable up until now, 1 if fake reachable (deadcode), 2 if fatal // unreachable (error) public int complainIfUnreachable( FlowInfo flowInfo, BlockScope scope, int previousComplaintLevel, boolean endOfBlock) { if ((flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0) { if ((flowInfo.reachMode() & FlowInfo.UNREACHABLE_OR_DEAD) != 0) this.bits &= ~ASTNode.IsReachable; if (flowInfo == FlowInfo.DEAD_END) { if (previousComplaintLevel < COMPLAINED_UNREACHABLE) { scope.problemReporter().unreachableCode(this); if (endOfBlock) scope.checkUnclosedCloseables(flowInfo, null, null, null); } return COMPLAINED_UNREACHABLE; } else { if (previousComplaintLevel < COMPLAINED_FAKE_REACHABLE) { scope.problemReporter().fakeReachable(this); if (endOfBlock) scope.checkUnclosedCloseables(flowInfo, null, null, null); } return COMPLAINED_FAKE_REACHABLE; } } return previousComplaintLevel; }
// Report an error if necessary public boolean complainIfUnreachable( FlowInfo flowInfo, BlockScope scope, boolean didAlreadyComplain) { if ((flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0) { this.bits &= ~ASTNode.IsReachableMASK; boolean reported = flowInfo == FlowInfo.DEAD_END; if (!didAlreadyComplain && reported) { scope.problemReporter().unreachableCode(this); } return reported; // keep going for fake reachable } return false; }
public void checkCapturedLocalInitializationIfNecessary( ReferenceBinding checkedType, BlockScope currentScope, FlowInfo flowInfo) { if (((checkedType.tagBits & (TagBits.AnonymousTypeMask | TagBits.LocalTypeMask)) == TagBits.LocalTypeMask) && !currentScope.isDefinedInType(checkedType)) { // only check external allocations NestedTypeBinding nestedType = (NestedTypeBinding) checkedType; SyntheticArgumentBinding[] syntheticArguments = nestedType.syntheticOuterLocalVariables(); if (syntheticArguments != null) for (int i = 0, count = syntheticArguments.length; i < count; i++) { SyntheticArgumentBinding syntheticArgument = syntheticArguments[i]; LocalVariableBinding targetLocal; if ((targetLocal = syntheticArgument.actualOuterLocalVariable) == null) continue; if (targetLocal.declaration != null && !flowInfo.isDefinitelyAssigned(targetLocal)) { currentScope.problemReporter().uninitializedLocalVariable(targetLocal, this); } } } }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { LocalVariableBinding local = this.expression.localVariableBinding(); if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { flowInfo = this.expression.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); FlowInfo initsWhenTrue = flowInfo.copy(); initsWhenTrue.markAsComparedEqualToNonNull(local); if ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0) { initsWhenTrue.markedAsNullOrNonNullInAssertExpression(local); } flowContext.recordUsingNullReference( currentScope, local, this.expression, FlowContext.CAN_ONLY_NULL | FlowContext.IN_INSTANCEOF, flowInfo); // no impact upon enclosing try context return FlowInfo.conditional(initsWhenTrue, flowInfo.copy()); } return this.expression.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // Consider the try block and catch block so as to compute the intersection of initializations // and // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append // its // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine // does not // complete, then only keep this result for the rest of the analysis // process the finally block (subroutine) - create a context for the subroutine preTryInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); if (anyExceptionVariable != null) { anyExceptionVariable.useFlag = LocalVariableBinding.USED; } if (returnAddressVariable != null) { // TODO (philippe) if subroutine is escaping, unused returnAddressVariable.useFlag = LocalVariableBinding.USED; } InsideSubRoutineFlowContext insideSubContext; FinallyFlowContext finallyContext; UnconditionalFlowInfo subInfo; if (subRoutineStartLabel == null) { // no finally block insideSubContext = null; finallyContext = null; subInfo = null; } else { // analyse finally block first insideSubContext = new InsideSubRoutineFlowContext(flowContext, this); subInfo = finallyBlock .analyseCode( currentScope, finallyContext = new FinallyFlowContext(flowContext, finallyBlock), flowInfo.copy().unconditionalInits().discardNullRelatedInitializations()) .unconditionalInits(); if (subInfo == FlowInfo.DEAD_END) { isSubRoutineEscaping = true; scope.problemReporter().finallyMustCompleteNormally(finallyBlock); } this.subRoutineInits = subInfo; } // process the try block in a context handling the local exceptions. ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext( insideSubContext == null ? flowContext : insideSubContext, tryBlock, caughtExceptionTypes, scope, flowInfo.unconditionalInits()); FlowInfo tryInfo; if (tryBlock.isEmptyBlock()) { tryInfo = flowInfo; tryBlockExit = false; } else { tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy()); tryBlockExit = !tryInfo.isReachable(); } // check unreachable catch blocks handlingContext.complainIfUnusedExceptionHandlers(scope, this); // process the catch blocks - computing the minimal exit depth amongst try/catch if (catchArguments != null) { int catchCount; catchExits = new boolean[catchCount = catchBlocks.length]; for (int i = 0; i < catchCount; i++) { // keep track of the inits that could potentially have led to this exception handler (for // final assignments diagnosis) FlowInfo catchInfo = flowInfo .copy() .unconditionalInits() .addPotentialInitializationsFrom( handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits()) .addPotentialInitializationsFrom(tryInfo.unconditionalInits()) .addPotentialInitializationsFrom(handlingContext.initsOnReturn); // catch var is always set LocalVariableBinding catchArg = catchArguments[i].binding; FlowContext catchContext = insideSubContext == null ? flowContext : insideSubContext; catchInfo.markAsDefinitelyAssigned(catchArg); catchInfo.markAsDefinitelyNonNull(catchArg); /* "If we are about to consider an unchecked exception handler, potential inits may have occured inside the try block that need to be detected , e.g. try { x = 1; throwSomething();} catch(Exception e){ x = 2} " "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index]) ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]." */ if (tryBlock.statements == null) { catchInfo.setReachMode(FlowInfo.UNREACHABLE); } catchInfo = catchBlocks[i].analyseCode(currentScope, catchContext, catchInfo); catchExits[i] = !catchInfo.isReachable(); tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits()); } } if (subRoutineStartLabel == null) { mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(tryInfo); return tryInfo; } // we also need to check potential multiple assignments of final variables inside the finally // block // need to include potential inits from returns inside the try/catch parts - 1GK2AOF finallyContext.complainOnDeferredChecks( tryInfo.isReachable() ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn)) : insideSubContext.initsOnReturn, currentScope); if (subInfo == FlowInfo.DEAD_END) { mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(subInfo); return subInfo; } else { FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo); mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { Constant cst = this.left.optimizedBooleanConstant(); boolean isLeftOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isLeftOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; if (isLeftOptimizedFalse) { // FALSE || anything // need to be careful of scenario: // (x || y) || !z, if passing the left info to the right, it would be swapped by the ! FlowInfo mergedInfo = this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); flowContext.expireNullCheckedFieldInfo(); mergedInfo = this.right.analyseCode(currentScope, flowContext, mergedInfo); flowContext.expireNullCheckedFieldInfo(); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } FlowInfo leftInfo = this.left.analyseCode(currentScope, flowContext, flowInfo); flowContext.expireNullCheckedFieldInfo(); // need to be careful of scenario: // (x || y) || !z, if passing the left info to the right, it would be swapped by the ! FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalCopy(); this.rightInitStateIndex = currentScope.methodScope().recordInitializationStates(rightInfo); int previousMode = rightInfo.reachMode(); if (isLeftOptimizedTrue) { if ((rightInfo.reachMode() & FlowInfo.UNREACHABLE) == 0) { currentScope.problemReporter().fakeReachable(this.right); rightInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } } rightInfo = this.right.analyseCode(currentScope, flowContext, rightInfo); flowContext.expireNullCheckedFieldInfo(); if ((this.left.implicitConversion & TypeIds.UNBOXING) != 0) { this.left.checkNPE(currentScope, flowContext, flowInfo); } if ((this.right.implicitConversion & TypeIds.UNBOXING) != 0) { this.right.checkNPE(currentScope, flowContext, flowInfo); } // The definitely null variables in right info when true should not be missed out while merging // https://bugs.eclipse.org/bugs/show_bug.cgi?id=299900 FlowInfo leftInfoWhenTrueForMerging = leftInfo .initsWhenTrue() .unconditionalCopy() .addPotentialInitializationsFrom(rightInfo.unconditionalInitsWithoutSideEffect()); FlowInfo mergedInfo = FlowInfo.conditional( // merging two true initInfos for such a negative case: if ((t && (b = t)) || f) r = b; // // b may not have been initialized leftInfoWhenTrueForMerging .unconditionalInits() .mergedWith( rightInfo.safeInitsWhenTrue().setReachMode(previousMode).unconditionalInits()), rightInfo.initsWhenFalse()); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { this.preAssertInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); Constant cst = this.assertExpression.optimizedBooleanConstant(); if ((this.assertExpression.implicitConversion & TypeIds.UNBOXING) != 0) { this.assertExpression.checkNPE(currentScope, flowContext, flowInfo); } boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false; flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING; FlowInfo conditionFlowInfo = this.assertExpression.analyseCode(currentScope, flowContext, flowInfo.copy()); flowContext.extendTimeToLiveForNullCheckedField(1); // survive this assert as a Statement flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING; UnconditionalFlowInfo assertWhenTrueInfo = conditionFlowInfo.initsWhenTrue().unconditionalInits(); FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse(); if (isOptimizedTrueAssertion) { assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } if (this.exceptionArgument != null) { // only gets evaluated when escaping - results are not taken into account FlowInfo exceptionInfo = this.exceptionArgument.analyseCode(currentScope, flowContext, assertInfo.copy()); if (isOptimizedTrueAssertion) { currentScope.problemReporter().fakeReachable(this.exceptionArgument); } else { flowContext.checkExceptionHandlers( currentScope.getJavaLangAssertionError(), this, exceptionInfo, currentScope); } } if (!isOptimizedTrueAssertion) { // add the assert support in the clinit manageSyntheticAccessIfNecessary(currentScope, flowInfo); } // account for potential AssertionError: flowContext.recordAbruptExit(); if (isOptimizedFalseAssertion) { return flowInfo; // if assertions are enabled, the following code will be unreachable // change this if we need to carry null analysis results of the assert // expression downstream } else { CompilerOptions compilerOptions = currentScope.compilerOptions(); if (!compilerOptions.includeNullInfoFromAsserts) { // keep just the initializations info, don't include assert's null info // merge initialization info's and then add back the null info from flowInfo to // make sure that the empty null info of assertInfo doesnt change flowInfo's null info. return ((flowInfo.nullInfoLessUnconditionalCopy()) .mergedWith(assertInfo.nullInfoLessUnconditionalCopy())) .addNullInfoFrom(flowInfo); } return flowInfo .mergedWith(assertInfo.nullInfoLessUnconditionalCopy()) .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo()); // keep the merge from the initial code for the definite assignment // analysis, tweak the null part to influence nulls downstream } }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // check captured variables are initialized in current context (26134) checkCapturedLocalInitializationIfNecessary( (ReferenceBinding) this.binding.declaringClass.erasure(), currentScope, flowInfo); // process arguments if (this.arguments != null) { boolean analyseResources = currentScope.compilerOptions().analyseResourceLeaks; boolean hasResourceWrapperType = analyseResources && this.resolvedType instanceof ReferenceBinding && ((ReferenceBinding) this.resolvedType).hasTypeBit(TypeIds.BitWrapperCloseable); for (int i = 0, count = this.arguments.length; i < count; i++) { flowInfo = this.arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); // if argument is an AutoCloseable insert info that it *may* be closed (by the target // method, i.e.) if (analyseResources && !hasResourceWrapperType) { // allocation of wrapped closeables is analyzed specially flowInfo = FakedTrackingVariable.markPassedToOutside( currentScope, this.arguments[i], flowInfo, flowContext, false); } this.arguments[i].checkNPEbyUnboxing(currentScope, flowContext, flowInfo); } analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); } // record some dependency information for exception types ReferenceBinding[] thrownExceptions; if (((thrownExceptions = this.binding.thrownExceptions).length) != 0) { if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6 thrownExceptions = currentScope.environment().convertToRawTypes(this.binding.thrownExceptions, true, true); } // check exception handling flowContext.checkExceptionHandlers( thrownExceptions, this, flowInfo.unconditionalCopy(), currentScope); } // after having analysed exceptions above start tracking newly allocated resource: if (currentScope.compilerOptions().analyseResourceLeaks && FakedTrackingVariable.isAnyCloseable(this.resolvedType)) FakedTrackingVariable.analyseCloseableAllocation(currentScope, flowInfo, this); if (this.binding.declaringClass.isMemberType() && !this.binding.declaringClass.isStatic()) { // allocating a non-static member type without an enclosing instance of parent type // https://bugs.eclipse.org/bugs/show_bug.cgi?id=335845 currentScope.resetDeclaringClassMethodStaticFlag(this.binding.declaringClass.enclosingType()); // Reviewed for https://bugs.eclipse.org/bugs/show_bug.cgi?id=378674 : // The corresponding problem (when called from static) is not produced until during code // generation } manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); manageSyntheticAccessIfNecessary(currentScope, flowInfo); // account for possible exceptions thrown by the constructor flowContext.recordAbruptExit(); // TODO whitelist of ctors that cannot throw any exc.?? return flowInfo; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // Consider the try block and catch block so as to compute the intersection of initializations // and // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append // its // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine // does not // complete, then only keep this result for the rest of the analysis // process the finally block (subroutine) - create a context for the subroutine this.preTryInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); if (this.anyExceptionVariable != null) { this.anyExceptionVariable.useFlag = LocalVariableBinding.USED; } if (this.returnAddressVariable != null) { // TODO (philippe) if subroutine is escaping, unused this.returnAddressVariable.useFlag = LocalVariableBinding.USED; } if (this.subRoutineStartLabel == null) { // no finally block -- this is a simplified copy of the else part // process the try block in a context handling the local exceptions. ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext( flowContext, this, this.caughtExceptionTypes, null, this.scope, flowInfo.unconditionalInits()); handlingContext.initsOnFinally = new NullInfoRegistry(flowInfo.unconditionalInits()); // only try blocks initialize that member - may consider creating a // separate class if needed FlowInfo tryInfo; if (this.tryBlock.isEmptyBlock()) { tryInfo = flowInfo; } else { tryInfo = this.tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy()); if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0) this.bits |= ASTNode.IsTryBlockExiting; } // check unreachable catch blocks handlingContext.complainIfUnusedExceptionHandlers(this.scope, this); // process the catch blocks - computing the minimal exit depth amongst try/catch if (this.catchArguments != null) { int catchCount; this.catchExits = new boolean[catchCount = this.catchBlocks.length]; this.catchExitInitStateIndexes = new int[catchCount]; for (int i = 0; i < catchCount; i++) { // keep track of the inits that could potentially have led to this exception handler (for // final assignments diagnosis) FlowInfo catchInfo; if (this.caughtExceptionTypes[i].isUncheckedException(true)) { catchInfo = handlingContext.initsOnFinally.mitigateNullInfoOf( flowInfo .unconditionalCopy() .addPotentialInitializationsFrom( handlingContext.initsOnException(this.caughtExceptionTypes[i])) .addPotentialInitializationsFrom(tryInfo) .addPotentialInitializationsFrom(handlingContext.initsOnReturn)); } else { FlowInfo initsOnException = handlingContext.initsOnException(this.caughtExceptionTypes[i]); catchInfo = flowInfo .nullInfoLessUnconditionalCopy() .addPotentialInitializationsFrom(initsOnException) .addNullInfoFrom( initsOnException) // null info only from here, this is the only way to enter // the catch block .addPotentialInitializationsFrom(tryInfo.nullInfoLessUnconditionalCopy()) .addPotentialInitializationsFrom( handlingContext.initsOnReturn.nullInfoLessUnconditionalCopy()); } // catch var is always set LocalVariableBinding catchArg = this.catchArguments[i].binding; catchInfo.markAsDefinitelyAssigned(catchArg); catchInfo.markAsDefinitelyNonNull(catchArg); /* "If we are about to consider an unchecked exception handler, potential inits may have occured inside the try block that need to be detected , e.g. try { x = 1; throwSomething();} catch(Exception e){ x = 2} " "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index]) ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]." */ if (this.tryBlock.statements == null) { catchInfo.setReachMode(FlowInfo.UNREACHABLE); } catchInfo = this.catchBlocks[i].analyseCode(currentScope, flowContext, catchInfo); this.catchExitInitStateIndexes[i] = currentScope.methodScope().recordInitializationStates(catchInfo); this.catchExits[i] = (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0; tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits()); } } this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(tryInfo); // chain up null info registry if (flowContext.initsOnFinally != null) { flowContext.initsOnFinally.add(handlingContext.initsOnFinally); } return tryInfo; } else { InsideSubRoutineFlowContext insideSubContext; FinallyFlowContext finallyContext; UnconditionalFlowInfo subInfo; // analyse finally block first insideSubContext = new InsideSubRoutineFlowContext(flowContext, this); subInfo = this.finallyBlock .analyseCode( currentScope, finallyContext = new FinallyFlowContext(flowContext, this.finallyBlock), flowInfo.nullInfoLessUnconditionalCopy()) .unconditionalInits(); if (subInfo == FlowInfo.DEAD_END) { this.bits |= ASTNode.IsSubRoutineEscaping; this.scope.problemReporter().finallyMustCompleteNormally(this.finallyBlock); } this.subRoutineInits = subInfo; // process the try block in a context handling the local exceptions. ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext( insideSubContext, this, this.caughtExceptionTypes, null, this.scope, flowInfo.unconditionalInits()); handlingContext.initsOnFinally = new NullInfoRegistry(flowInfo.unconditionalInits()); // only try blocks initialize that member - may consider creating a // separate class if needed FlowInfo tryInfo; if (this.tryBlock.isEmptyBlock()) { tryInfo = flowInfo; } else { tryInfo = this.tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy()); if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0) this.bits |= ASTNode.IsTryBlockExiting; } // check unreachable catch blocks handlingContext.complainIfUnusedExceptionHandlers(this.scope, this); // process the catch blocks - computing the minimal exit depth amongst try/catch if (this.catchArguments != null) { int catchCount; this.catchExits = new boolean[catchCount = this.catchBlocks.length]; this.catchExitInitStateIndexes = new int[catchCount]; for (int i = 0; i < catchCount; i++) { // keep track of the inits that could potentially have led to this exception handler (for // final assignments diagnosis) FlowInfo catchInfo; if (this.caughtExceptionTypes[i].isUncheckedException(true)) { catchInfo = handlingContext.initsOnFinally.mitigateNullInfoOf( flowInfo .unconditionalCopy() .addPotentialInitializationsFrom( handlingContext.initsOnException(this.caughtExceptionTypes[i])) .addPotentialInitializationsFrom(tryInfo) .addPotentialInitializationsFrom(handlingContext.initsOnReturn)); } else { FlowInfo initsOnException = handlingContext.initsOnException(this.caughtExceptionTypes[i]); catchInfo = flowInfo .nullInfoLessUnconditionalCopy() .addPotentialInitializationsFrom(initsOnException) .addNullInfoFrom( initsOnException) // null info only from here, this is the only way to enter // the catch block .addPotentialInitializationsFrom(tryInfo.nullInfoLessUnconditionalCopy()) .addPotentialInitializationsFrom( handlingContext.initsOnReturn.nullInfoLessUnconditionalCopy()); } // catch var is always set LocalVariableBinding catchArg = this.catchArguments[i].binding; catchInfo.markAsDefinitelyAssigned(catchArg); catchInfo.markAsDefinitelyNonNull(catchArg); /* "If we are about to consider an unchecked exception handler, potential inits may have occured inside the try block that need to be detected , e.g. try { x = 1; throwSomething();} catch(Exception e){ x = 2} " "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index]) ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]." */ if (this.tryBlock.statements == null) { catchInfo.setReachMode(FlowInfo.UNREACHABLE); } catchInfo = this.catchBlocks[i].analyseCode(currentScope, insideSubContext, catchInfo); this.catchExitInitStateIndexes[i] = currentScope.methodScope().recordInitializationStates(catchInfo); this.catchExits[i] = (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0; tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits()); } } // we also need to check potential multiple assignments of final variables inside the finally // block // need to include potential inits from returns inside the try/catch parts - 1GK2AOF finallyContext.complainOnDeferredChecks( handlingContext.initsOnFinally.mitigateNullInfoOf( (tryInfo.tagBits & FlowInfo.UNREACHABLE) == 0 ? flowInfo .unconditionalCopy() .addPotentialInitializationsFrom(tryInfo) . // lighten the influence of the try block, which may have // exited at any point addPotentialInitializationsFrom(insideSubContext.initsOnReturn) : insideSubContext.initsOnReturn), currentScope); // chain up null info registry if (flowContext.initsOnFinally != null) { flowContext.initsOnFinally.add(handlingContext.initsOnFinally); } this.naturalExitMergeInitStateIndex = currentScope.methodScope().recordInitializationStates(tryInfo); if (subInfo == FlowInfo.DEAD_END) { this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(subInfo); return subInfo; } else { FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } } }