private boolean matchClasses(PsiClass patternClass, PsiClass matchClass) {
    final PsiClass saveClazz = myClazz;
    myClazz = matchClass;
    final MatchContext context = myMatchingVisitor.getMatchContext();
    final JavaCompiledPattern javaPattern = (JavaCompiledPattern)context.getPattern();

    final Set<PsiElement> matchedElements = new HashSet<>();
    context.pushMatchedElementsListener(elements -> matchedElements.addAll(elements));
    try {
      final boolean templateIsInterface = patternClass.isInterface();
      if (templateIsInterface && !matchClass.isInterface()) return false;
      if (templateIsInterface && patternClass.isAnnotationType() && !matchClass.isAnnotationType()) return false;
      if (patternClass.isEnum() && !matchClass.isEnum()) return false;
      if (patternClass instanceof PsiTypeParameter != matchClass instanceof PsiTypeParameter) return false;
      if (patternClass.isRecord() && !matchClass.isRecord()) return false;

      if (!myMatchingVisitor.match(patternClass.getRecordHeader(), matchClass.getRecordHeader())) {
        return false;
      }
      if (!matchInAnyOrder(patternClass.getExtendsList(), matchClass.getExtendsList())) {
        return false;
      }

      // check if implements is in extended classes implements
      final PsiReferenceList implementsList = patternClass.getImplementsList();
      if (implementsList != null) {
        if (implementsList.getFirstChild() != null && matchClass.isInterface()) return false;
        final List<PsiJavaCodeReferenceElement> elements = new SmartList<>();
        for (PsiJavaCodeReferenceElement element : implementsList.getReferenceElements()) {
          final MatchingHandler handler = javaPattern.getHandler(element);
          if (handler instanceof SubstitutionHandler substitutionHandler &&
              (substitutionHandler.isSubtype() || substitutionHandler.isStrictSubtype())) {
            if (!matchWithinHierarchy(element, matchClass, substitutionHandler)) {
              return false;
            }
            continue;
          }
          elements.add(element);
        }
        if (!elements.isEmpty()) {
          final PsiReferenceList implementsList2 = matchClass.getImplementsList();
          final PsiElement[] matchElements = (implementsList2 == null) ? PsiElement.EMPTY_ARRAY : implementsList2.getReferenceElements();
          if (!myMatchingVisitor.matchInAnyOrder(elements.toArray(PsiElement.EMPTY_ARRAY), matchElements)) {
            return false;
          }
        }
      }

      final PsiField[] fields = PsiTreeUtil.getChildrenOfType(patternClass, PsiField.class);
      if (fields != null) {
        final PsiField[] fields2 = javaPattern.isRequestsSuperFields() ?
                                   matchClass.getAllFields() :
                                   matchClass.getFields();

        if (!myMatchingVisitor.matchInAnyOrder(fields, fields2)) {
          return false;
        }
      }

      final PsiMethod[] methods = PsiTreeUtil.getChildrenOfType(patternClass, PsiMethod.class);
      if (methods != null) {
        final PsiMethod[] methods2 = javaPattern.isRequestsSuperMethods() ?
                                     matchClass.getAllMethods() :
                                     matchClass.getMethods();

        if (!myMatchingVisitor.matchInAnyOrder(methods, methods2)) {
          return false;
        }
      }

      final PsiClass[] nestedClasses = PsiTreeUtil.getChildrenOfType(patternClass, PsiClass.class);
      if (nestedClasses != null) {
        final PsiClass[] nestedClasses2 = javaPattern.isRequestsSuperInners() ?
                                          matchClass.getAllInnerClasses() :
                                          matchClass.getInnerClasses();

        if (!myMatchingVisitor.matchInAnyOrder(nestedClasses, nestedClasses2)) {
          return false;
        }
      }

      final PsiClassInitializer[] initializers = patternClass.getInitializers();
      if (initializers.length > 0) {
        final PsiClassInitializer[] initializers2 = matchClass.getInitializers();

        if (!myMatchingVisitor.matchInAnyOrder(initializers, initializers2)) {
          return false;
        }
      }

      final List<PsiElement> unmatchedElements = new SmartList<>(PsiTreeUtil.getChildrenOfTypeAsList(matchClass, PsiMember.class));
      unmatchedElements.removeAll(matchedElements);
      MatchingHandler unmatchedSubstitutionHandler = null;
      for (PsiElement element = patternClass.getLBrace(); element != null; element = element.getNextSibling()) {
        if (element instanceof PsiTypeElement && element.getNextSibling() instanceof PsiErrorElement) {
          unmatchedSubstitutionHandler = javaPattern.getHandler(element);
          break;
        }
      }
      if (unmatchedSubstitutionHandler instanceof SubstitutionHandler handler) {
        for (PsiElement element : unmatchedElements) {
          handler.handle(element, context);
        }
      } else {
        matchClass.putUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY, unmatchedElements);
      }

      return true;
    }
    finally {
      myClazz = saveClazz;
      context.popMatchedElementsListener();
    }
  }
private boolean matchClasses(PsiClass patternClass, PsiClass matchClass)