@Nullable
  public static PyType getType(@NotNull PsiElement resolved, @NotNull List<PyType> elementTypes) {
    final String qualifiedName = getQualifiedName(resolved);

    final List<Integer> paramListTypePositions = new ArrayList<>();
    final List<Integer> ellipsisTypePositions = new ArrayList<>();
    for (int i = 0; i < elementTypes.size(); i++) {
      final PyType type = elementTypes.get(i);
      if (type instanceof PyTypeParser.ParameterListType) {
        paramListTypePositions.add(i);
      } else if (type instanceof PyTypeParser.EllipsisType) {
        ellipsisTypePositions.add(i);
      }
    }

    if (!paramListTypePositions.isEmpty()) {
      if (!("typing.Callable".equals(qualifiedName) && paramListTypePositions.equals(list(0)))) {
        return null;
      }
    }
    if (!ellipsisTypePositions.isEmpty()) {
      if (!("typing.Callable".equals(qualifiedName) && ellipsisTypePositions.equals(list(0))
          || "typing.Tuple".equals(qualifiedName)
              && ellipsisTypePositions.equals(list(1))
              && elementTypes.size() == 2)) {
        return null;
      }
    }

    if ("typing.Union".equals(qualifiedName)) {
      return PyUnionType.union(elementTypes);
    }
    if ("typing.Optional".equals(qualifiedName) && elementTypes.size() == 1) {
      return PyUnionType.union(elementTypes.get(0), PyNoneType.INSTANCE);
    }
    if ("typing.Callable".equals(qualifiedName) && elementTypes.size() == 2) {
      final PyTypeParser.ParameterListType paramList =
          as(elementTypes.get(0), PyTypeParser.ParameterListType.class);
      if (paramList != null) {
        return new PyCallableTypeImpl(paramList.getCallableParameters(), elementTypes.get(1));
      }
      if (elementTypes.get(0) instanceof PyTypeParser.EllipsisType) {
        return new PyCallableTypeImpl(null, elementTypes.get(1));
      }
    }
    if ("typing.Tuple".equals(qualifiedName)) {
      if (elementTypes.size() > 1 && elementTypes.get(1) instanceof PyTypeParser.EllipsisType) {
        return PyTupleType.createHomogeneous(resolved, elementTypes.get(0));
      }
      return PyTupleType.create(resolved, elementTypes);
    }
    final PyType builtinCollection = getBuiltinCollection(resolved);
    if (builtinCollection instanceof PyClassType) {
      final PyClassType classType = (PyClassType) builtinCollection;
      return new PyCollectionTypeImpl(classType.getPyClass(), false, elementTypes);
    }
    return null;
  }
  public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
    if (!TypeEvalStack.mayEvaluate(this)) {
      return null;
    }
    try {
      final boolean qualified = isQualified();
      if (!qualified) {
        String name = getReferencedName();
        if (PyNames.NONE.equals(name)) {
          return PyNoneType.INSTANCE;
        }
      }
      PyType type = getTypeFromProviders(context);
      if (type != null) {
        return type;
      }
      if (qualified) {
        PyType maybe_type = PyUtil.getSpecialAttributeType(this, context);
        if (maybe_type != null) return maybe_type;
        Ref<PyType> typeOfProperty = getTypeOfProperty(context);
        if (typeOfProperty != null) {
          return typeOfProperty.get();
        }
      }
      final PsiPolyVariantReference reference =
          getReference(PyResolveContext.noImplicits().withTypeEvalContext(context));
      final List<PsiElement> targets = PyUtil.multiResolveTopPriority(reference);
      if (targets.isEmpty()) {
        return getQualifiedReferenceTypeByControlFlow(context);
      }

      final List<PyType> members = new ArrayList<PyType>();
      for (PsiElement target : targets) {
        if (target == this || target == null) {
          continue;
        }
        if (!target.isValid()) {
          LOG.error(
              "Reference "
                  + this
                  + " resolved to invalid element "
                  + target
                  + " (text="
                  + target.getText()
                  + ")");
          continue;
        }
        members.add(getTypeFromTarget(target, context, this));
      }

      return PyUnionType.union(members);
    } finally {
      TypeEvalStack.evaluated(this);
    }
  }
 @Nullable
 private static PyType getGenericConstructorType(
     @NotNull PyFunction function, @NotNull Context context) {
   if (PyUtil.isInit(function)) {
     final PyClass cls = function.getContainingClass();
     if (cls != null) {
       final List<PyGenericType> genericTypes = collectGenericTypes(cls, context);
       final List<PyType> elementTypes = new ArrayList<>(genericTypes);
       if (!elementTypes.isEmpty()) {
         return new PyCollectionTypeImpl(cls, false, elementTypes);
       }
     }
   }
   return null;
 }
 private static PyType getTypeByControlFlow(
     @NotNull String name,
     @NotNull TypeEvalContext context,
     @NotNull PyExpression anchor,
     @NotNull ScopeOwner scopeOwner) {
   PyAugAssignmentStatement augAssignment =
       PsiTreeUtil.getParentOfType(anchor, PyAugAssignmentStatement.class);
   try {
     final PyElement element = augAssignment != null ? augAssignment : anchor;
     final List<ReadWriteInstruction> defs =
         PyDefUseUtil.getLatestDefs(scopeOwner, name, element, true);
     if (!defs.isEmpty()) {
       PyType type = defs.get(0).getType(context, anchor);
       for (int i = 1; i < defs.size(); i++) {
         type = PyUnionType.union(type, defs.get(i).getType(context, anchor));
       }
       return type;
     }
   } catch (PyDefUseUtil.InstructionNotFoundException ignored) {
   }
   return null;
 }
 @NotNull
 private static List<PsiElement> tryResolving(
     @NotNull PyExpression expression, @NotNull TypeEvalContext context) {
   final List<PsiElement> elements = Lists.newArrayList();
   if (expression instanceof PyReferenceExpression) {
     final PyReferenceExpression referenceExpr = (PyReferenceExpression) expression;
     final PyResolveContext resolveContext =
         PyResolveContext.noImplicits().withTypeEvalContext(context);
     final PsiPolyVariantReference reference = referenceExpr.getReference(resolveContext);
     final List<PsiElement> resolved = PyUtil.multiResolveTopPriority(reference);
     for (PsiElement element : resolved) {
       if (element instanceof PyFunction) {
         final PyFunction function = (PyFunction) element;
         if (PyUtil.isInit(function)) {
           final PyClass cls = function.getContainingClass();
           if (cls != null) {
             elements.add(cls);
             continue;
           }
         }
       } else if (element instanceof PyTargetExpression) {
         final PyTargetExpression targetExpr = (PyTargetExpression) element;
         // XXX: Requires switching from stub to AST
         final PyExpression assignedValue = targetExpr.findAssignedValue();
         if (assignedValue != null) {
           elements.add(assignedValue);
           continue;
         }
       }
       if (element != null) {
         elements.add(element);
       }
     }
   }
   return !elements.isEmpty() ? elements : Collections.singletonList(expression);
 }