@Nullable
  private static Ref<PyType> getTypeForResolvedElement(@NotNull PyExpression typeHint,
                                                       @Nullable PyQualifiedNameOwner alias,
                                                       @NotNull PsiElement resolved,
                                                       @NotNull Context context) {
    if (alias != null) {
      if (context.getTypeAliasStack().contains(alias)) {
        // Recursive types are not yet supported
        return null;
      }
      context.getTypeAliasStack().add(alias);
    }
    try {
      final Ref<PyType> typeFromParenthesizedExpression = getTypeFromParenthesizedExpression(resolved, context);
      if (typeFromParenthesizedExpression != null) {
        return typeFromParenthesizedExpression;
      }
      final PyType unionType = getUnionType(resolved, context);
      if (unionType != null) {
        return Ref.create(unionType);
      }
      final PyType concatenateType = getConcatenateType(resolved, context);
      if (concatenateType != null) {
        return Ref.create(concatenateType);
      }
      final Ref<PyType> optionalType = getOptionalType(resolved, context);
      if (optionalType != null) {
        return optionalType;
      }
      final PyType callableType = getCallableType(resolved, context);
      if (callableType != null) {
        return Ref.create(callableType);
      }
      final Ref<PyType> classVarType = unwrapTypeModifier(resolved, context, CLASS_VAR);
      if (classVarType != null) {
        return classVarType;
      }
      final Ref<PyType> classObjType = getClassObjectType(resolved, context);
      if (classObjType != null) {
        return classObjType;
      }
      final Ref<PyType> finalType = unwrapTypeModifier(resolved, context, FINAL, FINAL_EXT);
      if (finalType != null) {
        return finalType;
      }
      final Ref<PyType> annotatedType = getAnnotatedType(resolved, context);
      if (annotatedType != null) {
        return annotatedType;
      }
      final Ref<PyType> requiredOrNotRequiredType = getRequiredOrNotRequiredType(resolved, context);
      if (requiredOrNotRequiredType != null) {
        return requiredOrNotRequiredType;
      }
      final Ref<PyType> literalStringType = getLiteralStringType(resolved, context);
      if (literalStringType != null) {
        return literalStringType;
      }
      final Ref<PyType> literalType = getLiteralType(resolved, context);
      if (literalType != null) {
        return literalType;
      }
      final Ref<PyType> typeAliasType = getExplicitTypeAliasType(resolved);
      if (typeAliasType != null) {
        return typeAliasType;
      }
      final PyType parameterizedType = getParameterizedType(resolved, context);
      if (parameterizedType != null) {
        return Ref.create(parameterizedType);
      }
      final PyType collection = getCollection(resolved, context.getTypeContext());
      if (collection != null) {
        return Ref.create(collection);
      }
      final PyType typeParameter = getTypeParameterTypeFromDeclaration(resolved, context);
      if (typeParameter != null) {
        return Ref.create(anchorTypeParameter(typeHint, typeParameter, context));
      }
      final PyType unpackedType = getUnpackedType(resolved, context.getTypeContext());
      if (unpackedType != null) {
        return Ref.create(unpackedType);
      }
      final PyType typeParameterType = getTypeParameterTypeFromTypeParameter(resolved, context);
      if (typeParameterType != null) {
        return Ref.create(typeParameterType);
      }
      final PyType paramSpecType = getParamSpecType(resolved, context);
      if (paramSpecType != null) {
        return Ref.create(anchorTypeParameter(typeHint, paramSpecType, context));
      }
      final PyType stringBasedType = getStringLiteralType(resolved, context);
      if (stringBasedType != null) {
        return Ref.create(stringBasedType);
      }
      final Ref<PyType> anyType = getAnyType(resolved);
      if (anyType != null) {
        return anyType;
      }
      // We perform chained resolve only for actual aliases as tryResolvingWithAliases() returns the passed-in
      // expression both when it's not a reference expression and when it's failed to resolve it, hence we might
      // hit SOE for mere unresolved references in the latter case.
      if (alias != null) {
        final Ref<PyType> aliasedType = getAliasedType(resolved, context);
        if (aliasedType != null) {
          return aliasedType;
        }
      }
      final PyType typedDictType = PyTypedDictTypeProvider.Companion.getTypedDictTypeForResolvedElement(resolved, context.getTypeContext());
      if (typedDictType != null) {
        return Ref.create(typedDictType);
      }
      final Ref<PyType> classType = getClassType(resolved, context.getTypeContext());
      if (classType != null) {
        return classType;
      }
      final Ref<PyType> unionTypeFromBinaryOr = getTypeFromBinaryExpression(resolved, context);
      if (unionTypeFromBinaryOr != null) {
        return unionTypeFromBinaryOr;
      }
      final Ref<PyType> selfType = getSelfType(resolved, typeHint, context);
      if (selfType != null) {
        return selfType;
      }
      return null;
    }
    finally {
      if (alias != null) {
        context.getTypeAliasStack().remove(alias);
      }
    }
  }
@Nullable
  private static Ref<PyType> getTypeForResolvedElement(@NotNull PyExpression typeHint,
                                                       @Nullable PyQualifiedNameOwner alias,
                                                       @NotNull PsiElement resolved,
                                                       @NotNull Context context)