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

    <tr>
        <th>Summary</th>
        <td>
            libclang: attribute annotate on ClassDecl is not exposed in the AST if it is a result of a macro
        </td>
    </tr>

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

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

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

<pre>
    The source code I am parsing uses `[[clang::annotate]]` on a class declaration, however that annotation is not always visible in the AST.
In the following source, only `Test01` and `Test02` ClassDecls have a `attribute(annotate)` child in the AST.
The expected behavior would be for all of the class declarations have the `attribute(annotate)` child.
```
class [[clang::annotate("ann")]] Test01 {};
class __attribute__((annotate("ann"))) Test02 {};

#define ANNOTATE1 [[clang::annotate("ann")]]
#define ANNOTATE2 __attribute__((annotate("ann")))

class ANNOTATE1 Test03 {};
class ANNOTATE2 Test04 {};

#define ANNOTATE_STR1(x) [[clang::annotate((x))]]
#define ANNOTATE_STR2(x) __attribute__((annotate((x))))

class ANNOTATE_STR1("ann") Test05 {};
class ANNOTATE_STR2("ann") Test06 {};
```

Here's the code I am running:
```
#include <clang-c/Index.h>
#include <cstdio>

#include <vector>
#include <format>

CXErrorCode CreateSimpleTU(CXIndex index, CXTranslationUnit& tu)
{
    constexpr const char* header = "testfile.cpp";

    constexpr const char* header_args[] = {
        "-x",
        "c++",

 "-std=c++20",
        "--pedantic",
 "--pedantic-errors",

        "-Wall",
        "-Wextra",
 "-Weverything",
        "-Werror",

 "-Wno-c++98-compat",
    };

    const int tu_flags = CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_VisitImplicitAttributes;

    CXErrorCode err = clang_parseTranslationUnit2
    (
        index,
        header,
 /*args*/ header_args, /*num_args*/ std::size(header_args),
 /*unsaved files*/ nullptr,  /*num_unsaved_files*/ 0,
        tu_flags,
 &tu
    );

    return err;
}

constexpr bool PRETTY_PRINTED = false;

struct VisitorData
{
    int depth = 1;
};

static CXChildVisitResult VisitTU(CXCursor current_cursor, CXCursor parent, CXClientData client_data)
{
    VisitorData* data = reinterpret_cast<VisitorData*>(client_data);

    if (clang_Location_isFromMainFile(clang_getCursorLocation(current_cursor)) == 0)
    {
        return CXChildVisit_Continue;
    }

 const CXCursorKind cursor_kind = clang_getCursorKind(current_cursor);
 const bool is_attr = clang_isAttribute(cursor_kind) != 0;
    const bool has_attr = clang_Cursor_hasAttrs(current_cursor);

    const CXString display_name = clang_getCursorDisplayName(current_cursor);
    const CXString spelling = clang_getCursorSpelling(current_cursor);
    const CXString kind_spelling = clang_getCursorKindSpelling(cursor_kind);
 const CXPrintingPolicy print_policy = clang_getCursorPrintingPolicy(current_cursor);
    const CXString pretty_printed = clang_getCursorPrettyPrinted(current_cursor, print_policy);
    const CXType cursor_type = clang_getCursorType(current_cursor);
    const CXString type_spelling = clang_getTypeSpelling(cursor_type);

    std::string indent = std::format("{:->{}}", "", data->depth * 2);
    std::string msg = std::format("{} {} '{}' <{}> {}{}"
        , indent
        , clang_getCString(kind_spelling)
        , clang_getCString(spelling)
 , clang_getCString(type_spelling)
        , is_attr ? "[ATTRIB] " : ""
        , has_attr ? "[HAS_ATTR] " : ""
    );
 printf("%s\n", msg.c_str());

    if constexpr (PRETTY_PRINTED)
 {
        printf("%s\n", clang_getCString(pretty_printed));
 }

    VisitorData child_data;
    child_data.depth = data->depth + 1;
    clang_visitChildren(current_cursor, VisitTU, &child_data);

 clang_disposeString(type_spelling);
 clang_disposeString(pretty_printed);
 clang_PrintingPolicy_dispose(print_policy);
 clang_disposeString(kind_spelling);
    clang_disposeString(spelling);
 clang_disposeString(display_name);

    return CXChildVisit_Continue;
};


int main(int argc, char* argv[])
{
    {
        CXString clang_ver = clang_getClangVersion();
        printf("clang ver: %s\n", clang_getCString(clang_ver));
        clang_disposeString(clang_ver);
 }

    CXIndex index = clang_createIndex(/*exclide decls from PCH*/1, /*print diagnostics*/0); 
    if (index == nullptr)
    {
 printf("error\n");
        return 1;
    }

 CXTranslationUnit tu;
    CXErrorCode err = CreateSimpleTU(index, tu);

    if (tu == nullptr || err != CXError_Success)
    {
 printf("tu creation error: %d\n", int(err));
        return 123;
 }

    const int num_diags = clang_getNumDiagnostics(tu);
 printf("diagnostics (%d):\n", num_diags);
    for (int i = 0; i < num_diags; ++i)
    {
        CXDiagnostic diag = clang_getDiagnostic(tu, i);
        CXString s = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions());

        printf("%s\n", clang_getCString(s));

        clang_disposeString(s);
 clang_disposeDiagnostic(diag);
    }

    CXCursor cursor = clang_getTranslationUnitCursor(tu);

    VisitorData data;
 clang_visitChildren(cursor, VisitTU, /*user_data*/&data);

 clang_disposeTranslationUnit(tu);
    tu = nullptr;

 clang_disposeIndex(index);
    index = nullptr;

    return 0;
}

```
Here's the output it produces:
```
clang ver: clang version 18.1.1
diagnostics (0):
-- ClassDecl 'Test01' <Test01> [HAS_ATTR]
---- attribute(annotate) 'ann' <> [ATTRIB] [HAS_ATTR]
-- ClassDecl 'Test02' <Test02> [HAS_ATTR]
---- attribute(annotate) 'ann' <> [ATTRIB] [HAS_ATTR]
-- ClassDecl 'Test03' <Test03> [HAS_ATTR]
-- ClassDecl 'Test04' <Test04> [HAS_ATTR]
-- ClassDecl 'Test05' <Test05> [HAS_ATTR]
-- ClassDecl 'Test06' <Test06> [HAS_ATTR]
```

If you set PRETTY_PRINTED to true, the output will contain the pretty printed source representation of each node. As you can observe, that representation contains the annotation, which suggests that it is not lost during the parsing, just not properly exposed in the AST.

```
clang ver: clang version 18.1.1
diagnostics (0):
-- ClassDecl 'Test01' <Test01> [HAS_ATTR]
class [[clang::annotate("ann")]] Test01 {
}
---- attribute(annotate) 'ann' <> [ATTRIB] [HAS_ATTR]

-- ClassDecl 'Test02' <Test02> [HAS_ATTR]
class __attribute__((annotate("ann"))) Test02 {
}
---- attribute(annotate) 'ann' <> [ATTRIB] [HAS_ATTR]

-- ClassDecl 'Test03' <Test03> [HAS_ATTR]
class [[clang::annotate("ann")]] Test03 {
}
-- ClassDecl 'Test04' <Test04> [HAS_ATTR]
class __attribute__((annotate("ann"))) Test04 {
}
-- ClassDecl 'Test05' <Test05> [HAS_ATTR]
class [[clang::annotate("ann")]] Test05 {
}
-- ClassDecl 'Test06' <Test06> [HAS_ATTR]
class __attribute__((annotate("ann"))) Test06 {
}
```


Further testing indicates that the same applies to annotations attached to class methods.



</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzMOttv4rj6f036YoHAgRIe-kDDoK1-vzNbTdmdnqfIOIZ418SR7fSyf_3RZzvkzrQ7R9ozqiDE393f1R6iNT_ljN0Fy_tgub0hpcmkuvtD__V-ljn78-Yg0_e7fcaQlqWiDFGZMvSAyBkVRGmen1CpmUbB7cxSuKeC5Kcg3AThhuS5NMSwYLmFv9sZkjkiiAqiNUoZFUQRw2Ue4Bhl8pW9MIVMRgzyiFzmiGuUS4OIeCXvGr1wzQ-CIZ4jkzG0edpPg9k2mG0e3IujFEK-glBOWqAsc_EO4u2ZNrM5SEHy9PICw4sYJNoyKjTKyAtDBJaJMYofSsMCHF00wWuApxkXaV8IsBJ7Kxg1LEUHlpEXLhV6laWAn-goFSJCIHm0eD0zeOaw9gH2nifY3f3Zn47o-E7gKMCY5HmAMRCzG4OcZVCwug9W2yC8b5JKkosgSWLRozFi9s8Rw11i_hOHKTvynKHN16-_7jf7L_NPyjpGB39a0KZcTtVaJqtDOGyQmqOFWnxU0-Rp_20e4OgNjHRdaQvzQ5WBIK4I_kj7C8kf6F5J2bSXU3R53RyVNH3E256F2i7rPn9higV4pV1oXHKMKvOcOyMN4uKQ51SUKUNBGFt7TmiAdw95yt6mWRB-GYTTJuWyXhwAeWHUSDWCf5TqTEwHP37-opRUMYgeK0YMe-LnQrD9bwGO4mcrEeLwCUkpft4rkmthA_-3nJsA3yJT1nuz8sZCCCEqc23YW6HcE6IZUQHeoIyRlCkUhFsUYGyYNkcu2JQWBdi_7ZA_ppMQddKuCjiSTRHgX4Dx5M3ubNxboAG-t3_1qocBLG3SINx6GDwboTGZFCwlueG0BdBamTAwsh7g06DznQgxxuM7ezOK9Bh8h-LzbjLwtVFMYD2i4fdcTrx-62hC5bkgpkuonyUum4J4bpApk6MgJ22t33OQ5OlPXuzKnMKLe5lyKLqrAU9Kfueam4dzITjlZlOlBT3Au-mzTDlPskGUQG1nHcK4oQqO2gaqHLv91jlW09K7AG-sn-FNgHctz8OxX8_Lc9KAsc4DOVLzvyCZtXDWXeJlrskLSxFEQkUhL4UoDMiBGiw8ZNKEnPVUqDalyefWlE1TrAcsq5gpVQ5GrRdX21bevQTjQUqBHr992e__nTx-e_i6_-IC8EiEZh3a2qiSGmS3WKotMWQgX4AzpawwmSUzb0nQJUcMpyh-jqGlsFS_MV0Kz8HnrrhUWipES6VYbhJqf7os5pcKAiv-leAsNyAaovYxSUHMwczW1ANvEABamRXjuWGqUMwklGgThHEbFJIvjjr0-9vAj8iCgUv_v6TWlROud0qe_0V4vuOCXdZPzDhtKkBY6ahsO5wg3IKMs4tKLrg72dJ7QNO0SSxzw_Oy3tQqLTTFdgmhMu3_8TxFjn3yJzzXMXoRGGAGhb2wcTSto3FtW4UGHa43jV6zwcsqi-dO2bBbkBy5jPToOaGSjFi6-qpkXZrx85NR0L-nXBeCvCc5ObMhnbdu_Ss5s-uq90nrggkBDwNkn_zaZ2mCuZJrhGGP2sQbVu5uVPz8qDi4yulRCk7fUQE_k8L9GKDeBv-s8BBm5j2xTNighz1aiEcHMEA-bkk4xm7_XrDKlw08D3ACmM_KD7RGjA_k-mY3lsmQF9bFxpGGupYbS_Oy5Ns_2-tC2IebCaQj1-Sutq7sI_tlnyA9AYRPyXiDcFehLtuzPl3nudqi6guvPGu8gua0ara_VG23X8Tdjib2yvXf11viDBzgqOXfrcR3BakPPwLY2sBB6nXW2lnLLu83-_23B9esYoyCcFNZvIfbSFEV8i-bpwQIXENv7ZF176O3P17qYBnnfnvP-jSliYb-IvLj1WAhqgt-gKN2uW8YqFtGrjAeMGQ7kjvS9CpNuwC7YwVXTFvBdnk9rZuKrk_fN9oMi2RlewHqtv4pNlRO47rNgIC5bUjQt6IjCXVBajbuOnUqHYTvW6iD0E6lFb7FHMlww4x6EdM3TxflE2o0y-PVDvR6_zFwcGE_oYM8Ew57Bo9Enaj1OD8wEnV6cZPicFvX8-JLqvZ-wVQn98PD70xp13Z1zdUNBIuHXphycfujqLgw7QaE_zds4hbWtRhqDfcNvag9BXhwk1HkBg_2RgVPmT320-io5Bk9xr-46WNej0BWWZRycsqlNpz6-WTmREHdBvfCGZhfpp3B7rRpRjfQVqbr28W70Pxqu9qbP5EpWwhDQ2bvgORyMOLOQMY6eVN2tIQRGKZgS9i1qp5f8lRSyrT-gB1MiexecWknNlm5VdpwKw7jTQTz3LAPVbbC4VVfqad9GEFhg3U7Er6W521z26O2QVpyN_zDTuQgMcBuGnJf2HSFPkqFfHhzVLX49rGJBM5mTzX49Wknfq6ltn7b1qpe9BrFiA9Zse7RG_iu92mRAA51sKfsSErRgPDDwa-FPVa_Vpc_X2L1VVojiX08ow-otb4ab9ZK9UQOX-2Otx2NsS-10VhgNTuAdu0freFDtduevWimfPXe2De3HynlvYPQrsvbUxjUTG3XyFX51meUNp06RY-QqiN5NnZw0zmCbh1cy9IUpUHcoELJtKRMj51ct0rY5QfUPzSPpvPp3IF1Qnzm49uuTSb1xRWMAf6Cy40B_geMAa12t0KdTNDIDROQskf4bpxwFBrd9iC5AUlwUxL8T0oSNiUJxyUZQF00URefQl02UZefQr1tot6OoQ7eozwc0bsskWame6RoJDKqtFeiDVd95UJAVTLEX2e6BhlVhwH-1lexQjHNcn8rK4-IEZqhXKZsijba8qQkR_KgmXrxTIjp4nlGLljqW14Af804zZAuTyemjXbY3FT3v0Jqg9LSjfsgpbt7BsQ_Sm0sTKFkwZR4R-wNEsHABe3_YCT-7IVtO0P9l6Ppp6P75--Q_yn9PpYz_v7uhYPa_e0M9DOGXnxUlI9ltL9vk-VHBflYfvwZm9wOiDKYb93nrlQmYwoZpo0_NuSUGObzGGQhTc4MkaIQHN7KRvbT4NGEZiyF907qMzOZTPW0z-omvQvTdbgmN-xuvprjcDELl_gmuwsJpXO6nC3DQ7QgywWjmKzxcRGRxe2BrvENv8MzvJgtZst5NFsu1tPbKEzxIk2j9WK1jmYkWMzYmXAxFeLlPJXqdMO1LtldtIrm4Y0gBya0_X9CGOfsFdlFe562vVF3gDM5lCcdLGaCa6NrKoYbwe4EP1TuUIdwZQaGZN7YaJ_1-4kc5kBXFQhS7qZKHhFBZ0KVvCmVuMuMKWzXBQ3o7sRNVh6mVJ4DvAOB_NekUPIPRk2Ad1YNHeCdVfM_AQAA__9mgd8c">