Обработка вложенных расширений макросов в приложении Clang LibToolingC++

Программы на C++. Форум разработчиков
Ответить Пред. темаСлед. тема
Anonymous
 Обработка вложенных расширений макросов в приложении Clang LibTooling

Сообщение Anonymous »

Я разрабатываю приложение clang libTooling, которое инструментирует (переписывает) код C. Чтобы подготовить код для RecursiveASTVisitor (где окончательный код переписывается), мне нужно предварительно раскрыть все вызовы макросов в коде C. Я сталкиваюсь с проблемами при обработке вложенных макросов и макросов, подобных функциям, где параметр также является макросом (я думаю, это можно рассматривать как вариант вложенного макроса).
Я реализовал частичное решение, создав MacroExpander (подкласс clang::PPCallbacks), который частично расширяет макросы.
Отредактированный текст вопроса
Я добавил следующий блок кода в логика, которая обрабатывает расширение параметров макроса functionLike (это обновление кода также отражено в включенном примере кода), показывая, где именно мне нужна помощь. В частности, когда у нас есть параметр макроса, который требует расширения, мне нужно изменить его выражение.

Код: Выделить всё

std::vector expandedMacroArgs;
for (auto argNum = 0u; argNum < Args->getNumMacroArguments(); ++argNum) {
if (const auto* token = Args->getUnexpArgument(argNum)) {
// Check if macro argument is an unexpanded macro
if (Args->ArgNeedsPreexpansion(token, mPP)) {
// @BEGIN
// THIS IS WHERE I NEED HELP TO EXPAND argNum to its original expansion
std::vector tokens;
while (token->isNot(clang::tok::eof)) {
tokens.emplace_back(*token);
++token;
}
const auto& expandedMacroArg = tokens
| std::views::transform([&](const auto& next) {
return mPP.getSpelling(next);
})
| std::views::join_with(std::string(" "))
| std::ranges::to();
expandedMacroArgs.emplace_back(
expandedMacroArg);
// @END
}
}
}

Примеры и ожидаемый результат
Рассмотрим, например, пару макросов, определенную как:
#define MIN(a, b) (((a)   (b)) ? (a) : (b))

Вот несколько простых примеров ожидаемого расширения макросов:
[list]
[*]MIN(1, 7) => (((1)  (((5) >  (8)) ? (5) : (8))
[*]MIN(MAX(5, 8), 7) => ((((((5) > (8)) ? (5) : (8)))  (8)) ? (5) : (8))) : (7))
[/list]
Последний пример является вложенным и может быть получен путем замены MAX(5, 8) на  в макросе MIN(a, b). то есть:
MIN(MAX(5, 8), 7) => (((MAX(5, 8)) isFunctionLike()) {
// I think there is always just one token in these single
// objectLike macros that do not contain parameters.
for (const auto next : MI->tokens()) {
MacroBody += mPP.getSpelling(next);
}
#if defined MACRO_DEBUGGING
// Print the macro name and body
const auto debugString = std::format(
"Macro {} expands to: {}"
, MacroName
, MacroBody);
llvm::errs() getUnexpArgument(argNum)) {
// Check if macro argument is an unexpanded macro
if (Args->ArgNeedsPreexpansion(token, mPP)) {
// @BEGIN
// THIS IS WHERE I NEED HELP TO EXPAND argNum to its original expansion
std::vector tokens;
while (token->isNot(clang::tok::eof)) {
tokens.emplace_back(*token);
++token;
}
const auto& expandedMacroArg = tokens
| std::views::transform([&](const auto& next) {
return mPP.getSpelling(next);
})
| std::views::join_with(std::string(" "))
| std::ranges::to();
expandedMacroArgs.emplace_back(
expandedMacroArg);
// @END
}
}
}

// This is the complicated function like macro invocation.

// hack together a map of macro parameter names to their values,
// so we can substitute them in the macro body as we expand the tokens.
std::vector macroArgNames;
for (const auto next : MI->params()) {
macroArgNames.emplace_back(next->getName().str());
}

std::vector macroArgValues;
for (unsigned i = 0u, e = Args->getNumMacroArguments(); i != e; ++i) {
if (const auto next = Args->getUnexpArgument(i);  next) {
// Handle address parameters using special case logic.
if (next->is(clang::tok::amp)) {
auto addressParam = mPP.getSpelling(*next);
auto specialToken = next + 1;
while (specialToken->isNot(clang::tok::eof)) {
addressParam += mPP.getSpelling(*specialToken);
++specialToken;
}
macroArgValues.emplace_back(addressParam);
} else if (next->is(clang::tok::identifier)) {
// check if parameter is unexpanded macro
if (mPP.getMacroInfo(next->getIdentifierInfo())) {
//auto bar = mPP.getMacroInfo(II->getReplacementToken(0);
//auto baz = mPP.getSpelling(bar);
}
macroArgValues.emplace_back(mPP.getSpelling(*next));
} else {
macroArgValues.emplace_back(mPP.getSpelling(*next));
}
}
}

// Make sure we have the same number of arguments as parameters.
if (macroArgValues.size() == macroArgNames.size()) {
std::map macroParamInfo;
for (auto i = 0; itokens()) {
if (next.is(clang::tok::identifier)) {
// identifiers not found in our macro parameters
// need to be forwarded directly to the output unmodified.
const auto identifier = mPP.getSpelling(next);
if (const auto iter = macroParamInfo.find(
identifier); iter != macroParamInfo.cend()) {
MacroBody += iter->second;
} else {
MacroBody += identifier;
}
if (bStringizing) {
MacroBody += '"';
bStringizing = false;
} else {
MacroBody += " ";
}
} else if (next.is(clang::tok::hash)) {
// turn on bStringizing flag
MacroBody += '"';
bStringizing = true;
} else if (next.is(clang::tok::hashhash)) {
// do nothing (effectively) token pastes
MacroBody = util::trim_str(MacroBody);
} else if (next.isOneOf(
clang::tok::l_paren, clang::tok::r_paren,
clang::tok::l_brace, clang::tok::r_brace,
clang::tok::l_square, clang::tok::r_square,
clang::tok::colon, clang::tok::equal,
clang::tok::comma, clang::tok::semi)) {
// Punctuation
MacroBody = util::trim_str(MacroBody);
MacroBody += mPP.getSpelling(next);
} else {
MacroBody += mPP.getSpelling(next);
if (bStringizing) {
MacroBody += '"';
bStringizing = false;
} else {
MacroBody += " ";
}
}
}

#if defined (MACRO_DEBUGGING)
// Print the macro name and body
// For example if macro is "#define MAX(a, b) ...",
// commaSeparatedParams are "a, b".
const std::vector paramVec(MI->params().begin(), MI->params().end());
const auto&  commaSeparatedParams = paramVec
| std::views::transform([&](const clang::IdentifierInfo* next) {
return next->getName().str();
})
| std::views::join_with(std::string(", "))
| std::ranges::to();

// For example if macro usage is "MAX(1, 2) ...",
// commaSeparatedArgs are "1, 2".
std::vector argTokens;
for (unsigned i = 0u, e = Args->getNumMacroArguments(); i != e; ++i) {
if (const auto next = Args->getUnexpArgument(i); next) {
argTokens.emplace_back(*next);
}
}
const auto& commaSeparatedArgs = argTokens
| std::views::transform([&](const auto& next) {
return mPP.getSpelling(next);
})
| std::views::join_with(std::string(", "))
| std::ranges::to();
const auto debugString = std::format(
"Macro {}({}) expands to: {}"
, MacroName
, commaSeparatedArgs
, MacroBody);
llvm::errs() =
#define LT                      <
#define LE                       (b)) ?  \
/*LHS*/pow((a), 2) :   \
/*RHS*/pow((b), 3) )
#define KNOT_TO_MSEC(a)     ((a)*0.514444)
#define STANDARD_GRAVITY    (9.80665)
#define LON_LIMIT           180.0000241664
#define LAT_LIMIT           (LON_LIMIT/2.0)
#define LOG(format, ...)    printf(format, __VA_ARGS__)

void
variadicFunctionTest(int count, ...) {
va_list args;
va_start(args, count);
for (int i = 0; i < count;  i++) {
int num = va_arg(args, int);
LOG("Argument %d: %d\n", i+1, num);
}
va_end(args);
}

void foo() {
int a, b, c, d, e, f, g;
int t = 0;

variadicFunctionTest(3, 1, 2, 3);

// double bar = MULTI_LINE_MACRO(1, 3);
double bar = MULTI_LINE_MACRO(1, 3);

// double bar = KNOT_TO_MSEC(123.0) / STANDARD_GRAVITY;
double foo = KNOT_TO_MSEC(123.0) / STANDARD_GRAVITY;

// a = b > c ? d : e;
a = b > c ? d : e;

// a = (b > c) ? (a LT b) ? c : d : TRUE;
a = (b > c) ? (a LT b) ? c : d : TRUE;

// a = b GT c  ? d : e;
a =  b GT c  ? d : e;

// a = (b GT c) ? (a LT b) : FALSE ? d : e;
a = (b GT c) ? (a LT b) : FALSE ? d : e;

// a = MIN(1, 7);
a = MIN(1, 7);

// a = MAX( MIN(e, f), g);
a = MAX( MIN(e, f), g);
}

Вывод теста (обрезанный вверху..)
Нам не нужно видеть все расширения макросов #include .
Вывод теста (обрезанный вверху..)
Нам не нужно видеть все расширения макросов #include .
Вывод теста (обрезанный вверху..)
Нам не нужно видеть все расширения макросов #include .
Вывод теста (обрезан вверху..)
Нам не нужно видеть все расширения макросов #include .
 p>
...
Macro va_arg expands to: __crt_va_arg
Macro __crt_va_arg(args, int) expands to: ((sizeof(int)> sizeof(__int64)||(sizeof(int)&(sizeof(int)- 1))!= 0)? * *(int * *)((args += sizeof(__int64))- sizeof(__int64)):*(int *)((args += sizeof(__int64))- sizeof(__int64)))
Macro LOG("Argument %d: %d\n", i) expands to: printf("Argument %d: %d\n" ,i)
Macro va_end expands to: __crt_va_end
Macro __crt_va_end(args) expands to: ((void)(args=(va_list)0))
Macro MULTI_LINE_MACRO(1, 3) expands to: (((1)>(3))? pow((1),2):pow((3),3))
Macro KNOT_TO_MSEC(123.0) expands to: ((123.0)* 0.514444)
Macro STANDARD_GRAVITY expands to: (9.80665)
Macro LT expands to: <
Macro TRUE expands to: 1
Macro GT expands to: >
Macro GT expands to: >
Macro LT expands to: <
Macro FALSE expands to: 0
Macro MIN(1, 7) expands to: (((1)(g))?(MIN):(g))
Macro MIN(e, f) expands to: (((e)
#define GE                      >=
#define LT                      <
#define LE                       (b)) ?  \
/*LHS*/pow((a), 2) :   \
/*RHS*/pow((b), 3) )
#define KNOT_TO_MSEC(a)     ((a)*0.514444)
#define STANDARD_GRAVITY    (9.80665)
#define LON_LIMIT           180.0000241664
#define LAT_LIMIT           (LON_LIMIT/2.0)
#define LOG(format, ...)    printf(format, __VA_ARGS__)

void
variadicFunctionTest(int count, ...) {
va_list args;
__crt_va_start(args, count);
for (int i = 0; i < count; i++) {
int num = __crt_va_arg(args, int);
printf("Argument %d: %d\n" ,i);
}
__crt_va_end(args);
}

void foo() {
int a, b, c, d, e, f, g;
int t = 0;

variadicFunctionTest(3, 1, 2, 3);

// double bar = MULTI_LINE_MACRO(1, 3);
double bar = (((1)>(3))? pow((1),2):pow((3),3));

// double bar = KNOT_TO_MSEC(123.0) / STANDARD_GRAVITY;
double foo = ((123.0)* 0.514444) / (9.80665);

// a = b > c ? d : e;
a = b > c ? d : e;

// a = (b > c) ? (a LT b) ? c : d : TRUE;
a = (b > c) ? (a < b) ? c : d : 1;

// a = b GT c  ? d : e;
a =  b > c  ? d : e;

// a = (b GT c) ? (a LT b) : FALSE ? d : e;
a = (b > c) ? (a < b) : 0 ? d : e;

// a = MIN(1, 7);
a = (((1)(g(((e)

Подробнее здесь: [url]https://stackoverflow.com/questions/78660201/handling-nested-macro-expansions-in-clang-libtooling-application[/url]
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Clang LibTooling не обрабатывает исходные файлы.
    Anonymous » » в форуме C++
    0 Ответы
    67 Просмотры
    Последнее сообщение Anonymous
  • Можно ли использовать Parsedattrinforegistry с Clang libtooling
    Anonymous » » в форуме C++
    0 Ответы
    8 Просмотры
    Последнее сообщение Anonymous
  • MacOS Clang Compiler выпуск (Apple Clang версия 17.0.0 (Clang-1700.0.13.5))))
    Anonymous » » в форуме C++
    0 Ответы
    15 Просмотры
    Последнее сообщение Anonymous
  • Является ли Clang-tidy '-Cecks = clang-analyzer-*' падение замены для сканирования и проверки Clang-Analyze?
    Anonymous » » в форуме C++
    0 Ответы
    9 Просмотры
    Последнее сообщение Anonymous
  • Как использовать boost::wave для расширения вложенных макросов
    Anonymous » » в форуме C++
    0 Ответы
    33 Просмотры
    Последнее сообщение Anonymous

Вернуться в «C++»