private boolean matchWithinHierarchy(@Nullable PsiElement patternElement, PsiElement matchElement, SubstitutionHandler handler) {
    boolean includeInterfaces = true;
    boolean includeClasses = true;
    if (patternElement != null) {
      patternElement = StructuralSearchUtil.getParentIfIdentifier(patternElement);
      final PsiElement patternParent = patternElement.getParent();

      if (patternParent instanceof PsiReferenceList && patternParent.getParent() instanceof PsiClass psiClass) {
        if (patternParent == psiClass.getExtendsList()) {
          includeInterfaces = psiClass.isInterface();
        }
        else if (patternParent == psiClass.getImplementsList()) {
          includeClasses = false;
        }
      }
    }

    final NodeIterator nodes = new HierarchyNodeIterator(matchElement, includeClasses, includeInterfaces);
    if (!includeClasses) {
      nodes.advance();
    }
    if (handler.isStrictSubtype()) {
      nodes.advance();
    }

    final boolean negated = handler.getPredicate() instanceof NotPredicate;
    while (nodes.hasNext() && negated == handler.validate(nodes.current(), myMatchingVisitor.getMatchContext())) {
      nodes.advance();
    }
    return negated != nodes.hasNext();
  }
private boolean matchWithinHierarchy(@Nullable PsiElement patternElement, PsiElement matchElement, SubstitutionHandler handler)