Я разрабатываю приложение 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]
Я разрабатываю приложение clang libTooling, которое инструментирует (переписывает) код C. Чтобы подготовить код для RecursiveASTVisitor (где окончательный код переписывается), мне нужно предварительно раскрыть все вызовы макросов в коде C. Я сталкиваюсь с проблемами при обработке вложенных макросов и макросов, подобных функциям, где параметр также является макросом (я думаю, это можно рассматривать как вариант вложенного макроса). Я реализовал частичное решение, создав MacroExpander (подкласс clang::PPCallbacks), который частично расширяет макросы. Отредактированный текст вопроса Я добавил следующий блок кода в логика, которая обрабатывает расширение параметров макроса functionLike (это обновление кода также отражено в включенном примере кода), показывая, где именно мне нужна помощь. В частности, когда у нас есть параметр макроса, который требует расширения, мне нужно изменить его выражение. [code]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);
// 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));
Я пытаюсь запустить минимальный пример LibTooling, но, должно быть, упускаю что-то очевидное, поскольку мой посетитель AST вообще не вызывается, и любые ошибки во входных файлах не диагностируются.
Вот мой код:
#include
#include
#include...
Я строю простой инструмент с либеральным классом Clang, я знаю, что плагины Clang предлагают реестр атрибутов для регистрации ваших собственных атрибутов, и я хочу избежать чего -то вроде Clang :: Annotate, поскольку он немного уродливый, есть ли...
является той проблемой с проблемой компилятора macos g ++ (при использовании оптимизации)?
Проблема состоит в том, что при распределении с () или {} объектом с пустым конструктором, компилятор неверно думает, что объект ненициализируется, и, таким...
Является ли Clang-tidy '-Checks = clang-analyzer-*' падение замены для Scan-Build и clang-ceck -нализис ? Если я использую первое, есть ли необходимость снова использовать последние? Несмотря на то, что ответы там говорят Да , нет связанных...
Я разрабатываю приложение boost wave для сопоставления нерасширенных экземпляров макросов в исходном файле C с соответствующим расширенным текстом макроса.
Сопоставление должно работать как для функции, так и для объекта. -подобные вызовы макросов....