Як розібрати коментарі C++ з вихідного коду за допомогою clang

У нашій попередній публікації How to parse C++ ‘struct’ definition from source code using clang ми навчилися розбирати визначення struct C++ за допомогою парсера C++ clang. У цій публікації ми покажемо, як розібрати коментарі C++ з вихідного коду за допомогою clang.

parse_comments.cpp
#include <clang-c/Index.h>
#include <iostream>
#include <vector>
#include <string>
#include <cstring>

void extractCommentBlocks(CXTranslationUnit unit) {
    CXSourceRange range = clang_getCursorExtent(clang_getTranslationUnitCursor(unit));
    CXToken *tokens;
    unsigned numTokens;

    clang_tokenize(unit, range, &tokens, &numTokens);

    for (unsigned i = 0; i < numTokens; ++i) {
        CXTokenKind tokenKind = clang_getTokenKind(tokens[i]);
        if (tokenKind == CXToken_Comment) {
            CXString tokenSpelling = clang_getTokenSpelling(unit, tokens[i]);
            CXSourceLocation location = clang_getTokenLocation(unit, tokens[i]);

            unsigned line, column;
            CXFile file;
            clang_getFileLocation(location, &file, &line, &column, nullptr);

            std::cout << "Коментар у рядку " << line << ", колонка " << column << ": "
                      << clang_getCString(tokenSpelling) << std::endl;
            clang_disposeString(tokenSpelling);
        }
    }

    clang_disposeTokens(unit, tokens, numTokens);
}

int main() {
    CXIndex index = clang_createIndex(0, 0);
    const char *code = R"(
        // Це однорядковий коментар
        typedef struct {
            double a; /* Той дивний параметр */
            double b; /* Інший дивний параметр */
        } myParameters;

        /*
         * Це багаторядковий
         * блок коментарів
         */
        int function() {
            // Інший однорядковий коментар
            return 42; /* вбудований коментар */
        }
    )";

    CXUnsavedFile unsavedFile = {"test.cpp", code, (unsigned long)strlen(code)};
    CXTranslationUnit unit = clang_parseTranslationUnit(index, "test.cpp", nullptr, 0, &unsavedFile, 1, CXTranslationUnit_None);
    if (unit == nullptr) {
        std::cerr << "Не вдалося розібрати одиницю трансляції." << std::endl;
        return 1;
    }

    CXCursor cursor = clang_getTranslationUnitCursor(unit);
    extractCommentBlocks(unit);

    clang_disposeTranslationUnit(unit);
    clang_disposeIndex(index);
    return 0;
}

Як скомпілювати

В Ubuntu встановіть необхідні бібліотеки за допомогою

install_libclang19.sh
sudo apt -y install libclang-19-dev

і скомпілюйте код за допомогою

build_parse_comments.sh
g++ parse_comments.cpp -o parse_comments -std=c++17 -I/usr/lib/llvm-19/include -L/usr/lib/llvm-19/lib -lclang

Приклад виводу

Запустіть за допомогою ./parse_comments

parse_comments_output.txt
Коментар у рядку 2, колонка 9: // Це однорядковий коментар
Коментар у рядку 4, колонка 23: /* Той дивний параметр */
Коментар у рядку 5, колонка 23: /* Інший дивний параметр */
Коментар у рядку 8, колонка 9: /*
         * Це багаторядковий
         * блок коментарів
         */
Коментар у рядку 13, колонка 13: // Інший однорядковий коментар
Коментар у рядку 14, колонка 24: /* вбудований коментар */

Альтернативна збірка за допомогою cmake

Можливо, вам доведеться скоригувати версії (19.1.1) та інші параметри тут, щоб це правильно працювало для вашого налаштування.

CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(parse_comments)

# Встановити стандарт C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Знайти компоненти LLVM/Clang
find_package(LLVM 19.1.1 REQUIRED CONFIG)
find_package(Clang REQUIRED CONFIG)

# Додати каталоги include
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CLANG_INCLUDE_DIRS})

# Створити виконуваний файл
add_executable(parse_comments parse_comments.cpp)

# Додати визначення LLVM
target_compile_definitions(parse_comments PUBLIC ${LLVM_DEFINITIONS})

# Зв'язати з libclang
target_link_libraries(parse_comments PUBLIC libclang)

Дивіться схожі статті за категоріями: C/C++ Clang Source Introspection