Libclang C++ не может проанализировать объявление поля вложенного шаблона

Мы хотим проанализировать следующий исходный файл:

#pragma once

#include "Vec3.h"

template<typename T> struct Array;

struct S1
{
    int S1i;
    Array<Array<Vec3>> S1Grid;
};

struct S2
{
    int S2i;
    Array<Array<Vec3> > S2Grid;
};

struct S3
{
    int S3i;
    Array<Array<char>> S3Grid;
};

Используя следующий код парсера:

#include <clang-c/Index.h>
#include <string>

static CXChildVisitResult CursorVisitorTest(CXCursor cursor, CXCursor parent, CXClientData client_data)
{
    CXCursorKind Kind = clang_getCursorKind(cursor);
    printf("%d %s\n", Kind, clang_getCString(clang_getCursorSpelling(cursor)));
    return CXChildVisit_Recurse;
}

void Test()
{
    CXIndex index = clang_createIndex(0, 0);
    std::string header_path = "Example.h";
    CXTranslationUnit TranslationUnit;
    static const char* args[] = { "-std=c++17", "-xc++", "-DHEADER_TOOL" };
    CXErrorCode error = clang_parseTranslationUnit2(
        index,
        header_path.c_str(),
        args,
        3,
        nullptr,
        0,
        CXTranslationUnit_SingleFileParse,
        &TranslationUnit
    );
    if (error == CXError_Success && TranslationUnit != nullptr)
    {
        CXCursor cursor = clang_getTranslationUnitCursor(TranslationUnit);
        clang_visitChildren(cursor, &CursorVisitorTest, nullptr);
        clang_disposeTranslationUnit(TranslationUnit);
    }
}

Мы получили следующий результат:

31 Array
27 T
2 S1
6 S1i
2 S2
6 S2i
6 S2Grid
2 S3
6 S3i
6 S3Grid
45 Array
45 Array

Мы заметили, что Clang не может проанализировать S1Grid как поле. Судя по тому, что S2Grid анализируется правильно, мы подозреваем, что >> в типе S1Gird анализируется как сдвиг вправо. Интересно, что S3Grid также правильно анализируется. Вероятно, потому, что char — это встроенный тип, а Vec3 — нет? Что мы можем сделать, чтобы lib clang правильно анализировал вложенный шаблон, не добавляя вручную пробел в исходном коде?

Версия Clang, возвращаемая clang_getClangVersion, равна clang version 9.0.0 (tags/RELEASE_900/final)

Я не знаю, как работают парсеры, но может ли проблема в том, что Array<Array<char>> является неполным типом?

Ted Lyngmo 02.08.2024 23:13

Неа. В этом случае он не сможет разобрать Array<Array<char> > Grid;. Это шаг токенизатора, с которым у clang возникают проблемы.

TSs 03.08.2024 00:34

Какую версию clang вы используете? И как вы определили, что ему «не удается проанализировать Grid как поле»?

Scott McPeak 03.08.2024 15:00

@ScottMcPeak Вопрос обновлен и включает дополнительную информацию.

TSs 04.08.2024 17:03

Спасибо за дополнительную информацию. Однако я по-прежнему не могу воспроизвести какую-либо проблему при использовании предоставленного кода и Clang 9.0.0; Я вижу две FieldDecl, одна из которых соответствует Grid. Я предлагаю вам отредактировать вопрос, включив в него полную программу, которую можно скомпилировать и запустить, с операторами печати в CursorVisitTest, а также включить выходные данные запуска этой программы и ее предположительно отсутствующие FieldDecl выходные данные.

Scott McPeak 04.08.2024 17:43

@ScottMcPeak Спасибо, что уделили время, и извините за все эти разговоры. Я думал, что это простая проблема: у меня отсутствует флаг или у меня нет последней версии. Я также не хотел раскрывать слишком большую часть нашей кодовой базы. Вопрос был обновлен с использованием минимального компилируемого примера кода.

TSs 05.08.2024 03:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
75
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Основная проблема — восстановление ошибок.

Входной файл Example.h содержит синтаксические ошибки. Во-первых, существует #include из Vec3.h, который не найден. Если это исправлено (просто закомментировав это), то Clang сообщает о дополнительных ошибках, в частности, Vec3 — это необъявленный идентификатор, что и есть вызывая проблему.

Поскольку существуют синтаксические ошибки, Clang пытается предоставить «лучший усилия» разобрать, что должен означать код, но это обязательно должны использовать некоторые эвристики для обработки ошибок, и эти эвристики несовершенны. При использовании Clang 9 и этого конкретного ввода эвристика восстановления, очевидно, не распознает >> как закрывающую разделитель вложенных идентификаторов шаблонов и, следовательно, S1Grid не является записано как поле в результирующем AST.

Решение № 1. Исправьте входной код

Хотя Clang можно использовать для анализа кода, содержащего синтаксические ошибки, и регулярно используется таким образом для поддержки IDE, это не то, что было изначально предназначено для этого, и в любом случае эвристика всегда будет несовершенным. Поэтому самое простое решение — убедиться, что код не содержит синтаксических ошибок перед его анализом.

Если вы хотите действовать именно так, вам следует изменить способ вы вызываете Clang для проверки синтаксических ошибок и сообщения о них. Посмотреть вопрос Есть ли способ получить значимое сообщение об ошибке при компиляции кода через libclang? для получения подробной информации о том, как это сделать.

Решение № 2. Обновите Clang до более поздней версии.

Для ввода этого примера Clang 9 не сообщает поле S1Grid, но Clang 11, 14 и 16 (все остальные, которые я тестировал) работают. Очевидно, В этом случае улучшена эвристика восстановления ошибок. Опять нет гарантировать, что эти версии будут делать правильные действия на каждом входе, но для этот, они делают.

Спасибо за ответ. После небольшого тестирования я обнаружил, что включаемый файл не обрабатывается, поскольку мы используем CXTranslationUnit_SingleFileParse. Нам это нужно, потому что мы хотим выполнять отражения кода для структур, определенных в определенной папке, а не для каких-либо структур, которые они включают. Не похоже, что clang может проанализировать исходный файл с учетом включений без фактического включения кода в заголовочный файл в единицу перевода.

TSs 05.08.2024 21:16

Я не уверен, что вы подразумеваете под «отражениями кода», но если ваша цель состоит в том, чтобы игнорировать структуры из включенных заголовков или, возможно, в зависимости от того, в какой папке находится заголовок, это можно сделать, исследовав исходное местоположение каждого объекта в единицу перевода и игнорируя те, которые выходят за рамки предполагаемой области. (И такая фильтрация требуется независимо от того, какой парсер вы используете; не существует синтаксического анализатора, который будет учитывать содержимое заголовка без включения его в TU AST.)

Scott McPeak 05.08.2024 21:27

Другие вопросы по теме