@Nullable
 private PsiElement resolveToDocStringOwner() {
   // here the ^Q target is already resolved; the resolved element may point to intermediate
   // assignments
   if (myElement instanceof PyTargetExpression) {
     final String targetName = myElement.getText();
     myReassignmentChain.addWith(
         TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", targetName)).addItem(BR));
     final PyExpression assignedValue = ((PyTargetExpression) myElement).findAssignedValue();
     if (assignedValue instanceof PyReferenceExpression) {
       final PsiElement resolved = resolveWithoutImplicits((PyReferenceExpression) assignedValue);
       if (resolved != null) {
         return resolved;
       }
     }
     return assignedValue;
   }
   if (myElement instanceof PyReferenceExpression) {
     myReassignmentChain.addWith(
         TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", myElement.getText())).addItem(BR));
     return resolveWithoutImplicits((PyReferenceExpression) myElement);
   }
   // it may be a call to a standard wrapper
   if (myElement instanceof PyCallExpression) {
     final PyCallExpression call = (PyCallExpression) myElement;
     final Pair<String, PyFunction> wrapInfo =
         PyCallExpressionHelper.interpretAsModifierWrappingCall(call, myOriginalElement);
     if (wrapInfo != null) {
       String wrapperName = wrapInfo.getFirst();
       PyFunction wrappedFunction = wrapInfo.getSecond();
       myReassignmentChain.addWith(
           TagSmall, $(PyBundle.message("QDOC.wrapped.in.$0", wrapperName)).addItem(BR));
       return wrappedFunction;
     }
   }
   return myElement;
 }
 private void addModulePath(PyFile followed) {
   // what to prepend to a module description?
   final VirtualFile file = followed.getVirtualFile();
   if (file == null) {
     myProlog.addWith(TagSmall, $(PyBundle.message("QDOC.module.path.unknown")));
   } else {
     final String path = file.getPath();
     RootFinder finder = new RootFinder(path);
     RootVisitorHost.visitRoots(followed, finder);
     final String rootPath = finder.getResult();
     if (rootPath != null) {
       String afterPart = path.substring(rootPath.length());
       myProlog.addWith(TagSmall, $(rootPath).addWith(TagBold, $(afterPart)));
     } else {
       myProlog.addWith(TagSmall, $(path));
     }
   }
 }
 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"));
       }
     }
   }
 }
  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);
      }
    }
  }