Clang: как получить имя макроса, используемого для определения размера массива постоянного размера - PullRequest
1 голос
/ 09 июня 2019

TL; DR;

Как получить имя макроса, используемого для размера объявления массива постоянного размера, из callExpr -> arg_0 -> DeclRefExpr.

Подробное описание проблемы:

Недавно я начал работать над проблемой, которая требует инструмента преобразования источника в источник для изменения конкретные вызовы функций с дополнительным аргументом. Исследования о том, как я могу достичь, представили меня в этот удивительный набор инструментов Clang. Я изучал, как использовать различные инструменты, предоставляемые в libtooling для достичь моей цели. Но теперь я застрял в проблеме, обратитесь за помощью здесь.

Рассмотрим приведенную ниже программу (фиктивная из моих источников), моя цель - переписать все вызовы strcpy. функция с безопасной версией strcpy_s и добавление дополнительного параметра в новый вызов функции т.е. максимальный размер указателя назначения. так что для приведенной ниже программы мой рефакторинг будет strcpy_s (inStr, STR_MAX, argv [1]);

Я написал класс RecursiveVisitor и проверял все вызовы функций в методе VisitCallExpr, чтобы получить максимальный размер я получаю VarDecl первого набора и пытаюсь получить размер (ConstArrayType). поскольку исходный файл уже предварительно обработан, я вижу размер 2049, но мне нужен макрос STR_MAX в этот случай. как я могу получить это? (Создание замен с этой информацией и использование RefactoringTool после их замены)

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 

#define STR_MAX 2049

int main(int argc, char **argv){
  char inStr[STR_MAX];

  if(argc>1){
    //Clang tool required to transaform the below call into strncpy_s(inStr, STR_MAX, argv[1], strlen(argv[1]));
    strcpy(inStr, argv[1]);
  } else {
    printf("\n not enough args");
    return -1;
  }

  printf("got [%s]", inStr);

  return 0;
}

1 Ответ

0 голосов
/ 09 июня 2019

Как вы правильно заметили, исходный код уже предварительно обработан и в нем развернуты все макросы.Таким образом, AST будет просто иметь целочисленное выражение в качестве размера массива.

Немного информации о расположении источника

ПРИМЕЧАНИЕ : вы можете пропуститьи перейдем непосредственно к решению, приведенному ниже

Информация о расширенных макросах содержится в исходных местоположениях узлов AST и обычно может быть получена с использованием Lexer (лексер и препроцессор Кланга оченьтесно связаны и могут даже рассматриваться как одно целое).Это минимум и не очень очевидно для работы, но это то, что есть.

Поскольку вы ищете способ получить оригинальное имя макроса для замены, вам нужно только получить орфография (то есть, как это было написано в оригинальном исходном коде), и вам не нужно много рассказывать об определениях макросов, макросах в стиле функций и их аргументах и ​​т. д.

У Clang есть дватипы различных местоположений: SourceLocation и CharSourceLocation .Первый может быть найден почти везде через AST.Это относится к позиции с точки зрения токенов .Это объясняет, почему позиции начало и конец могут быть несколько нелогичными:

// clang::DeclRefExpr
//
//  ┌─ begin location
foo(VeryLongButDescriptiveVariableName);
//  └─ end location
// clang::BinaryOperator
//
//           ┌─ begin location
int Result = LHS + RHS;
//                 └─ end location

Как вы можете видеть, этот тип местоположения источника указывает на началосоответствующего токена. CharSourceLocation , с другой стороны, указывает непосредственно на символов .

Итак, чтобы получить исходный текст выражения, нам нужно преобразовать SourceLocation до CharSourceLocation и получите соответствующий текст из источника.

Решение

Я изменил ваш пример, чтобы показать другие случаитакже расширения макросов:

#define STR_MAX 2049
#define BAR(X) X

int main() {
  char inStrDef[STR_MAX];
  char inStrFunc[BAR(2049)];
  char inStrFuncNested[BAR(BAR(STR_MAX))];
}

Следующий код:

// clang::VarDecl *VD;
// clang::ASTContext *Context;
auto &SM = Context->getSourceManager();
auto &LO = Context->getLangOpts();
auto DeclarationType = VD->getTypeSourceInfo()->getTypeLoc();

if (auto ArrayType = DeclarationType.getAs<ConstantArrayTypeLoc>()) {
  auto *Size = ArrayType.getSizeExpr();

  auto CharRange = Lexer::getAsCharRange(Size->getSourceRange(), SM, LO);
  // Lexer gets text for [start, end) and we want him to grab the end as well
  CharRange.setEnd(CharRange.getEnd().getLocWithOffset(1));

  auto StringRep = Lexer::getSourceText(CharRange, SM, LO);
  llvm::errs() << StringRep << "\n";
}

создает этот вывод для фрагмента:

STR_MAX
BAR(2049)
BAR(BAR(STR_MAX))

Надеюсь, эта информация полезна.Счастливого взлома с Clang!

...