@Override protected Nullness create(MethodCallInstruction key) { final PsiCallExpression callExpression = key.getCallExpression(); if (callExpression instanceof PsiNewExpression) { return Nullness.NOT_NULL; } return callExpression != null ? DfaPsiUtil.getElementNullability( key.getResultType(), callExpression.resolveMethod()) : null; }
private DfaValue popQualifier( MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { @NotNull final DfaValue qualifier = memState.pop(); boolean unboxing = instruction.getMethodType() == MethodCallInstruction.MethodType.UNBOXING; NullabilityProblem problem = unboxing ? NullabilityProblem.unboxingNullable : NullabilityProblem.callNPE; PsiExpression anchor = unboxing ? instruction.getContext() : instruction.getCallExpression(); if (!checkNotNullable(memState, qualifier, problem, anchor)) { forceNotNull(runner, memState, qualifier); } return qualifier; }
@NotNull private DfaValue getMethodResultValue( MethodCallInstruction instruction, @Nullable DfaValue qualifierValue, DfaValueFactory factory) { DfaValue precalculated = instruction.getPrecalculatedReturnValue(); if (precalculated != null) { return precalculated; } final PsiType type = instruction.getResultType(); final MethodCallInstruction.MethodType methodType = instruction.getMethodType(); if (methodType == MethodCallInstruction.MethodType.UNBOXING) { return factory.getBoxedFactory().createUnboxed(qualifierValue); } if (methodType == MethodCallInstruction.MethodType.BOXING) { DfaValue boxed = factory.getBoxedFactory().createBoxed(qualifierValue); return boxed == null ? factory.createTypeValue(type, Nullness.NOT_NULL) : boxed; } if (methodType == MethodCallInstruction.MethodType.CAST) { assert qualifierValue != null; if (qualifierValue instanceof DfaConstValue) { Object casted = TypeConversionUtil.computeCastTo(((DfaConstValue) qualifierValue).getValue(), type); return factory .getConstFactory() .createFromValue(casted, type, ((DfaConstValue) qualifierValue).getConstant()); } return qualifierValue; } if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) { Nullness nullability = myReturnTypeNullability.get(instruction); if (nullability == Nullness.UNKNOWN && factory.isUnknownMembersAreNullable()) { nullability = Nullness.NULLABLE; } return factory.createTypeValue(type, nullability); } return DfaUnknownValue.getInstance(); }
@Nullable private DfaValue[] popCallArguments( MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final PsiExpression[] args = instruction.getArgs(); PsiMethod method = instruction.getTargetMethod(); boolean varargCall = instruction.isVarArgCall(); DfaValue[] argValues; if (method == null || instruction.getContracts().isEmpty()) { argValues = null; } else { int paramCount = method.getParameterList().getParametersCount(); if (paramCount == args.length || method.isVarArgs() && args.length >= paramCount - 1) { argValues = new DfaValue[paramCount]; if (varargCall) { argValues[paramCount - 1] = DfaUnknownValue.getInstance(); } } else { argValues = null; } } for (int i = 0; i < args.length; i++) { final DfaValue arg = memState.pop(); int paramIndex = args.length - i - 1; if (argValues != null && (paramIndex < argValues.length - 1 || !varargCall)) { argValues[paramIndex] = arg; } PsiExpression expr = args[paramIndex]; Nullness requiredNullability = instruction.getArgRequiredNullability(expr); if (requiredNullability == Nullness.NOT_NULL) { if (!checkNotNullable( memState, arg, NullabilityProblem.passingNullableToNotNullParameter, expr)) { forceNotNull(runner, memState, arg); } } else if (requiredNullability == Nullness.UNKNOWN) { checkNotNullable( memState, arg, NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter, expr); } } return argValues; }
private DfaValue getDfaContractReturnValue( MethodContract contract, MethodCallInstruction instruction, DfaValueFactory factory) { switch (contract.returnValue) { case NULL_VALUE: return factory.getConstFactory().getNull(); case NOT_NULL_VALUE: return factory.createTypeValue(instruction.getResultType(), Nullness.NOT_NULL); case TRUE_VALUE: return factory.getConstFactory().getTrue(); case FALSE_VALUE: return factory.getConstFactory().getFalse(); case THROW_EXCEPTION: return factory.getConstFactory().getContractFail(); default: return getMethodResultValue(instruction, null, factory); } }
@Override public DfaInstructionState[] visitMethodCall( final MethodCallInstruction instruction, final DataFlowRunner runner, final DfaMemoryState memState) { DfaValue[] argValues = popCallArguments(instruction, runner, memState); final DfaValue qualifier = popQualifier(instruction, runner, memState); List<DfaMemoryState> currentStates = ContainerUtil.newArrayList(memState); Set<DfaMemoryState> finalStates = ContainerUtil.newLinkedHashSet(); if (argValues != null) { for (MethodContract contract : instruction.getContracts()) { currentStates = addContractResults( argValues, contract, currentStates, instruction, runner.getFactory(), finalStates); } } for (DfaMemoryState state : currentStates) { state.push(getMethodResultValue(instruction, qualifier, runner.getFactory())); finalStates.add(state); } return ContainerUtil.map2Array( finalStates, DfaInstructionState.class, new Function<DfaMemoryState, DfaInstructionState>() { @Override public DfaInstructionState fun(DfaMemoryState state) { if (instruction.shouldFlushFields()) { state.flushFields(); } return new DfaInstructionState( runner.getInstruction(instruction.getIndex() + 1), state); } }); }