@NotNull
 private static List<PyGenericType> collectGenericTypes(
     @NotNull PyClass cls, @NotNull Context context) {
   boolean isGeneric = false;
   for (PyClass ancestor : cls.getAncestorClasses(context.getTypeContext())) {
     if (GENERIC_CLASSES.contains(ancestor.getQualifiedName())) {
       isGeneric = true;
       break;
     }
   }
   if (isGeneric) {
     final ArrayList<PyGenericType> results = new ArrayList<>();
     // XXX: Requires switching from stub to AST
     for (PyExpression expr : cls.getSuperClassExpressions()) {
       if (expr instanceof PySubscriptionExpression) {
         final PyExpression indexExpr = ((PySubscriptionExpression) expr).getIndexExpression();
         if (indexExpr != null) {
           for (PsiElement resolved : tryResolving(indexExpr, context.getTypeContext())) {
             final PyGenericType genericType = getGenericType(resolved, context);
             if (genericType != null) {
               results.add(genericType);
             }
           }
         }
       }
     }
     return results;
   }
   return Collections.emptyList();
 }
 @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
 @Override
 public String getQualifiedName() {
   String name = getName();
   if (name == null) {
     return null;
   }
   PyClass containingClass = getContainingClass();
   if (containingClass != null) {
     return containingClass.getQualifiedName() + "." + name;
   }
   if (PsiTreeUtil.getStubOrPsiParent(this) instanceof PyFile) {
     VirtualFile virtualFile = getContainingFile().getVirtualFile();
     if (virtualFile != null) {
       final String packageName =
           QualifiedNameFinder.findShortestImportableName(this, virtualFile);
       return packageName + "." + name;
     }
   }
   return null;
 }