<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/138782>138782</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            ASTMatcher `unless(hasAncestor(classTemplatePartialSpecializationDecl()))` has no effect
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          isVoid
      </td>
    </tr>
</table>

<pre>
    ## Reproducer:

Given the following `example.cpp`:
```c++
#include <type_traits>

template <typename A, typename B>
struct Tensor {};

template <class T>
struct is_tensor : std::false_type {};

template <class A, class B>
struct is_tensor<Tensor<A, B>> : std::true_type {};
```

The second `is_tensor` yields the following tree
```
`-ClassTemplatePartialSpecializationDecl 0x5e81939cb9e8 <line:9:1, line:10:50> col:8 struct is_tensor definition explicit_specialization
  |-DefinitionData empty aggregate standard_layout trivially_copyable trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
  | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
  | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
  | |-MoveConstructor exists simple trivial needs_implicit
  | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
  | |-MoveAssignment exists simple trivial needs_implicit
  | `-Destructor simple irrelevant trivial needs_implicit
  |-public 'std::true_type':'std::integral_constant<bool, true>'
  |-TemplateArgument type 'Tensor<type-parameter-0-0, type-parameter-0-1>'
  | `-TemplateSpecializationType 0x5e81939cb000 'Tensor<type-parameter-0-0, type-parameter-0-1>' dependent
  |   |-name: 'Tensor'
  |   | `-ClassTemplateDecl 0x5e81939cae88 <line:3:1, line:4:16> col:8 Tensor
  | |-TemplateArgument type 'type-parameter-0-0'
  |   | `-TemplateTypeParmType 0x5e8193817f40 'type-parameter-0-0' dependent depth 0 index 0
  | `-TemplateArgument type 'type-parameter-0-1'
  |     `-TemplateTypeParmType 0x5e8193877fb0 'type-parameter-0-1' dependent depth 0 index 1
 |-TemplateTypeParmDecl 0x5e81939cb720 <line:9:11, col:17> col:17 referenced class depth 0 index 0 A
  |-TemplateTypeParmDecl 0x5e81939cb7a0 <col:20, col:26> col:26 referenced class depth 0 index 1 B
  `-CXXRecordDecl 0x5e81939cbc88 <line:10:1, col:8> col:8 implicit struct is_tensor
```

It's obvious that the `CXXRecordDecl is_tensor` is a child of `ClassTemplatePartialSpecializationDecl`.

Given ASTMatcher setup:

```c++
class RecordCallback : public MatchFinder::MatchCallback {
public:
  RecordCallback() = default;
  void run(const MatchFinder::MatchResult & Result) {
    const CXXRecordDecl *RD =
 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("record");

    std::string name = RD->getNameAsString();

    if (name == "is_tensor")
    {
 std::string source_range =
 RD->getSourceRange().printToString(*Result.SourceManager);
        std::cout << source_range << std::endl;
    }
  }
};

void parse(std::vector<std::string> options) {

  auto ast = default_ast_unit_from_command_line(options);

  MatchFinder finder;

 RecordCallback record_callback;

  finder.addMatcher(
 recordDecl(allOf(
 unless(hasAncestor(classTemplatePartialSpecializationDecl())),
 unless(hasAncestor(classTemplateDecl())))).bind("record"),
 &record_callback);

 finder.matchAST(ast->getASTContext());
}
```

prints

```
<example.cpp:9:1, line:10:50>
```

## Expected:

Nothing should be printed because `unless(hasAncestor(classTemplatePartialSpecializationDecl()))` exclude all `recorddecl`s with CTPSD in ancestors. Noticably, `unless(hasAncestor(classTemplateDecl()))` works as expected.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJy0WE-P4joS_zTmYoGCA0k4cEjD9GoPMzvqRqt3ixy7At5n7Mh2mO799CvbAZI086ZXOzuK1Cau-v2qyvXHGWqtOCqALVo_ofV-Rjt30mYr7D-14LNa8_ctIikiKX6B1mjeMTAoLVHin7-JCyjsToAbLaX-IdQRoyyBN3puJSxY26Is6aWzJD4MkSf_JCUiqVBMdhwwSnfuvYXKGSqcRemXiO_g3ErqbvuKngGXiOzw7ddTFLbOdMzhAyirDUb5E8r3KH36CMMktRYfRlrCVq5XTEtsHfcmp2VDpYXKM_0aMBgVl0-PsVG6O1wXQTrIpV_GnM50jyivwYv0hxNgC0wr7qN9Z8gS_C5Acjs5EmcApihZMt95cw-9K9-pcYLK1xaYoFL8mzqh1R6YxMnbGorlJt2wegOFd1kKBSgtNygtl96T_vfSH_U68T4xLVFaFvhDgDk0QgmPjeGtlYIJV9kRJ0pKjFG-m-9vonvqKIZz694xPR4NHH3oraOKU8MrSd9157Az4iKolO8V0-07rSVcX2EpHBgq8YnaimllHby1plJaBdHqrC9QMacNZlRFgYpDQzvpKm_B1aKrVX5j56W8b9pgeBPW2RvbjQErAG4rcY5-4h4TeCUGdozQd7p9H0Jbr3x35OZA1VJDz1OC66KayI0ovuoLPLB-wjRG_mBjGbrGGZT7f5k4YPjvLMyS-R6mARTGgIQLVe4X-vO2q6VgGJH8Y00ikvs3gy2hHBwNldEVqhxKd7XWMrQo04EvcZLfwK_FVppjF1yLlU7yW2fwL-YhIuDAzJN5cu12o7fLMXDw-go-LuGDZxiUcJIk_wMh5tCC4qDuAY-e-V7sG9kdemDd3cZRy5l0FwrFsLukk-6y8r-zYXPpiYaZ89MAP3LzkYFXAB-279ScR-ErlnmzSn4Kdw-OX7kTTrBQHN5w8vCcfm3jcmwj_oSFed7Ujy1c_pWFS08zDOAVfToBcpJMJ0A4pHgmy_x-PsscG2jAgGLA-9E4CQsuP1TGT3lp4I3QJLlTkkFKkOxXlEv8FCl9Lv7xxwswbfiUi43yMEy1gY_FMAVvfe3DtP8wtP_ufOPAur4I3fkJTV0Y0yhLxpaM5rmwmGJ2EpJj3QTZTw1tlCWL4RWtfD18pY6dwGALrmtvN7gH17IYt2jQjkpZU_ZnuKT0rTEAPft4mtgEw4u7ZO5BomykwRMwRApENhil--tIjLccjC9acGw6hUgRGupPuF7AdtJhRDIclwEujxg4DmA8Dioi5cveU3qZqLT4pjnYxRGcX5Q23uXUMRKN1EPzKxAhJrxChCCyud0GPedtIlhn_JUr3E29hy_7OUq_eBJ6htK-hu0YgRGAaDAixVXNayJC7qkQGaNo7-mU0urOMKgMVUe4eXplfw2bL34vki9aI5Q76IFBZR-XKPuVKnoEc7MT9_9utMzfulC6Q-luyh3fXQVBcXnH8JfaWPX-7_BWHU6_pcZ6E2_aF_CDHKW7ib--DHXrE97ejz8g085pTK0bZlhFras6JVzVGH2umD6fqeJVqHFS3HEGZzLIPdz0KXjdnFRHTIuKXRP8DhIVF5Tzvv58pJOy1wi5RQoq5T-afqNTEqxFpDhRWyoG1jtPCva5so951T-7z-J9VPXPohaKP0j7iItINvV6GL3e77N3unw9eCet63OxfD3stHLw5u6kUTGmxLhvhjy1k27l1-lu-J35V58jH0H7T9ovby0wB_zWDr9pdwq1dNKd5LgGHOjBLxntbGjXv--IsgTDW_wCplJ67BhTHlu4xT-EO-Hd4fvrHguFaU9mF_ibdoLRWr57hz9p0yP6H9r8aTG1_msshGIx49uUb9INncF2ma-ydb5erZLZabthfFM0BOiSsmzVJFlWsLTe1DVh6WpNmpnYkoSsk3WSkXSdrJJFw3jK6zSr6WrDl-sMrRI4UyEXUl7OC22OM2FtB9tlWuQFmUlag7ThPyIIUfADh12fdev9zGy90rzujhatEum_CO4wTjgJ28GQ-92ndKIWK42haYC5WWfk9uRca8PXwDMiz0fhTl29YPqMyLM3q_8zb43-FzCHyHNwxiLy3Ht72ZL_BAAA__-DRIw3">