Как найти в моей программе выражение "const char * + int"

Я переношу исходный код, и программа конвертера не конвертировала конкатенацию встроенных строк с целыми числами. Теперь у меня много кода с такими выражениями:

f("some text" + i);

Так как C/С++ интерпретирует это как индекс массива, f получит "some text" или "ome text", или "me text"...

Мой исходный язык преобразует конкатенацию строки с int как конкатенацию строки. Теперь мне нужно идти по строкам через исходный код и вручную изменить предыдущее выражение:

f("some text" + std::to_string(i));

Программе преобразования удалось преобразовать локальные переменные "String" в "std::string", в результате получились выражения:

std::string some_str = ...;
int i = ...;

f(some_str + i);

Это было легко исправить, потому что с такими выражениями компилятор С++ выводит ошибку.

Есть ли какой-нибудь инструмент для автоматического поиска таких выражений в исходном коде?

Ответ 1

Я нашел очень простой способ обнаружить эту проблему. Регулярное выражение или lint не будут соответствовать более сложным выражениям следующим образом:

f("Hello " + g(i));

Что мне нужно, так это сделать вывод типа, поэтому я разрешаю компилятору сделать это. Использование std::string вместо литеральной строки вызывает ошибку, поэтому я написал простой конвертер исходного кода, чтобы перевести все строковые литералы в завернутый std::string, например:

f(std::string("Hello ") + g(i));

Затем, после перекомпиляции проекта, я увижу все ошибки. Исходный код находится на GitHub, в 48 строках кода Python:

https://gist.github.com/alejolp/3a700e1730e0328c68de

Ответ 2

Легко! Просто замените все + на -&:

find . -name '*.cpp' -print0 | xargs -0 sed -i '' 's/+/-\&/g'


При попытке скомпилировать ваш проект вы увидите, между другими ошибками, что-то вроде этого:

foo.cpp:9:16: error: 'const char *' and 'int *' are not pointers to compatible types
    return f(s -& i);
             ~ ^~~~

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


Поэтому вам просто нужно отфильтровать вывод компилятора, чтобы сохранить только эти ошибки:

clang++ foo.cpp 2>&1 | grep -F "error: 'const char *' and 'int *' are not pointers to compatible types"

И вы получите:

foo.cpp:9:16: error: 'const char *' and 'int *' are not pointers to compatible types
foo.cpp:18:10: error: 'const char *' and 'int *' are not pointers to compatible types

Ответ 3

Вы можете попробовать flint, разработанную и используемую в Facebook программу с открытым исходным кодом для С++. Он имеет черную последовательность символов (checkBlacklistedSequences). Вы можете добавить свою последовательность токенов в функцию checkBlacklistedSequences, а flint сообщит о них.

в checkBlacklistedSequences, я добавил последовательность string_literal + number

BlacklistEntry([tk!"string_literal", tk!"+", tk!"number"],
               "string_literal + number problem!\n",
                true),

затем скомпилируйте и протестируйте

$ cat -n test.cpp
 1  #include <iostream>
 2  #include <string>
 3  
 4  using namespace std;
 5  
 6  void f(string str)
 7  {
 8      cout << str << endl;
 9  }
10  
11  int main(int argc, char *argv[])
12  {
13      f("Hello World" + 2);
14  
15      f("Hello World" + std::to_string(2));
16  
17      f("Hello World" + 2);
18  
19      return 0;
20  }

$ ./flint test.cpp 
test.cpp(13): Warning: string_literal + number problem!
test.cpp(17): Warning: string_literal + number problem!

flint имеет две версии (старая версия, разработанная на С++ и новая версия на языке D), я внесла свои изменения в версию D.

Ответ 4

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

В корневом каталоге исходного кода попробуйте:

grep -rn '".\+"\s*+\s*' .

который может найти все файлы, которые содержат строку типа "xxxxx" +, надеюсь, что это поможет вам найти все необходимые строки.

Если все целые числа являются постоянными, вы можете изменить выключение grep как:

grep -rn '".\+"\s*+\s*[0-9]*' .

И вы также можете включить ( перед строковой константой:

grep -rn '(".\+"\s*+\s*[0-9]*' .

Это может быть не "правильный" ответ, но я надеюсь, что это может вам помочь.

Ответ 5

Вам может не понадобиться внешний инструмент. Вместо этого вы можете использовать правило С++ для одного пользователя. В принципе, вам нужно изменить аргумент функции f от const char*/std::string к типу, который неявно конвертируется только из строкового литерала (const char[size]) или экземпляра std::string (что вы get, когда вы добавляете std::to_string в выражение).

#include <string>
#include <iostream>

struct string_proxy
{
    std::string value;

    string_proxy(const std::string& value) : value(value) {}

    string_proxy(std::string&& value) : value(std::move(value)) {}

    template <size_t size>
    string_proxy(const char (&str)[size]) : value(str) {}
};

void f(string_proxy proxy)
{
    std::cout << proxy.value << std::endl;
}

int main()
{
    f("this works"); // const char[size]
    f("this works too: " + std::to_string(10)); //  std::string
    f("compile error!" + 10); // const char*
    return 0;
}

Обратите внимание, что это не работает на MSVC, по крайней мере, не в версии 2012 года; это, скорее всего, ошибка, поскольку нет предупреждений. Он отлично работает в g ​​++ и clang (вы можете быстро проверить его здесь).

Ответ 6

Если ваш случай точно соответствует

"some text in quotations" + a_numeric_variable_or_constant

то Powergrep или подобные программы позволят вам сканировать все файлы для

("[^"]+")\s*\+\s*(\w+)

и замените на

\1 + std::to_string(\2)

Это приведет к возможным совпадениям с вами, но я настоятельно рекомендую сначала просмотреть, что вы заменяете. Потому что это также заменит строковые переменные.

Регулярные выражения не могут понять семантику вашего кода, поэтому они не могут быть уверены, что если они являются целыми числами. Для этого вам нужна программа с парсером, таким как CDT или анализаторы статического кода. Но, к сожалению, я не знаю, что это может сделать. Таким образом, я надеюсь, что регулярное выражение поможет:)

PS: В худшем случае, если переменные не являются числовыми, компилятор даст вам ошибку, потому что функция to_string не принимает ничего, кроме числовых значений. Может быть позже, вы можете вручную заменить только те, на которые я могу только надеяться, не будет больше.

PS 2: Некоторые могут подумать, что Powergrep стоит дорого. Вы можете использовать пробную версию в течение 15 дней с полной функциональностью.

Ответ 7

Вы можете попробовать плагин Map-Reduce Clang. Инструмент был разработан в Google, чтобы делать именно такой рефакторинг, смешивая сильную проверку типов и регулярное выражение.

(см. видео-презентацию здесь).

Ответ 8

Вы можете использовать оператор С++ typecasting и создать новый класс, который может перегрузить оператор + в соответствии с вашими потребностями. Вы можете заменить int на новый класс Integer и выполнить необходимую перегрузку. Это не требует изменений или замены слов в вызове основной функции.

class Integer{
    long  i;
    std::string formatted;
public:
     Integer(int i){i = i;}
     operator char*(){
        return (char*)formatted.c_str();}
     friend Integer operator +( char* input, Integer t);
};
Integer operator +( char* input, Integer integer) {
    integer.formatted = input + std::to_string(integer.i);
    return integer;
}
Integer i = ....
f("test" + i); //executes the overloaded operator

Ответ 9

Я предполагаю для функции f (some_str + i); ваше определение должно быть таким:

 void f(std::string value)
 {
    // do something.
 }

если вы объявите какой-либо другой класс, например AdvString, чтобы реализовать Operator + для межсегментов. если вы заявите свою функцию, как показано ниже. он будет работать как эта реализация f (some_str + i);

 void f(AdvString value)
 {
   // do something.
 }

пример реализации здесь https://github.com/prasaathviki/advstring