@Nullable
  static List<OperationRecord> extractMapForEach(PsiMethodCallExpression terminalCall) {
    if (!MAP_FOREACH.test(terminalCall) || !ExpressionUtils.isVoidContext(terminalCall)) return null;
    PsiExpression qualifier = ExpressionUtils.getEffectiveQualifier(terminalCall.getMethodExpression());
    if (qualifier == null) return null;
    // Do not visit this path if some class implements both Map and Stream
    PsiType type = qualifier.getType();
    if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) return null;
    PsiExpression arg = terminalCall.getArgumentList().getExpressions()[0];
    FunctionHelper fn = FunctionHelper.create(arg, 2, true);
    if (fn == null) return null;
    PsiType keyType = PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 0, false);
    PsiType valueType = PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 1, false);
    if (!isValidElementType(keyType, terminalCall, true) || !isValidElementType(valueType, terminalCall, true)) return null;
    Project project = terminalCall.getProject();
    JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
    PsiClass entryClass = facade.findClass(CommonClassNames.JAVA_UTIL_MAP_ENTRY, terminalCall.getResolveScope());
    if (entryClass == null || entryClass.getTypeParameters().length != 2) return null;
    PsiType entryType = JavaPsiFacade.getElementFactory(project).createType(entryClass, keyType, valueType);
    keyType = GenericsUtil.getVariableTypeByExpressionType(keyType);
    valueType = GenericsUtil.getVariableTypeByExpressionType(valueType);
    TerminalOperation terminal = new TerminalOperation.MapForEachTerminalOperation(fn, keyType, valueType);
    SourceOperation source = new SourceOperation.ForEachSource(qualifier, true);
    OperationRecord terminalRecord = new OperationRecord();
    OperationRecord sourceRecord = new OperationRecord();
    terminalRecord.myOperation = terminal;
    sourceRecord.myOperation = source;
    sourceRecord.myOutVar = terminalRecord.myInVar = new ChainVariable(entryType);
    sourceRecord.myInVar = terminalRecord.myOutVar = ChainVariable.STUB;
    return Arrays.asList(sourceRecord, terminalRecord);
  }
@Nullable
  static List<OperationRecord> extractMapForEach(PsiMethodCallExpression terminalCall)