@Nullable
 public PyType getReturnStatementType(TypeEvalContext typeEvalContext) {
   final ReturnVisitor visitor = new ReturnVisitor(this, typeEvalContext);
   final PyStatementList statements = getStatementList();
   statements.accept(visitor);
   if (isGeneratedStub() && !visitor.myHasReturns) {
     if (PyNames.INIT.equals(getName())) {
       return PyNoneType.INSTANCE;
     }
     return null;
   }
   return visitor.result();
 }
 private static boolean suggestRename(
     @NotNull PsiNameIdentifierOwner element, @NotNull PsiElement originalElement) {
   // Target expressions in the same scope are treated as the same variable
   if ((element instanceof PyTargetExpression)
       && originalElement instanceof PyTargetExpression) {
     return false;
   }
   // Renaming an __init__ method results in renaming its class
   else if (element instanceof PyFunction
       && PyNames.INIT.equals(element.getName())
       && ((PyFunction) element).getContainingClass() != null) {
     return false;
   }
   return true;
 }
  /**
   * Map decorators of element to {@link
   * com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator}.
   *
   * @param element decoratable element to check
   * @param context type evaluation context. If it doesn't allow switch to AST, decorators will be
   *     compared by the text of the last component of theirs qualified names.
   * @return list of known decorators in declaration order with duplicates (with any)
   */
  @NotNull
  public static List<KnownDecorator> getKnownDecorators(
      @NotNull PyDecoratable element, @NotNull TypeEvalContext context) {
    final PyDecoratorList decoratorList = element.getDecoratorList();
    if (decoratorList == null) {
      return Collections.emptyList();
    }
    final List<KnownDecorator> result = new ArrayList<>();
    final boolean allowResolve = context.maySwitchToAST((PsiElement) element);
    for (PyDecorator decorator : decoratorList.getDecorators()) {
      final QualifiedName qualifiedName = decorator.getQualifiedName();
      if (qualifiedName == null) {
        continue;
      }

      final KnownDecorator knownDecorator = ourByShortName.get(qualifiedName.getLastComponent());
      if (knownDecorator != null) {
        if (allowResolve) {
          PyQualifiedNameOwner resolved =
              as(resolveDecorator(decorator), PyQualifiedNameOwner.class);
          if (resolved instanceof PyFunction && PyNames.INIT.equals(resolved.getName())) {
            resolved = ((PyFunction) resolved).getContainingClass();
          }
          if (resolved != null && resolved.getQualifiedName() != null) {
            final QualifiedName resolvedName =
                QualifiedName.fromDottedString(resolved.getQualifiedName());
            if (resolvedName.equals(knownDecorator.getQualifiedName())) {
              result.add(knownDecorator);
            }
          }
        } else {
          result.add(knownDecorator);
        }
      }
    }
    return result;
  }
  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);
      }
    }
  }