@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;
  }
 @Nullable
 private static PyType getParameterizedType(
     @NotNull PsiElement element, @NotNull Context context) {
   if (element instanceof PySubscriptionExpression) {
     final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression) element;
     final PyExpression operand = subscriptionExpr.getOperand();
     final PyExpression indexExpr = subscriptionExpr.getIndexExpression();
     final PyType operandType = getType(operand, context);
     if (operandType instanceof PyClassType) {
       final PyClass cls = ((PyClassType) operandType).getPyClass();
       final List<PyType> indexTypes = getIndexTypes(subscriptionExpr, context);
       if (PyNames.TUPLE.equals(cls.getQualifiedName())) {
         if (indexExpr instanceof PyTupleExpression) {
           final PyExpression[] elements = ((PyTupleExpression) indexExpr).getElements();
           if (elements.length == 2 && isEllipsis(elements[1])) {
             return PyTupleType.createHomogeneous(element, indexTypes.get(0));
           }
         }
         return PyTupleType.create(element, indexTypes);
       } else if (indexExpr != null) {
         return new PyCollectionTypeImpl(cls, false, indexTypes);
       }
     }
   }
   return null;
 }
 @Nullable
 private static PyType getCallableType(@NotNull PsiElement resolved, @NotNull Context context) {
   if (resolved instanceof PySubscriptionExpression) {
     final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression) resolved;
     final PyExpression operand = subscriptionExpr.getOperand();
     final Collection<String> operandNames =
         resolveToQualifiedNames(operand, context.getTypeContext());
     if (operandNames.contains("typing.Callable")) {
       final PyExpression indexExpr = subscriptionExpr.getIndexExpression();
       if (indexExpr instanceof PyTupleExpression) {
         final PyTupleExpression tupleExpr = (PyTupleExpression) indexExpr;
         final PyExpression[] elements = tupleExpr.getElements();
         if (elements.length == 2) {
           final PyExpression parametersExpr = elements[0];
           final PyExpression returnTypeExpr = elements[1];
           if (parametersExpr instanceof PyListLiteralExpression) {
             final List<PyCallableParameter> parameters = new ArrayList<>();
             final PyListLiteralExpression listExpr = (PyListLiteralExpression) parametersExpr;
             for (PyExpression argExpr : listExpr.getElements()) {
               parameters.add(new PyCallableParameterImpl(null, getType(argExpr, context)));
             }
             final PyType returnType = getType(returnTypeExpr, context);
             return new PyCallableTypeImpl(parameters, returnType);
           }
           if (isEllipsis(parametersExpr)) {
             return new PyCallableTypeImpl(null, getType(returnTypeExpr, context));
           }
         }
       }
     }
   }
   return null;
 }
 @Nullable
 private PyType resolveQualifierType(
     @NotNull List<Token<PyElementType>> tokens,
     @NotNull PyFile file,
     @NotNull TypeEvalContext context,
     @NotNull Map<TextRange, PyType> types,
     @NotNull Map<PyType, TextRange> fullRanges,
     @NotNull Map<PyType, PyImportElement> imports) {
   if (tokens.isEmpty()) {
     return null;
   }
   final Token<PyElementType> firstToken = tokens.get(0);
   final String firstText = firstToken.getText().toString();
   final TextRange firstRange = firstToken.getRange();
   final List<RatedResolveResult> resolveResults = file.multiResolveName(firstText);
   if (resolveResults.isEmpty()) {
     return getImplicitlyResolvedType(tokens, context, types, fullRanges, firstRange);
   }
   final List<PyType> members = Lists.newArrayList();
   for (RatedResolveResult result : resolveResults) {
     final PsiElement resolved = result.getElement();
     PyType type = null;
     if (resolved instanceof PyTargetExpression) {
       type =
           PyTypingTypeProvider.getTypeFromTargetExpression(
               (PyTargetExpression) resolved, context);
     }
     if (type == null && resolved instanceof PyTypedElement) {
       type = context.getType((PyTypedElement) resolved);
     }
     if (type != null) {
       if (!allowResolveToType(type)) {
         continue;
       }
       if (type instanceof PyClassLikeType) {
         type = ((PyClassLikeType) type).toInstance();
       }
       types.put(firstRange, type);
       fullRanges.put(type, firstRange);
       for (PyFromImportStatement fromImportStatement : file.getFromImports()) {
         for (PyImportElement importElement : fromImportStatement.getImportElements()) {
           if (firstText.equals(importElement.getVisibleName())) {
             imports.put(type, importElement);
           }
         }
       }
       for (PyImportElement importElement : file.getImportTargets()) {
         if (firstText.equals(importElement.getVisibleName())) {
           imports.put(type, importElement);
         }
       }
     }
     members.add(type);
   }
   if (!members.isEmpty()) {
     tokens.remove(0);
   }
   return PyUnionType.union(members);
 }
 @Nullable
 private static PyType getType(@NotNull PyExpression expression, @NotNull Context context) {
   final List<PyType> members = Lists.newArrayList();
   for (PsiElement resolved : tryResolving(expression, context.getTypeContext())) {
     members.add(getTypeForResolvedElement(resolved, context));
   }
   return PyUnionType.union(members);
 }
 @Nullable
 private static PyType getGenericTypeBound(
     @NotNull PyExpression[] typeVarArguments, @NotNull Context context) {
   final List<PyType> types = new ArrayList<>();
   for (int i = 1; i < typeVarArguments.length; i++) {
     types.add(getType(typeVarArguments[i], context));
   }
   return PyUnionType.union(types);
 }
  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);
    }
  }
 public PyInplaceVariableIntroducer(
     PyTargetExpression target, IntroduceOperation operation, List<PsiElement> occurrences) {
   super(
       target,
       operation.getEditor(),
       operation.getProject(),
       "Introduce Variable",
       occurrences.toArray(new PsiElement[occurrences.size()]),
       null);
   myTarget = target;
 }
 @NotNull
 public QualifiedResolveResult followAssignmentsChain(PyResolveContext resolveContext) {
   PyReferenceExpression seeker = this;
   QualifiedResolveResult ret = null;
   List<PyExpression> qualifiers = new ArrayList<PyExpression>();
   PyExpression qualifier = seeker.getQualifier();
   if (qualifier != null) {
     qualifiers.add(qualifier);
   }
   Set<PsiElement> visited = new HashSet<PsiElement>();
   visited.add(this);
   SEARCH:
   while (ret == null) {
     ResolveResult[] targets = seeker.getReference(resolveContext).multiResolve(false);
     for (ResolveResult target : targets) {
       PsiElement elt = target.getElement();
       if (elt instanceof PyTargetExpression) {
         PsiElement assigned_from = null;
         final PyTargetExpression expr = (PyTargetExpression) elt;
         final TypeEvalContext context = resolveContext.getTypeEvalContext();
         if (context.maySwitchToAST(expr) || expr.getStub() == null) {
           assigned_from = expr.findAssignedValue();
         }
         // TODO: Maybe findAssignedValueByStub() should become a part of the PyTargetExpression
         // interface
         else if (elt instanceof PyTargetExpressionImpl) {
           assigned_from = ((PyTargetExpressionImpl) elt).findAssignedValueByStub(context);
         }
         if (assigned_from instanceof PyReferenceExpression) {
           if (visited.contains(assigned_from)) {
             break;
           }
           visited.add(assigned_from);
           seeker = (PyReferenceExpression) assigned_from;
           if (seeker.getQualifier() != null) {
             qualifiers.add(seeker.getQualifier());
           }
           continue SEARCH;
         } else if (assigned_from != null)
           ret = new QualifiedResolveResultImpl(assigned_from, qualifiers, false);
       } else if (ret == null && elt instanceof PyElement && target.isValidResult()) {
         // remember this result, but a further reference may be the next resolve result
         ret =
             new QualifiedResolveResultImpl(
                 elt, qualifiers, target instanceof ImplicitResolveResult);
       }
     }
     // all resolve results checked, reassignment not detected, nothing more to do
     break;
   }
   if (ret == null) ret = EMPTY_RESULT;
   return ret;
 }
 @NotNull
 private static List<PyType> getIndexTypes(
     @NotNull PySubscriptionExpression expression, @NotNull Context context) {
   final List<PyType> types = new ArrayList<>();
   final PyExpression indexExpr = expression.getIndexExpression();
   if (indexExpr instanceof PyTupleExpression) {
     final PyTupleExpression tupleExpr = (PyTupleExpression) indexExpr;
     for (PyExpression expr : tupleExpr.getElements()) {
       types.add(getType(expr, context));
     }
   } else if (indexExpr != null) {
     types.add(getType(indexExpr, context));
   }
   return types;
 }
 @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;
 }
 @Nullable
 protected static PsiElement findOccurrenceUnderCaret(
     List<PsiElement> occurrences, Editor editor) {
   if (occurrences.isEmpty()) {
     return null;
   }
   int offset = editor.getCaretModel().getOffset();
   for (PsiElement occurrence : occurrences) {
     if (occurrence != null && occurrence.getTextRange().contains(offset)) {
       return occurrence;
     }
   }
   int line = editor.getDocument().getLineNumber(offset);
   for (PsiElement occurrence : occurrences) {
     if (occurrence.isValid()
         && editor.getDocument().getLineNumber(occurrence.getTextRange().getStartOffset())
             == line) {
       return occurrence;
     }
   }
   for (PsiElement occurrence : occurrences) {
     if (occurrence.isValid()) {
       return occurrence;
     }
   }
   return null;
 }
  @Nullable
  public Ref<PyType> getParameterType(
      @NotNull PyNamedParameter param, @NotNull PyFunction func, @NotNull TypeEvalContext context) {
    final PyAnnotation annotation = param.getAnnotation();
    if (annotation != null) {
      // XXX: Requires switching from stub to AST
      final PyExpression value = annotation.getValue();
      if (value != null) {
        final PyType type = getType(value, new Context(context));
        if (type != null) {
          final PyType optionalType = getOptionalTypeFromDefaultNone(param, type, context);
          return Ref.create(optionalType != null ? optionalType : type);
        }
      }
    }

    final String paramComment = param.getTypeCommentAnnotation();
    if (paramComment != null) {
      return Ref.create(getStringBasedType(paramComment, param, new Context(context)));
    }

    final String comment = func.getTypeCommentAnnotation();
    if (comment != null) {
      final PyTypeParser.ParseResult result =
          PyTypeParser.parsePep484FunctionTypeComment(param, comment);
      final PyCallableType functionType = as(result.getType(), PyCallableType.class);
      if (functionType != null) {
        final List<PyCallableParameter> paramTypes = functionType.getParameters(context);
        // Function annotation of kind (...) -> Type
        if (paramTypes == null) {
          return Ref.create();
        }
        final PyParameter[] funcParams = func.getParameterList().getParameters();
        final int startOffset = omitFirstParamInTypeComment(func) ? 1 : 0;
        for (int paramIndex = 0; paramIndex < funcParams.length; paramIndex++) {
          if (funcParams[paramIndex] == param) {
            final int typeIndex = paramIndex - startOffset;
            if (typeIndex >= 0 && typeIndex < paramTypes.size()) {
              return Ref.create(paramTypes.get(typeIndex).getType(context));
            }
            break;
          }
        }
      }
    }
    return null;
  }
  /**
   * Moves methods (as opposite to {@link #makeMethodsAbstract(java.util.Collection,
   * com.jetbrains.python.psi.PyClass...)})
   *
   * @param from source
   * @param methodsToMove what to move
   * @param to where
   * @return newly added methods
   */
  private static List<PyElement> moveMethods(
      final PyClass from, final Collection<PyFunction> methodsToMove, final PyClass... to) {
    final List<PyElement> result = new ArrayList<PyElement>();
    for (final PyClass destClass : to) {
      // We move copies here because there may be several destinations
      final List<PyFunction> copies = new ArrayList<PyFunction>(methodsToMove.size());
      for (final PyFunction element : methodsToMove) {
        final PyFunction newMethod = (PyFunction) element.copy();
        copies.add(newMethod);
      }

      result.addAll(PyClassRefactoringUtil.copyMethods(copies, destClass));
    }
    deleteElements(methodsToMove);

    PyClassRefactoringUtil.insertPassIfNeeded(from);
    return result;
  }
 private boolean smartIntroduce(final IntroduceOperation operation) {
   final Editor editor = operation.getEditor();
   final PsiFile file = operation.getFile();
   int offset = editor.getCaretModel().getOffset();
   PsiElement elementAtCaret = file.findElementAt(offset);
   if (!checkIntroduceContext(file, editor, elementAtCaret)) return true;
   final List<PyExpression> expressions = new ArrayList<PyExpression>();
   while (elementAtCaret != null) {
     if (elementAtCaret instanceof PyStatement || elementAtCaret instanceof PyFile) {
       break;
     }
     if (elementAtCaret instanceof PyExpression && isValidIntroduceVariant(elementAtCaret)) {
       expressions.add((PyExpression) elementAtCaret);
     }
     elementAtCaret = elementAtCaret.getParent();
   }
   if (expressions.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) {
     operation.setElement(expressions.get(0));
     performActionOnElement(operation);
     return true;
   } else if (expressions.size() > 1) {
     IntroduceTargetChooser.showChooser(
         editor,
         expressions,
         new Pass<PyExpression>() {
           @Override
           public void pass(PyExpression pyExpression) {
             operation.setElement(pyExpression);
             performActionOnElement(operation);
           }
         },
         new Function<PyExpression, String>() {
           public String fun(PyExpression pyExpression) {
             return pyExpression.getText();
           }
         });
     return true;
   }
   return false;
 }
 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;
 }
 /** @param self should be this */
 @NotNull
 private static List<PyAssignmentStatement> findAttributesStatic(@NotNull final PsiElement self) {
   final List<PyAssignmentStatement> result = new ArrayList<>();
   for (final PyAssignmentStatement statement :
       new PsiQuery(self).siblings(PyAssignmentStatement.class).getElements()) {
     for (final PyQualifiedExpression targetExpression :
         new PsiQuery(statement.getTargets()).filter(PyQualifiedExpression.class).getElements()) {
       final PyExpression qualifier = targetExpression.getQualifier();
       if (qualifier == null) {
         continue;
       }
       final PsiReference qualifierReference = qualifier.getReference();
       if (qualifierReference == null) {
         continue;
       }
       if (qualifierReference.isReferenceTo(self)) {
         result.add(statement);
       }
     }
   }
   return result;
 }
 @Nullable
 private Modifier getWrappersFromStub() {
   final StubElement parentStub = getStub().getParentStub();
   final List childrenStubs = parentStub.getChildrenStubs();
   int index = childrenStubs.indexOf(getStub());
   if (index >= 0 && index < childrenStubs.size() - 1) {
     StubElement nextStub = (StubElement) childrenStubs.get(index + 1);
     if (nextStub instanceof PyTargetExpressionStub) {
       final PyTargetExpressionStub targetExpressionStub = (PyTargetExpressionStub) nextStub;
       if (targetExpressionStub.getInitializerType()
           == PyTargetExpressionStub.InitializerType.CallExpression) {
         final QualifiedName qualifiedName = targetExpressionStub.getInitializer();
         if (QualifiedName.fromComponents(PyNames.CLASSMETHOD).equals(qualifiedName)) {
           return CLASSMETHOD;
         }
         if (QualifiedName.fromComponents(PyNames.STATICMETHOD).equals(qualifiedName)) {
           return STATICMETHOD;
         }
       }
     }
   }
   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);
 }
 protected static PsiElement findAnchor(List<PsiElement> occurrences) {
   PsiElement anchor = occurrences.get(0);
   next:
   do {
     final PyStatement statement = PsiTreeUtil.getParentOfType(anchor, PyStatement.class);
     if (statement != null) {
       final PsiElement parent = statement.getParent();
       for (PsiElement element : occurrences) {
         if (!PsiTreeUtil.isAncestor(parent, element, true)) {
           anchor = statement;
           continue next;
         }
       }
     }
     return statement;
   } while (true);
 }
 @Nullable
 private String extractReturnType() {
   final String ARROW = "->";
   final StructuredDocString structuredDocString = getStructuredDocString();
   if (structuredDocString != null) {
     return structuredDocString.getReturnType();
   }
   final String docString = getDocStringValue();
   if (docString != null && docString.contains(ARROW)) {
     final List<String> lines = StringUtil.split(docString, "\n");
     while (lines.size() > 0 && lines.get(0).trim().length() == 0) {
       lines.remove(0);
     }
     if (lines.size() > 1 && lines.get(1).trim().length() == 0) {
       String firstLine = lines.get(0);
       int pos = firstLine.lastIndexOf(ARROW);
       if (pos >= 0) {
         return firstLine.substring(pos + 2).trim();
       }
     }
   }
   return null;
 }
  /** @param anchor should never be null or null will be returned */
  @NotNull
  public static ParseResult parse(@Nullable final PsiElement anchor, @NotNull String type) {
    PyPsiUtils.assertValid(anchor);
    if (anchor == null) {
      return EMPTY_RESULT;
    }

    final ForwardDeclaration<ParseResult, PyElementType> typeExpr = ForwardDeclaration.create();

    final FunctionalParser<ParseResult, PyElementType> classType =
        token(IDENTIFIER)
            .then(many(op(".").skipThen(token(IDENTIFIER))))
            .map(new MakeSimpleType(anchor))
            .cached()
            .named("class-type");

    final FunctionalParser<ParseResult, PyElementType> tupleType =
        op("(")
            .skipThen(typeExpr)
            .then(many(op(",").skipThen(typeExpr)))
            .thenSkip(op(")"))
            .map(
                value -> {
                  ParseResult result = value.getFirst();
                  final List<ParseResult> rest = value.getSecond();
                  if (rest.isEmpty()) {
                    return result;
                  }
                  final List<PyType> types = new ArrayList<>();
                  types.add(result.getType());
                  for (ParseResult r : rest) {
                    result = result.merge(r);
                    types.add(r.getType());
                  }
                  return result.withType(PyTupleType.create(anchor, types));
                })
            .named("tuple-type");

    final FunctionalParser<ParseResult, PyElementType> typeParameter =
        token(PARAMETER)
            .then(maybe(op("<=").skipThen(typeExpr)))
            .map(
                value -> {
                  final Token<PyElementType> token = value.getFirst();
                  final String name = token.getText().toString();
                  final TextRange range = token.getRange();
                  final ParseResult boundResult = value.getSecond();
                  if (boundResult != null) {
                    final PyGenericType type1 = new PyGenericType(name, boundResult.getType());
                    final ParseResult result = new ParseResult(null, type1, range);
                    return result.merge(boundResult).withType(type1);
                  }
                  return new ParseResult(null, new PyGenericType(name, null), range);
                })
            .named("type-parameter");

    final FunctionalParser<ParseResult, PyElementType> simpleExpr =
        classType.or(tupleType).or(typeParameter).named("simple-expr");

    final FunctionalParser<ParseResult, PyElementType> paramExpr =
        classType
            .thenSkip(op("["))
            .then(typeExpr)
            .then(many(op(",").skipThen(typeExpr)))
            .thenSkip(op("]"))
            .map(
                value -> {
                  final Pair<ParseResult, ParseResult> firstPair = value.getFirst();
                  final ParseResult first = firstPair.getFirst();
                  final ParseResult second = firstPair.getSecond();
                  final List<ParseResult> third = value.getSecond();
                  final PyType firstType = first.getType();
                  final List<PyType> typesInBrackets = new ArrayList<>();
                  typesInBrackets.add(second.getType());
                  ParseResult result = first;
                  result = result.merge(second);
                  for (ParseResult r : third) {
                    typesInBrackets.add(r.getType());
                    result = result.merge(r);
                  }
                  final List<PyType> elementTypes =
                      third.isEmpty()
                          ? Collections.singletonList(second.getType())
                          : typesInBrackets;
                  final PsiElement resolved = first.getElement();
                  if (resolved != null) {
                    final PyType typingType = PyTypingTypeProvider.getType(resolved, elementTypes);
                    if (typingType != null) {
                      return result.withType(typingType);
                    }
                  }
                  if (firstType instanceof PyClassType) {
                    final PyType type1 =
                        new PyCollectionTypeImpl(
                            ((PyClassType) firstType).getPyClass(), false, elementTypes);
                    return result.withType(type1);
                  }
                  return EMPTY_RESULT;
                })
            .or(
                classType
                    .thenSkip(op("of"))
                    .then(simpleExpr)
                    .map(
                        value -> {
                          final ParseResult firstResult = value.getFirst();
                          final ParseResult secondResult = value.getSecond();
                          final ParseResult result = firstResult.merge(secondResult);
                          final PyType firstType = firstResult.getType();
                          final PyType secondType = secondResult.getType();
                          if (firstType != null) {
                            if (firstType instanceof PyClassType && secondType != null) {
                              return result.withType(
                                  new PyCollectionTypeImpl(
                                      ((PyClassType) firstType).getPyClass(),
                                      false,
                                      Collections.singletonList(secondType)));
                            }
                            return result.withType(firstType);
                          }
                          return EMPTY_RESULT;
                        }))
            .or(
                classType
                    .thenSkip(op("from"))
                    .then(simpleExpr)
                    .thenSkip(op("to"))
                    .then(simpleExpr)
                    .map(
                        value -> {
                          final Pair<ParseResult, ParseResult> firstPair = value.getFirst();
                          final ParseResult first = firstPair.getFirst();
                          final ParseResult second = firstPair.getSecond();
                          final ParseResult third = value.getSecond();
                          final PyType firstType = first.getType();
                          if (firstType instanceof PyClassType) {
                            final List<PyType> elementTypes =
                                Arrays.asList(second.getType(), third.getType());
                            final PyCollectionTypeImpl type1 =
                                new PyCollectionTypeImpl(
                                    ((PyClassType) firstType).getPyClass(), false, elementTypes);
                            return first.merge(second).merge(third).withType(type1);
                          }
                          return EMPTY_RESULT;
                        }))
            .named("param-expr");

    final FunctionalParser<ParseResult, PyElementType> callableExpr =
        op("(")
            .skipThen(maybe(typeExpr.then(many(op(",").skipThen(typeExpr)))))
            .thenSkip(op(")"))
            .thenSkip(op("->"))
            .then(typeExpr)
            .map(
                value -> {
                  final List<PyCallableParameter> parameters = new ArrayList<>();
                  final ParseResult returnResult = value.getSecond();
                  ParseResult result;
                  final Pair<ParseResult, List<ParseResult>> firstPair = value.getFirst();
                  if (firstPair != null) {
                    final ParseResult first = firstPair.getFirst();
                    final List<ParseResult> second = firstPair.getSecond();
                    result = first;
                    parameters.add(new PyCallableParameterImpl(null, first.getType()));
                    for (ParseResult r : second) {
                      result = result.merge(r);
                      parameters.add(new PyCallableParameterImpl(null, r.getType()));
                    }
                    result = result.merge(returnResult);
                  } else {
                    result = returnResult;
                  }
                  return result.withType(
                      new PyCallableTypeImpl(parameters, returnResult.getType()));
                })
            .named("callable-expr");

    final FunctionalParser<ParseResult, PyElementType> singleExpr =
        paramExpr.or(callableExpr).or(simpleExpr).named("single-expr");

    final FunctionalParser<ParseResult, PyElementType> unionExpr =
        singleExpr
            .then(many(op("or").or(op("|")).skipThen(singleExpr)))
            .map(
                value -> {
                  final ParseResult first = value.getFirst();
                  final List<ParseResult> rest = value.getSecond();
                  if (rest.isEmpty()) {
                    return first;
                  }
                  final List<PyType> types = new ArrayList<>();
                  types.add(first.getType());
                  ParseResult result = first;
                  for (ParseResult r : rest) {
                    types.add(r.getType());
                    result = result.merge(r);
                  }
                  return result.withType(PyUnionType.union(types));
                })
            .named("union-expr");

    typeExpr.define(unionExpr).named("type-expr");

    final FunctionalParser<ParseResult, PyElementType> typeFile =
        typeExpr.endOfInput().named("type-file");

    try {
      return typeFile.parse(tokenize(type));
    } catch (ParserException e) {
      return EMPTY_RESULT;
    }
  }
  @NotNull
  public static ParseResult parsePep484FunctionTypeComment(
      @NotNull PsiElement anchor, @NotNull String text) {
    final ForwardDeclaration<ParseResult, PyElementType> typeExpr = ForwardDeclaration.create();

    final Function<Pair<ParseResult, List<ParseResult>>, ParseResult> toParamTypeList =
        pair -> {
          if (pair != null) {
            final ParseResult first = pair.getFirst();
            final List<ParseResult> second = pair.getSecond();
            final List<PyType> itemTypes = new ArrayList<>();
            ParseResult result = first;
            itemTypes.add(result.getType());
            for (ParseResult r : second) {
              result = result.merge(r);
              itemTypes.add(r.getType());
            }
            return result.withType(ParameterListType.fromParameterTypes(itemTypes));
          }
          return EMPTY_RESULT.withType(new ParameterListType(Collections.emptyList()));
        };

    final FunctionalParser<ParseResult, PyElementType> ellipsis =
        op("...").map(token -> EMPTY_RESULT.withType(EllipsisType.INSTANCE)).named("ellipsis");

    final FunctionalParser<ParseResult, PyElementType> classType =
        token(IDENTIFIER)
            .then(many(op(".").skipThen(token(IDENTIFIER))))
            .map(new MakeSimpleType(anchor))
            .cached()
            .named("class-type");

    final FunctionalParser<ParseResult, PyElementType> typeParamList =
        op("[")
            .skipThen(maybe(typeExpr.then(many(op(",").skipThen(typeExpr)))))
            .thenSkip(op("]"))
            .map(toParamTypeList)
            .named("type-param-list");

    final FunctionalParser<ParseResult, PyElementType> typeParam =
        typeExpr.or(typeParamList).or(ellipsis).named("type-param");

    final FunctionalParser<ParseResult, PyElementType> genericType =
        classType
            .thenSkip(op("["))
            .then(typeParam)
            .then(many(op(",").skipThen(typeParam)))
            .thenSkip(op("]"))
            .map(
                value -> {
                  final Pair<ParseResult, ParseResult> firstPair = value.getFirst();
                  final ParseResult first = firstPair.getFirst();
                  final ParseResult second = firstPair.getSecond();
                  final List<ParseResult> third = value.getSecond();
                  final List<PyType> typesInBrackets = new ArrayList<>();
                  typesInBrackets.add(second.getType());
                  ParseResult result = first;
                  result = result.merge(second);
                  for (ParseResult r : third) {
                    typesInBrackets.add(r.getType());
                    result = result.merge(r);
                  }
                  final List<PyType> elementTypes =
                      third.isEmpty()
                          ? Collections.singletonList(second.getType())
                          : typesInBrackets;
                  final PsiElement resolved = first.getElement();
                  if (resolved != null) {
                    final PyType typingType = PyTypingTypeProvider.getType(resolved, elementTypes);
                    if (typingType != null) {
                      return result.withType(typingType);
                    }
                  }
                  return EMPTY_RESULT;
                })
            .named("generic-type");

    typeExpr.define(genericType.or(classType)).named("type-expr");

    final FunctionalParser<ParseResult, PyElementType> paramType =
        maybe(op("*"))
            .then(maybe(op("*")))
            .then(typeExpr)
            .map(
                pair -> {
                  final ParseResult paramResult = pair.getSecond();
                  final PyType type = paramResult.getType();
                  int starCount = 0;
                  if (pair.getFirst().getFirst() != null) {
                    starCount++;
                  }
                  if (pair.getFirst().getSecond() != null) {
                    starCount++;
                  }
                  if (starCount == 0) {
                    return paramResult;
                  } else if (starCount == 1) {
                    final PyClassType tupleType = PyTupleType.createHomogeneous(anchor, type);
                    if (tupleType != null) {
                      return paramResult.withType(tupleType);
                    }
                    return EMPTY_RESULT;
                  } else if (starCount == 2) {
                    final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(anchor);
                    final PyClassType dictType = builtinCache.getDictType();
                    if (dictType != null) {
                      final PyClass pyClass = dictType.getPyClass();
                      return paramResult.withType(
                          new PyCollectionTypeImpl(
                              pyClass, false, Arrays.asList(builtinCache.getStrType(), type)));
                    }
                    return EMPTY_RESULT;
                  }
                  return EMPTY_RESULT;
                })
            .named("param-type");

    final FunctionalParser<ParseResult, PyElementType> paramTypes =
        paramType.then(many(op(",").skipThen(paramType))).map(toParamTypeList).named("param-types");

    final FunctionalParser<ParseResult, PyElementType> funcType =
        op("(")
            .skipThen(maybe(paramTypes.or(ellipsis)))
            .thenSkip(op(")"))
            .thenSkip(op("->"))
            .then(typeExpr)
            .map(
                value -> {
                  final ParseResult paramsResult = value.getFirst();
                  final ParseResult returnResult = value.getSecond();
                  final List<PyCallableParameter> parameters;
                  ParseResult result = returnResult;
                  if (paramsResult != null) {
                    result = result.merge(paramsResult);
                    final ParameterListType paramTypesList =
                        as(paramsResult.getType(), ParameterListType.class);
                    if (paramTypesList != null) {
                      parameters = paramTypesList.getCallableParameters();
                    }
                    // ellipsis
                    else {
                      parameters = null;
                    }
                  } else {
                    parameters = Collections.emptyList();
                    result = returnResult;
                  }
                  return result.withType(
                      new PyCallableTypeImpl(parameters, returnResult.getType()));
                })
            .named("func-type");

    final FunctionalParser<ParseResult, PyElementType> typeFile =
        funcType.endOfInput().named("function-type-comment");

    try {
      return typeFile.parse(tokenize(text));
    } catch (ParserException e) {
      return EMPTY_RESULT;
    }
  }
    @Nullable
    @Override
    public ParseResult fun(@NotNull Pair<Token<PyElementType>, List<Token<PyElementType>>> value) {
      final Token<PyElementType> first = value.getFirst();
      final List<Token<PyElementType>> rest = value.getSecond();
      final TextRange firstRange = first.getRange();
      final boolean unqualified = rest.isEmpty();

      if (unqualified) {
        final ParseResult result = parseBuiltinType(first);
        if (result != null) {
          return result;
        }
      }

      final PsiFile file = myAnchor.getContainingFile();
      final List<Token<PyElementType>> tokens = new ArrayList<>();
      tokens.add(first);
      tokens.addAll(rest);

      if (file instanceof PyFile) {
        final PyFile pyFile = (PyFile) file;
        final TypeEvalContext context = TypeEvalContext.codeInsightFallback(file.getProject());
        final Map<TextRange, PyType> types = new HashMap<>();
        final Map<PyType, TextRange> fullRanges = new HashMap<>();
        final Map<PyType, PyImportElement> imports = new HashMap<>();

        PyType type = resolveQualifierType(tokens, pyFile, context, types, fullRanges, imports);
        PsiElement resolved = type != null ? getElement(type) : null;

        if (type != null) {
          final PyResolveContext resolveContext =
              PyResolveContext.defaultContext().withTypeEvalContext(context);
          final PyExpression expression =
              myAnchor instanceof PyExpression ? (PyExpression) myAnchor : null;

          for (Token<PyElementType> token : tokens) {
            final PyType qualifierType = type;
            type = null;
            final List<? extends RatedResolveResult> results =
                qualifierType.resolveMember(
                    token.getText().toString(), expression, AccessDirection.READ, resolveContext);
            if (results != null && !results.isEmpty()) {
              resolved = results.get(0).getElement();
              if (resolved instanceof PyTypedElement) {
                type = context.getType((PyTypedElement) resolved);
                if (type != null && !allowResolveToType(type)) {
                  type = null;
                  break;
                }
                if (type instanceof PyClassLikeType) {
                  type = ((PyClassLikeType) type).toInstance();
                }
              }
            }
            if (type == null) {
              break;
            }
            types.put(token.getRange(), type);
            fullRanges.put(
                type,
                TextRange.create(firstRange.getStartOffset(), token.getRange().getEndOffset()));
          }
          if (type != null) {
            return new ParseResult(resolved, type, types, fullRanges, imports);
          }
        }
      }

      return EMPTY_RESULT;
    }