@Nullable
  @Override
  public PyType getReturnType(
      @NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) {
    final PyType type = getGenericReturnType(context, callSite);

    if (callSite == null) {
      return type;
    }
    final PyTypeChecker.AnalyzeCallResults results =
        PyTypeChecker.analyzeCallSite(callSite, context);

    if (PyTypeChecker.hasGenerics(type, context)) {
      if (results != null) {
        final Map<PyGenericType, PyType> substitutions =
            PyTypeChecker.unifyGenericCall(
                this, results.getReceiver(), results.getArguments(), context);
        if (substitutions != null) {
          return PyTypeChecker.substitute(type, substitutions, context);
        }
      }
      return null;
    }
    if (results != null && isDynamicallyEvaluated(results.getArguments().values(), context)) {
      return PyUnionType.createWeakType(type);
    } else {
      return type;
    }
  }
 @Override
 public void visitPyReturnStatement(PyReturnStatement node) {
   if (PsiTreeUtil.getParentOfType(node, ScopeOwner.class, true) == myFunction) {
     final PyExpression expr = node.getExpression();
     PyType returnType;
     returnType = expr == null ? PyNoneType.INSTANCE : myContext.getType(expr);
     if (!myHasReturns) {
       myResult = returnType;
       myHasReturns = true;
     } else {
       myResult = PyUnionType.union(myResult, returnType);
     }
   }
 }
 @Override
 public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
   for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
     final PyType type = provider.getCallableType(this, context);
     if (type != null) {
       return type;
     }
   }
   final PyFunctionTypeImpl type = new PyFunctionTypeImpl(this);
   if (PyKnownDecoratorUtil.hasUnknownDecorator(this, context) && getProperty() == null) {
     return PyUnionType.createWeakType(type);
   }
   return type;
 }
 @Override
 public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
   for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
     final PyType type = provider.getCallableType(this, context);
     if (type != null) {
       return type;
     }
   }
   final PyFunctionType type = new PyFunctionType(this);
   if (getDecoratorList() != null) {
     return PyUnionType.createWeakType(type);
   }
   return type;
 }
  @Nullable
  private Ref<? extends PyType> getYieldStatementType(@NotNull final TypeEvalContext context) {
    Ref<PyType> elementType = null;
    final PyBuiltinCache cache = PyBuiltinCache.getInstance(this);
    final PyStatementList statements = getStatementList();
    final Set<PyType> types = new LinkedHashSet<>();
    statements.accept(
        new PyRecursiveElementVisitor() {
          @Override
          public void visitPyYieldExpression(PyYieldExpression node) {
            final PyExpression expr = node.getExpression();
            final PyType type = expr != null ? context.getType(expr) : null;
            if (node.isDelegating() && type instanceof PyCollectionType) {
              final PyCollectionType collectionType = (PyCollectionType) type;
              // TODO: Select the parameter types that matches T in Iterable[T]
              final List<PyType> elementTypes = collectionType.getElementTypes(context);
              types.add(elementTypes.isEmpty() ? null : elementTypes.get(0));
            } else {
              types.add(type);
            }
          }

          @Override
          public void visitPyFunction(PyFunction node) {
            // Ignore nested functions
          }
        });
    final int n = types.size();
    if (n == 1) {
      elementType = Ref.create(types.iterator().next());
    } else if (n > 0) {
      elementType = Ref.create(PyUnionType.union(types));
    }
    if (elementType != null) {
      final PyClass generator = cache.getClass(PyNames.FAKE_GENERATOR);
      if (generator != null) {
        final List<PyType> parameters =
            Arrays.asList(elementType.get(), null, getReturnStatementType(context));
        return Ref.create(new PyCollectionTypeImpl(generator, false, parameters));
      }
    }
    if (!types.isEmpty()) {
      return Ref.create(null);
    }
    return null;
  }
 @Override
 public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
   for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
     final PyType type = provider.getCallableType(this, context);
     if (type != null) {
       return type;
     }
   }
   final boolean hasCustomDecorators =
       PyUtil.hasCustomDecorators(this)
           && !PyUtil.isDecoratedAsAbstract(this)
           && getProperty() == null;
   final PyFunctionType type = new PyFunctionType(this);
   if (hasCustomDecorators) {
     return PyUnionType.createWeakType(type);
   }
   return type;
 }
  @Nullable
  private Ref<? extends PyType> getYieldStatementType(@NotNull final TypeEvalContext context) {
    Ref<PyType> elementType = null;
    final PyBuiltinCache cache = PyBuiltinCache.getInstance(this);
    final PyStatementList statements = getStatementList();
    final Set<PyType> types = new LinkedHashSet<PyType>();
    if (statements != null) {
      statements.accept(
          new PyRecursiveElementVisitor() {
            @Override
            public void visitPyYieldExpression(PyYieldExpression node) {
              final PyType type = context.getType(node);
              if (node.isDelegating() && type instanceof PyCollectionType) {
                final PyCollectionType collectionType = (PyCollectionType) type;
                types.add(collectionType.getElementType(context));
              } else {
                types.add(type);
              }
            }

            @Override
            public void visitPyFunction(PyFunction node) {
              // Ignore nested functions
            }
          });
      final int n = types.size();
      if (n == 1) {
        elementType = Ref.create(types.iterator().next());
      } else if (n > 0) {
        elementType = Ref.create(PyUnionType.union(types));
      }
    }
    if (elementType != null) {
      final PyClass generator = cache.getClass(PyNames.FAKE_GENERATOR);
      if (generator != null) {
        return Ref.create(new PyCollectionTypeImpl(generator, false, elementType.get()));
      }
    }
    if (!types.isEmpty()) {
      return Ref.create(null);
    }
    return null;
  }
 @Nullable
 private PyType analyzeCallType(
     @Nullable PyType type,
     @Nullable PyExpression receiver,
     @NotNull Map<PyExpression, PyNamedParameter> parameters,
     @NotNull TypeEvalContext context) {
   if (PyTypeChecker.hasGenerics(type, context)) {
     final Map<PyGenericType, PyType> substitutions =
         PyTypeChecker.unifyGenericCall(receiver, parameters, context);
     if (substitutions != null) {
       type = PyTypeChecker.substitute(type, substitutions, context);
     } else {
       type = null;
     }
   }
   if (receiver != null) {
     type = replaceSelf(type, receiver, context);
   }
   if (type != null && isDynamicallyEvaluated(parameters.values(), context)) {
     type = PyUnionType.createWeakType(type);
   }
   return type;
 }