@Nullable
 private PyType createCoroutineType(@Nullable PyType returnType) {
   final PyBuiltinCache cache = PyBuiltinCache.getInstance(this);
   if (returnType instanceof PyClassLikeType
       && PyNames.FAKE_COROUTINE.equals(((PyClassLikeType) returnType).getClassQName())) {
     return returnType;
   }
   final PyClass generator = cache.getClass(PyNames.FAKE_COROUTINE);
   return generator != null
       ? new PyCollectionTypeImpl(generator, false, Collections.singletonList(returnType))
       : null;
 }
 @Nullable
 private Ref<PyType> getTypeOfProperty(
     @Nullable PyType qualifierType, @NotNull String name, @NotNull TypeEvalContext context) {
   if (qualifierType instanceof PyClassType) {
     final PyClassType classType = (PyClassType) qualifierType;
     PyClass pyClass = classType.getPyClass();
     Property property = pyClass.findProperty(name, true);
     if (property != null) {
       if (classType.isDefinition()) {
         return Ref.<PyType>create(
             PyBuiltinCache.getInstance(pyClass).getObjectType(PyNames.PROPERTY));
       }
       if (AccessDirection.of(this) == AccessDirection.READ) {
         final PyType type = property.getType(context);
         if (type != null) {
           return Ref.create(type);
         }
       }
       return Ref.create();
     }
   } else if (qualifierType instanceof PyUnionType) {
     final PyUnionType unionType = (PyUnionType) qualifierType;
     for (PyType type : unionType.getMembers()) {
       final Ref<PyType> result = getTypeOfProperty(type, name, context);
       if (result != null) {
         return result;
       }
     }
   }
   return null;
 }
  @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;
  }
  @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
 public static PyType getTypeFromTarget(
     @NotNull final PsiElement target,
     final TypeEvalContext context,
     PyReferenceExpression anchor) {
   if (!(target
       instanceof PyTargetExpression)) { // PyTargetExpression will ask about its type itself
     final PyType pyType = getReferenceTypeFromProviders(target, context, anchor);
     if (pyType != null) {
       return pyType;
     }
   }
   if (target instanceof PyTargetExpression) {
     final String name = ((PyTargetExpression) target).getName();
     if (PyNames.NONE.equals(name)) {
       return PyNoneType.INSTANCE;
     }
     if (PyNames.TRUE.equals(name) || PyNames.FALSE.equals(name)) {
       return PyBuiltinCache.getInstance(target).getBoolType();
     }
   }
   if (target instanceof PyFile) {
     return new PyModuleType((PyFile) target);
   }
   if (target instanceof PyImportedModule) {
     return new PyImportedModuleType((PyImportedModule) target);
   }
   if ((target instanceof PyTargetExpression || target instanceof PyNamedParameter)
       && anchor != null
       && context.allowDataFlow(anchor)) {
     final ScopeOwner scopeOwner = PsiTreeUtil.getStubOrPsiParentOfType(anchor, ScopeOwner.class);
     if (scopeOwner != null
         && scopeOwner == PsiTreeUtil.getStubOrPsiParentOfType(target, ScopeOwner.class)) {
       final String name = ((PyElement) target).getName();
       if (name != null) {
         final PyType type = getTypeByControlFlow(name, context, anchor, scopeOwner);
         if (type != null) {
           return type;
         }
       }
     }
   }
   if (target instanceof PyFunction) {
     final PyDecoratorList decoratorList = ((PyFunction) target).getDecoratorList();
     if (decoratorList != null) {
       final PyDecorator propertyDecorator = decoratorList.findDecorator(PyNames.PROPERTY);
       if (propertyDecorator != null) {
         return PyBuiltinCache.getInstance(target).getObjectType(PyNames.PROPERTY);
       }
       for (PyDecorator decorator : decoratorList.getDecorators()) {
         final QualifiedName qName = decorator.getQualifiedName();
         if (qName != null
             && (qName.endsWith(PyNames.SETTER)
                 || qName.endsWith(PyNames.DELETER)
                 || qName.endsWith(PyNames.GETTER))) {
           return PyBuiltinCache.getInstance(target).getObjectType(PyNames.PROPERTY);
         }
       }
     }
   }
   if (target instanceof PyTypedElement) {
     return context.getType((PyTypedElement) target);
   }
   if (target instanceof PsiDirectory) {
     final PsiDirectory dir = (PsiDirectory) target;
     PsiFile file = dir.findFile(PyNames.INIT_DOT_PY);
     if (file != null) {
       return getTypeFromTarget(file, context, anchor);
     }
     if (PyUtil.isPackage(dir, anchor)) {
       final PsiFile containingFile = anchor.getContainingFile();
       if (containingFile instanceof PyFile) {
         final QualifiedName qualifiedName = QualifiedNameFinder.findShortestImportableQName(dir);
         if (qualifiedName != null) {
           final PyImportedModule module =
               new PyImportedModule(null, (PyFile) containingFile, qualifiedName);
           return new PyImportedModuleType(module);
         }
       }
     }
   }
   return null;
 }