@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;
 }
 public Property getProperty() {
   final PyClass containingClass = getContainingClass();
   if (containingClass != null) {
     return containingClass.findPropertyByCallable(this);
   }
   return null;
 }
 @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 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;
 }
  /**
   * Looks for two standard decorators to a function, or a wrapping assignment that closely follows
   * it.
   *
   * @return a flag describing what was detected.
   */
  @Nullable
  public Modifier getModifier() {
    final String deconame = getClassOrStaticMethodDecorator();
    if (PyNames.CLASSMETHOD.equals(deconame)) {
      return CLASSMETHOD;
    } else if (PyNames.STATICMETHOD.equals(deconame)) {
      return STATICMETHOD;
    }
    // implicit staticmethod __new__
    final PyClass cls = getContainingClass();
    if (cls != null && PyNames.NEW.equals(getName()) && cls.isNewStyleClass(null)) {
      return STATICMETHOD;
    }
    //
    if (getStub() != null) {
      return getWrappersFromStub();
    }
    final String funcName = getName();
    if (funcName != null) {
      PyAssignmentStatement currentAssignment =
          PsiTreeUtil.getNextSiblingOfType(this, PyAssignmentStatement.class);
      while (currentAssignment != null) {
        final String modifier =
            currentAssignment
                .getTargetsToValuesMapping()
                .stream()
                .filter(
                    pair ->
                        pair.getFirst() instanceof PyTargetExpression
                            && funcName.equals(pair.getFirst().getName()))
                .filter(pair -> pair.getSecond() instanceof PyCallExpression)
                .map(
                    pair ->
                        interpretAsModifierWrappingCall((PyCallExpression) pair.getSecond(), this))
                .filter(interpreted -> interpreted != null && interpreted.getSecond() == this)
                .map(interpreted -> interpreted.getFirst())
                .filter(
                    wrapperName ->
                        PyNames.CLASSMETHOD.equals(wrapperName)
                            || PyNames.STATICMETHOD.equals(wrapperName))
                .findAny()
                .orElse(null);

        if (PyNames.CLASSMETHOD.equals(modifier)) {
          return CLASSMETHOD;
        } else if (PyNames.STATICMETHOD.equals(modifier)) {
          return STATICMETHOD;
        }

        currentAssignment =
            PsiTreeUtil.getNextSiblingOfType(currentAssignment, PyAssignmentStatement.class);
      }
    }
    return null;
  }
 protected String getElementLocation() {
   final PyClass containingClass = getContainingClass();
   if (containingClass != null) {
     return "("
         + containingClass.getName()
         + " in "
         + getPackageForFile(getContainingFile())
         + ")";
   }
   return super.getElementLocation();
 }
 /**
  * Looks for two standard decorators to a function, or a wrapping assignment that closely follows
  * it.
  *
  * @return a flag describing what was detected.
  */
 @Nullable
 public Modifier getModifier() {
   String deconame = getClassOrStaticMethodDecorator();
   if (PyNames.CLASSMETHOD.equals(deconame)) {
     return CLASSMETHOD;
   } else if (PyNames.STATICMETHOD.equals(deconame)) {
     return STATICMETHOD;
   }
   // implicit staticmethod __new__
   PyClass cls = getContainingClass();
   if (cls != null && PyNames.NEW.equals(getName()) && cls.isNewStyleClass()) {
     return STATICMETHOD;
   }
   //
   if (getStub() != null) {
     return getWrappersFromStub();
   }
   String func_name = getName();
   if (func_name != null) {
     PyAssignmentStatement assignment =
         PsiTreeUtil.getNextSiblingOfType(this, PyAssignmentStatement.class);
     if (assignment != null) {
       for (Pair<PyExpression, PyExpression> pair : assignment.getTargetsToValuesMapping()) {
         PyExpression value = pair.getSecond();
         if (value instanceof PyCallExpression) {
           PyExpression target = pair.getFirst();
           if (target instanceof PyTargetExpression && func_name.equals(target.getName())) {
             Pair<String, PyFunction> interpreted =
                 interpretAsModifierWrappingCall((PyCallExpression) value, this);
             if (interpreted != null) {
               PyFunction original = interpreted.getSecond();
               if (original == this) {
                 String wrapper_name = interpreted.getFirst();
                 if (PyNames.CLASSMETHOD.equals(wrapper_name)) {
                   return CLASSMETHOD;
                 } else if (PyNames.STATICMETHOD.equals(wrapper_name)) {
                   return STATICMETHOD;
                 }
               }
             }
           }
         }
       }
     }
   }
   return null;
 }
  private void buildFromAttributeDoc() {
    PyClass cls = PsiTreeUtil.getParentOfType(myElement, PyClass.class);
    assert cls != null;
    String type =
        PyUtil.isInstanceAttribute((PyExpression) myElement)
            ? "Instance attribute "
            : "Class attribute ";
    myProlog
        .addItem(type)
        .addWith(TagBold, $().addWith(TagCode, $(((PyTargetExpression) myElement).getName())))
        .addItem(" of class ")
        .addWith(PythonDocumentationProvider.LinkMyClass, $().addWith(TagCode, $(cls.getName())))
        .addItem(BR);

    final String docString = ((PyTargetExpression) myElement).getDocStringValue();
    if (docString != null) {
      addFormattedDocString(myElement, docString, myBody, myEpilog);
    }
  }
 private void addPredefinedMethodDoc(PyFunction fun, String mothodName) {
   PyClassType objectType =
       PyBuiltinCache.getInstance(fun)
           .getObjectType(); // old- and new-style classes share the __xxx__ stuff
   if (objectType != null) {
     PyClass objectClass = objectType.getPyClass();
     PyFunction predefinedMethod = objectClass.findMethodByName(mothodName, false);
     if (predefinedMethod != null) {
       PyStringLiteralExpression predefinedDocstring = predefinedMethod.getDocStringExpression();
       String predefinedDoc =
           predefinedDocstring != null ? predefinedDocstring.getStringValue() : null;
       if (predefinedDoc != null
           && predefinedDoc.length() > 1) { // only a real-looking doc string counts
         addFormattedDocString(fun, predefinedDoc, myBody, myBody);
         myEpilog.addItem(BR).addItem(BR).addItem(PyBundle.message("QDOC.copied.from.builtin"));
       }
     }
   }
 }
 @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;
 }
  private void addInheritedDocString(
      @NotNull final PyFunction pyFunction, @Nullable final PyClass pyClass) {
    boolean notFound = true;
    final String methodName = pyFunction.getName();
    if (pyClass == null || methodName == null) {
      return;
    }
    final boolean isConstructor = PyNames.INIT.equals(methodName);
    Iterable<PyClass> classes = pyClass.getAncestorClasses(null);
    if (isConstructor) {
      // look at our own class again and maybe inherit class's doc
      classes = new ChainIterable<PyClass>(pyClass).add(classes);
    }
    for (PyClass ancestor : classes) {
      PyStringLiteralExpression docstringElement = null;
      PyFunction inherited = null;
      boolean isFromClass = false;
      if (isConstructor) docstringElement = pyClass.getDocStringExpression();
      if (docstringElement != null) {
        isFromClass = true;
      } else {
        inherited = ancestor.findMethodByName(methodName, false);
      }
      if (inherited != null) {
        docstringElement = inherited.getDocStringExpression();
      }
      if (docstringElement != null) {
        final String inheritedDoc = docstringElement.getStringValue();
        if (inheritedDoc.length() > 1) {
          myEpilog.addItem(BR).addItem(BR);
          String ancestor_name = ancestor.getName();
          String marker =
              (pyClass == ancestor)
                  ? PythonDocumentationProvider.LINK_TYPE_CLASS
                  : PythonDocumentationProvider.LINK_TYPE_PARENT;
          final String ancestor_link =
              $().addWith(new LinkWrapper(marker + ancestor_name), $(ancestor_name)).toString();
          if (isFromClass) {
            myEpilog.addItem(PyBundle.message("QDOC.copied.from.class.$0", ancestor_link));
          } else {
            myEpilog.addItem(PyBundle.message("QDOC.copied.from.$0.$1", ancestor_link, methodName));
          }
          myEpilog.addItem(BR).addItem(BR);
          ChainIterable<String> formatted = new ChainIterable<String>();
          ChainIterable<String> unformatted = new ChainIterable<String>();
          addFormattedDocString(pyFunction, inheritedDoc, formatted, unformatted);
          myEpilog.addWith(TagCode, formatted).add(unformatted);
          notFound = false;
          break;
        }
      }
    }

    if (notFound) {
      // above could have not worked because inheritance is not searched down to 'object'.
      // for well-known methods, copy built-in doc string.
      // TODO: also handle predefined __xxx__ that are not part of 'object'.
      if (PyNames.UnderscoredAttributes.contains(methodName)) {
        addPredefinedMethodDoc(pyFunction, methodName);
      }
    }
  }
  private boolean buildFromProperty(
      PsiElement elementDefinition,
      @Nullable final PsiElement outerElement,
      @NotNull final TypeEvalContext context) {
    if (myOriginalElement == null) {
      return false;
    }
    final String elementName = myOriginalElement.getText();
    if (!PyNames.isIdentifier(elementName)) {
      return false;
    }
    if (!(outerElement instanceof PyQualifiedExpression)) {
      return false;
    }
    final PyExpression qualifier = ((PyQualifiedExpression) outerElement).getQualifier();
    if (qualifier == null) {
      return false;
    }
    final PyType type = context.getType(qualifier);
    if (!(type instanceof PyClassType)) {
      return false;
    }
    final PyClass cls = ((PyClassType) type).getPyClass();
    final Property property = cls.findProperty(elementName, true, null);
    if (property == null) {
      return false;
    }

    final AccessDirection direction = AccessDirection.of((PyElement) outerElement);
    final Maybe<PyCallable> accessor = property.getByDirection(direction);
    myProlog
        .addItem("property ")
        .addWith(TagBold, $().addWith(TagCode, $(elementName)))
        .addItem(" of ")
        .add(PythonDocumentationProvider.describeClass(cls, TagCode, true, true));
    if (accessor.isDefined() && property.getDoc() != null) {
      myBody.addItem(": ").addItem(property.getDoc()).addItem(BR);
    } else {
      final PyCallable getter = property.getGetter().valueOrNull();
      if (getter != null && getter != myElement && getter instanceof PyFunction) {
        // not in getter, getter's doc comment may be useful
        final PyStringLiteralExpression docstring = ((PyFunction) getter).getDocStringExpression();
        if (docstring != null) {
          myProlog
              .addItem(BR)
              .addWith(TagItalic, $("Copied from getter:"))
              .addItem(BR)
              .addItem(docstring.getStringValue());
        }
      }
      myBody.addItem(BR);
    }
    myBody.addItem(BR);
    if (accessor.isDefined() && accessor.value() == null) elementDefinition = null;
    final String accessorKind = getAccessorKind(direction);
    if (elementDefinition != null) {
      myEpilog.addWith(TagSmall, $(BR, BR, accessorKind, " of property")).addItem(BR);
    }

    if (!(elementDefinition instanceof PyDocStringOwner)) {
      myBody
          .addWith(
              TagItalic,
              elementDefinition != null ? $("Declaration: ") : $(accessorKind + " is not defined."))
          .addItem(BR);
      if (elementDefinition != null) {
        myBody.addItem(combUp(PyUtil.getReadableRepr(elementDefinition, false)));
      }
    }
    return true;
  }