public static @NotNull TypeInfo fromString(@Nullable String text) {
    if (text == null) return TypeInfo.SimpleTypeInfo.NULL;
    TypeKind kind = TEXT_TO_KIND.get(text);
    if (kind != null) {
      return kind.isReference() ? new RefTypeInfo(text) : new SimpleTypeInfo(kind);
    }
    if (text.startsWith("? extends ")) {
      return new DerivedTypeInfo(TypeKind.EXTENDS, fromString(text.substring("? extends ".length())));
    }
    if (text.startsWith("? super ")) {
      return new DerivedTypeInfo(TypeKind.SUPER, fromString(text.substring("? super ".length())));
    }
    if (text.endsWith("[]")) {
      return fromString(text.substring(0, text.length() - 2)).arrayOf();
    }
    if (text.endsWith("...")) {
      return new DerivedTypeInfo(TypeKind.ELLIPSIS, fromString(text.substring(0, text.length() - 3)));
    }
    if (text.endsWith(">")) {
      int depth = 1;
      int end = text.length() - 1;
      List<TypeInfo> components = new ArrayList<>();
      for (int pos = end - 1; pos > 0; pos--) {
        char ch = text.charAt(pos);
        if (ch == '>') depth++;
        else if (ch == ',' && depth == 1) {
          String component = text.substring(pos + 1, end);
          end = pos;
          components.add(fromString(component));
        }
        else if (ch == '<') {
          depth--;
          if (depth == 0) {
            String component = text.substring(pos + 1, end);
            components.add(fromString(component));
            Collections.reverse(components);
            int prevGeneric = text.lastIndexOf('>', pos);
            RefTypeInfo outer;
            String name;
            if (prevGeneric > 0) {
              if (text.charAt(prevGeneric + 1) != '.') {
                throw new IllegalArgumentException("Malformed type: " + text);
              }
              outer = (RefTypeInfo)fromString(text.substring(0, prevGeneric + 1));
              name = text.substring(prevGeneric + 2, pos);
            } else {
              name = text.substring(0, pos);
              outer = null;
            }
            return new RefTypeInfo(name, outer, components.toArray(EMPTY_ARRAY));
          }
        }
      }
      throw new IllegalArgumentException("Malformed type: " + text);
    }
    return new RefTypeInfo(text);
  }
public static @NotNull TypeInfo fromString(@Nullable String text)