Мы хотим проанализировать следующий исходный файл:
#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> > Grid;
. Это шаг токенизатора, с которым у clang возникают проблемы.
Какую версию clang вы используете? И как вы определили, что ему «не удается проанализировать Grid как поле»?
@ScottMcPeak Вопрос обновлен и включает дополнительную информацию.
Спасибо за дополнительную информацию. Однако я по-прежнему не могу воспроизвести какую-либо проблему при использовании предоставленного кода и Clang 9.0.0; Я вижу две FieldDecl
, одна из которых соответствует Grid
. Я предлагаю вам отредактировать вопрос, включив в него полную программу, которую можно скомпилировать и запустить, с операторами печати в CursorVisitTest
, а также включить выходные данные запуска этой программы и ее предположительно отсутствующие FieldDecl
выходные данные.
@ScottMcPeak Спасибо, что уделили время, и извините за все эти разговоры. Я думал, что это простая проблема: у меня отсутствует флаг или у меня нет последней версии. Я также не хотел раскрывать слишком большую часть нашей кодовой базы. Вопрос был обновлен с использованием минимального компилируемого примера кода.
Входной файл Example.h
содержит синтаксические ошибки. Во-первых, существует
#include
из Vec3.h
, который не найден. Если это исправлено (просто
закомментировав это), то Clang сообщает о дополнительных ошибках,
в частности, Vec3
— это необъявленный идентификатор, что и есть
вызывая проблему.
Поскольку существуют синтаксические ошибки, Clang пытается предоставить «лучший
усилия» разобрать, что должен означать код, но это обязательно
должны использовать некоторые эвристики для обработки ошибок, и эти эвристики
несовершенны. При использовании Clang 9 и этого конкретного ввода
эвристика восстановления, очевидно, не распознает >>
как закрывающую
разделитель вложенных идентификаторов шаблонов и, следовательно, S1Grid
не является
записано как поле в результирующем AST.
Хотя Clang можно использовать для анализа кода, содержащего синтаксические ошибки, и регулярно используется таким образом для поддержки IDE, это не то, что было изначально предназначено для этого, и в любом случае эвристика всегда будет несовершенным. Поэтому самое простое решение — убедиться, что код не содержит синтаксических ошибок перед его анализом.
Если вы хотите действовать именно так, вам следует изменить способ вы вызываете Clang для проверки синтаксических ошибок и сообщения о них. Посмотреть вопрос Есть ли способ получить значимое сообщение об ошибке при компиляции кода через libclang? для получения подробной информации о том, как это сделать.
Для ввода этого примера Clang 9 не сообщает поле S1Grid
, но
Clang 11, 14 и 16 (все остальные, которые я тестировал) работают. Очевидно,
В этом случае улучшена эвристика восстановления ошибок. Опять нет
гарантировать, что эти версии будут делать правильные действия на каждом входе, но для
этот, они делают.
Спасибо за ответ. После небольшого тестирования я обнаружил, что включаемый файл не обрабатывается, поскольку мы используем CXTranslationUnit_SingleFileParse
. Нам это нужно, потому что мы хотим выполнять отражения кода для структур, определенных в определенной папке, а не для каких-либо структур, которые они включают. Не похоже, что clang может проанализировать исходный файл с учетом включений без фактического включения кода в заголовочный файл в единицу перевода.
Я не уверен, что вы подразумеваете под «отражениями кода», но если ваша цель состоит в том, чтобы игнорировать структуры из включенных заголовков или, возможно, в зависимости от того, в какой папке находится заголовок, это можно сделать, исследовав исходное местоположение каждого объекта в единицу перевода и игнорируя те, которые выходят за рамки предполагаемой области. (И такая фильтрация требуется независимо от того, какой парсер вы используете; не существует синтаксического анализатора, который будет учитывать содержимое заголовка без включения его в TU AST.)
Я не знаю, как работают парсеры, но может ли проблема в том, что
Array<Array<char>>
является неполным типом?