PsiSubstitutor (PsiTypeParameter @NotNull [] typeParameters, PsiParameter @NotNull [] parameters, PsiExpression @NotNull [] arguments, @NotNull PsiSubstitutor partialSubstitutor, @NotNull PsiElement parent, @NotNull ParameterTypeInferencePolicy policy, @Nullable MethodCandidateInfo currentMethod) { final PsiExpressionList argumentList = InferenceSession.getArgumentList(parent); class NoContainer { PsiSubstitutor infer(boolean prohibitCaching) { final InferenceSession inferenceSession = new InferenceSession(typeParameters, partialSubstitutor, parent.getManager(), parent, policy); inferenceSession.initExpressionConstraints(parameters, arguments, currentMethod != null ? currentMethod.getElement() : null, currentMethod != null && currentMethod.isVarargs()); return inferenceSession.performGuardedInference(parameters, arguments, parent, currentMethod, PsiSubstitutor.EMPTY, prohibitCaching); } } if (parent instanceof PsiCall) { //overload resolution can't depend on outer call => should not traverse to top if (//in order to avoid caching of candidates' errors on parent (!) , so check for overload resolution is left here //But overload resolution can depend on type of lambda parameter. As it can't depend on lambda body, //traversing down would stop at lambda level and won't take into account overloaded method !MethodCandidateInfo.isOverloadCheck(argumentList)) { final PsiCall topLevelCall = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(parent, false, () -> { if (parent instanceof PsiExpression && !PsiPolyExpressionUtil.isPolyExpression((PsiExpression)parent)) { return null; } return LambdaUtil.treeWalkUp(parent); }); if (topLevelCall != null) { InferenceSession session; if (MethodCandidateInfo.isOverloadCheck() || !PsiDiamondType.ourDiamondGuard.currentStack().isEmpty() || !policy.equals(DefaultParameterTypeInferencePolicy.INSTANCE)) { session = startTopLevelInference(topLevelCall, policy, currentMethod); } else { session = CachedValuesManager.getCachedValue(topLevelCall, () -> new CachedValueProvider.Result<>( startTopLevelInference(topLevelCall, DefaultParameterTypeInferencePolicy.INSTANCE, null), PsiModificationTracker.MODIFICATION_COUNT)); if (session != null) { //reject cached top level session if it was based on wrong candidate: check nested session if candidate (it's type parameters) are the same //such situations are avoided when overload resolution is performed (MethodCandidateInfo.isOverloadCheck above) //but situations when client code iterates through PsiResolveHelper.getReferencedMethodCandidates or similar are impossible to guess final Map<PsiElement, InferenceSession> sessions = session.getInferenceSessionContainer().myNestedSessions; final InferenceSession childSession = sessions.get(parent); if (childSession != null) { for (PsiTypeParameter parameter : typeParameters) { if (!childSession.getInferenceSubstitution().getSubstitutionMap().containsKey(parameter)) { session = startTopLevelInference(topLevelCall, policy, currentMethod); break; } } } } } if (session != null) { final PsiSubstitutor childSubstitutor = inferNested(parameters, arguments, (PsiCall)parent, currentMethod, session); if (childSubstitutor != null) return childSubstitutor; return new NoContainer().infer(true); } else if (topLevelCall instanceof PsiMethodCallExpression) { return new InferenceSession(typeParameters, partialSubstitutor, parent.getManager(), parent, policy).prepareSubstitution(); } } } } return new NoContainer().infer(false); }