@Contract("null -> null")
  public static ReflectiveType getReflectiveType(@Nullable PsiExpression context) {
    context = PsiUtil.skipParenthesizedExprDown(context);
    if (context == null) {
      return null;
    }
    if (context instanceof PsiClassObjectAccessExpression) {
      final PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand();
      return ReflectiveType.create(operand.getType(), true);
    }

    if (context instanceof PsiMethodCallExpression methodCall) {
      final String methodReferenceName = methodCall.getMethodExpression().getReferenceName();
      if (FOR_NAME.equals(methodReferenceName)) {
        final PsiMethod method = methodCall.resolveMethod();
        if (method != null && isJavaLangClass(method.getContainingClass())) {
          final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions();
          if (expressions.length == 1) {
            final PsiExpression argument = findDefinition(PsiUtil.skipParenthesizedExprDown(expressions[0]));
            final String className = computeConstantExpression(argument, String.class);
            if (className != null) {
              return ReflectiveType.create(findClass(className, context), true);
            }
          }
        }
      }
      else if (GET_CLASS.equals(methodReferenceName) && methodCall.getArgumentList().isEmpty()) {
        final PsiMethod method = methodCall.resolveMethod();
        if (method != null && isJavaLangObject(method.getContainingClass())) {
          final PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(methodCall.getMethodExpression().getQualifierExpression());
          if (qualifier instanceof PsiReferenceExpression) {
            final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)qualifier);
            if (definition != null) {
              return getClassInstanceType(definition);
            }
          }
          //TODO type of the qualifier may be a supertype of the actual value - need to compute the type of the actual value
          // otherwise getDeclaredField and getDeclaredMethod may work not reliably
          if (qualifier != null) {
            return getClassInstanceType(qualifier);
          }
        }
      }
    }

    if (context instanceof PsiReferenceExpression reference &&
        reference.resolve() instanceof PsiVariable variable &&
        isJavaLangClass(PsiTypesUtil.getPsiClass(variable.getType()))) {
      final PsiExpression definition = findVariableDefinition(reference, variable);
      if (definition != null) {
        ReflectiveType result = ourGuard.doPreventingRecursion(variable, false, () -> getReflectiveType(definition));
        if (result != null) {
          return result;
        }
      }
    }

    if (context.getType() instanceof PsiClassType type) {
      final PsiClassType.ClassResolveResult resolveResult = type.resolveGenerics();
      final PsiClass resolvedElement = resolveResult.getElement();
      if (!isJavaLangClass(resolvedElement)) return null;

      if (context instanceof PsiReferenceExpression ref && TYPE.equals(ref.getReferenceName()) && 
          ref.resolve() instanceof PsiField field &&
          field.hasModifierProperty(PsiModifier.FINAL) && field.hasModifierProperty(PsiModifier.STATIC)) {
        final PsiType[] classTypeArguments = type.getParameters();
        final PsiPrimitiveType unboxedType = classTypeArguments.length == 1
                                             ? PsiPrimitiveType.getUnboxedType(classTypeArguments[0]) : null;
        if (unboxedType != null && field.getContainingClass() == PsiUtil.resolveClassInClassTypeOnly(classTypeArguments[0])) {
          return ReflectiveType.create(unboxedType, true);
        }
      }
      final PsiTypeParameter[] parameters = resolvedElement.getTypeParameters();
      if (parameters.length == 1) {
        final PsiType typeArgument = resolveResult.getSubstitutor().substitute(parameters[0]);
        final PsiType erasure = TypeConversionUtil.erasure(typeArgument);
        final PsiClass argumentClass = PsiTypesUtil.getPsiClass(erasure);
        if (argumentClass != null && !isJavaLangObject(argumentClass)) {
          return ReflectiveType.create(argumentClass, false);
        }
      }
    }
    return null;
  }
@Contract("null -> null")
  public static ReflectiveType getReflectiveType(@Nullable PsiExpression context)