@Nullable
  public static NullabilityProblem<?> fromContext(@NotNull PsiExpression expression,
                                                  Map<PsiExpression, NullabilityProblemKind<? super PsiExpression>> customNullabilityProblems) {
    if (TypeConversionUtil.isPrimitiveAndNotNull(expression.getType()) ||
        expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).resolve() instanceof PsiClass) {
      return null;
    }
    PsiExpression context = findTopExpression(expression);
    NullabilityProblemKind<? super PsiExpression> kind = customNullabilityProblems.get(context);
    if (kind != null) {
      return kind.problem(context, expression);
    }
    PsiElement parent = context.getParent();
    if (parent instanceof PsiReferenceExpression ref) {
      if (ref.resolve() instanceof PsiMember member && member.hasModifierProperty(PsiModifier.STATIC)) {
        return null;
      }
      PsiElement grandParent = parent.getParent();
      if (grandParent instanceof PsiMethodCallExpression) {
        PsiParameter parameter = MethodCallUtils.getParameterForArgument(context);
        if (parameter != null) {
          return getParameterProblem(parameter, expression, context);
        }
        return callNPE.problem((PsiMethodCallExpression)grandParent, expression);
      }
      return fieldAccessNPE.problem(context, expression);
    }
    PsiType targetType = null;
    if (parent instanceof PsiLambdaExpression lambda) {
      targetType = LambdaUtil.getFunctionalInterfaceReturnType(lambda);
    }
    else if (parent instanceof PsiReturnStatement) {
      targetType = PsiTypesUtil.getMethodReturnType(parent);
    }
    if (targetType != null && !PsiTypes.voidType().equals(targetType)) {
      if (TypeConversionUtil.isPrimitiveAndNotNull(targetType)) {
        return createUnboxingProblem(context, expression);
      }
      return nullableReturn.problem(context, expression);
    }
    if (parent instanceof PsiVariable var) {
      if (var.getType() instanceof PsiPrimitiveType) {
        return createUnboxingProblem(context, expression);
      }
      Nullability nullability = DfaPsiUtil.getElementNullability(var.getType(), var);
      if (nullability == Nullability.NOT_NULL) {
        return assigningToNotNull.problem(context, expression);
      }
    }
    if (parent instanceof PsiAssignmentExpression assignment) {
      return getAssignmentProblem(assignment, expression, context);
    }
    if (parent instanceof PsiTemplateExpression templateExpression && templateExpression.getProcessor() == expression) {
      return templateNPE.problem(templateExpression, expression);
    }
    if (parent instanceof PsiExpressionList expressionList) {
      return getExpressionListProblem(expressionList, expression, context);
    }
    if (parent instanceof PsiArrayInitializerExpression arrayInitializer) {
      return getArrayInitializerProblem(arrayInitializer, expression, context);
    }
    if (parent instanceof PsiTypeCastExpression) {
      if (TypeConversionUtil.isAssignableFromPrimitiveWrapper(context.getType())) {
        // Only casts to primitives are here; casts to objects were already traversed by findTopExpression
        return unboxingNullable.problem(context, expression);
      }
    }
    else if (parent instanceof PsiIfStatement || parent instanceof PsiWhileStatement || parent instanceof PsiDoWhileStatement ||
             parent instanceof PsiUnaryExpression || parent instanceof PsiConditionalExpression ||
             (parent instanceof PsiForStatement forStatement && forStatement.getCondition() == context) ||
             (parent instanceof PsiAssertStatement assertStatement && assertStatement.getAssertCondition() == context)) {
      return createUnboxingProblem(context, expression);
    }
    if (parent instanceof PsiSwitchBlock switchBlock) {
      return getSwitchBlockProblem(switchBlock, expression, context);
    }
    if (parent instanceof PsiForeachStatement || parent instanceof PsiThrowStatement ||
        parent instanceof PsiSynchronizedStatement) {
      return fieldAccessNPE.problem(context, expression);
    }
    if (parent instanceof PsiNewExpression newExpression) {
      return newExpression.getQualifier() == context ?
             innerClassNPE.problem(newExpression, expression) :
             createUnboxingProblem(context, expression); // Array dimension
    }
    if (parent instanceof PsiPolyadicExpression polyadic) {
      IElementType type = polyadic.getOperationTokenType();
      boolean noUnboxing = (type == JavaTokenType.PLUS && TypeUtils.isJavaLangString(polyadic.getType())) ||
                           ((type == JavaTokenType.EQEQ || type == JavaTokenType.NE) &&
                            !ContainerUtil.exists(polyadic.getOperands(),
                                                  op -> TypeConversionUtil.isPrimitiveAndNotNull(op.getType())));
      if (!noUnboxing) {
        return createUnboxingProblem(context, expression);
      }
    }
    if (parent instanceof PsiArrayAccessExpression arrayAccessExpression) {
      if (arrayAccessExpression.getArrayExpression() == context) {
        return arrayAccessNPE.problem(arrayAccessExpression, expression);
      }
      return createUnboxingProblem(context, expression);
    }
    return null;
  }
@Nullable
  public static NullabilityProblem<?> fromContext(@NotNull PsiExpression expression,
                                                  Map<PsiExpression, NullabilityProblemKind<? super PsiExpression>> customNullabilityProblems)