Подтвердить что ты не робот

Автоматически обнаруживать идентичные последовательные вызовы std::string:: find()

Во время просмотра кода я нашел исходный код следующим образом:

void f_odd(std::string &className, std::string &testName)
{
   if (className.find("::") != std::string::npos)
   {
     testName = className.substr(className.find("::") + 2);
     (void)className.erase(className.find("::"), std::string::npos);
   }
}

Внутри этой функции std::string:: find() вызывается три раза с тем же шаблоном (здесь "::" ).

Этот код, конечно, может быть реорганизован на

void f(std::string &className, std::string &testName)
{
   const size_t endOfClassNamePos = className.find("::");
   if (endOfClassNamePos != std::string::npos)
   {
     testName = className.substr(endOfClassNamePos + 2);
     (void)className.erase(endOfClassNamePos, std::string::npos);
   }
}

где find вызывается только один раз.

Вопрос

Кто-нибудь знает стратегию обнаружения такого шаблона, как это? У меня огромная база кода, где я собираюсь определить этот шаблон. Я планирую использовать среду Windows или Linux.

Потенциальные стратегии

  • Используйте/адаптируйте инструмент анализа статического кода, например, cppcheck, чтобы обнаружить эти странности.
  • Поиск в базе кода с регулярным выражением.
  • Используйте/адаптируйте clang-tidy для обнаружения этого шаблона.
  • Напишите специальную проверку на каком-либо языке (например, Python), который обнаруживает эти проблемы. В этом случае проверка должна выполняться по предварительно обработанному коду.

Нет Go

  • Обзор вручную

Обновление 1

Я решил начать с потенциальной стратегии 1). Я планирую адаптировать cppcheck, чтобы поймать эту проблему.

Cppcheck предлагает возможность писать настраиваемые правила на основе регулярных выражений PCRE. Для этого cppcheck должен быть скомпилирован с поддержкой поддержки PCRE. Поскольку текущая среда тестирования основана на Linux, следующие команды можно использовать для загрузки последней версии cppcheck:

git clone https://github.com/danmar/cppcheck.git && cd cppcheck

После этого скомпилируйте и установите инструмент следующим образом:

sudo make install HAVE_RULES=yes

Теперь выполняется базовая настройка инструмента. Чтобы разработать правило cppcheck, я подготовил простой тестовый пример (файл: test.cpp), похожий на пример кода в первом разделе этой статьи. Этот файл содержит три функции, и правило cppcheck должно выдать предупреждение на f_odd и f_odd1 о последовательных одинаковых вызовах std::string::find.

test.cpp:

#include <string>
void f(std::string &className, std::string &testName)
{
   const size_t endOfClassNamePos = className.find("::");
   if (endOfClassNamePos != std::string::npos)
   {
     testName = className.substr(endOfClassNamePos + 2);
     (void)className.erase(endOfClassNamePos, std::string::npos);
   }
 }

 void f_odd(std::string &className, std::string &testName)
 {
    if (className.find("::") != std::string::npos)
    {
       testName = className.substr(className.find("::") + 2);
       (void)className.erase(className.find("::"), std::string::npos);
    }
 }

 #define A "::"
 #define B "::"
 #define C "::"
 void f_odd1(std::string &className, std::string &testName)
 {
   if (className.find(A) != std::string::npos)
   {
      testName = className.substr(className.find(B) + 2);
      (void)className.erase(className.find(C), std::string::npos);
   }
 }

Пока все хорошо. Теперь cppcheck должен быть изменен, чтобы поймать последовательные идентичные вызовы std::string::find. Для этого я создал cppcheck_rule файл, который содержит регулярное выражение, которое соответствует последовательным идентичным вызовам std::string::find:

<?xml version="1.0"?>
<rule>
<tokenlist>normal</tokenlist>
<pattern><![CDATA[([a-zA-Z][a-zA-Z0-9]*)(\s*\.\s*find)(\s*\(\s*\"[ -~]*\"\s*\))[ -\{\n]*(\1\2\3)+[ -z\n]]]></pattern>
<message>
    <severity>style</severity>
    <summary>Found identical consecutive std::string::find calls.</summary>
</message>

Этот файл можно использовать для расширения cppcheck о новой проверке. Попробуем:

cppcheck --rule-file=rules/rule.xml test/test.cpp

а выход -

Checking test/test.cpp...
[test/test.cpp:14]: (style) Found identical consecutive std::string::find calls.
[test/test.cpp:26]: (style) Found identical consecutive std::string::find calls.

Теперь идентичные последовательные вызовы std::string::find могут быть обнаружены в кодах C/С++. Кто-нибудь знает лучшее/более эффективное или более умное решение?

Литература:


4b9b3361

Ответ 1

Основная проблема с таким инструментом заключается в том, что лексический анализ может только проверять, есть ли текстовое повторение. Например, вызов className.find("::") дважды является потенциальной проблемой, если переменная ссылается на одну и ту же строку дважды. Но позвольте мне добавить небольшое изменение в ваш код: className = className.substr(className.find("::") + 2);. Внезапно значение следующего className.find резко изменилось.

Вы можете найти такие изменения? Для этого нужен полномасштабный компилятор, и даже тогда вы должны быть пессимистичными. Придерживаясь вашего примера, можно ли className изменить через итератор? Это не просто прямые манипуляции, о которых вы должны знать.

Нет ли положительных новостей? Ну: существующие компиляторы имеют аналогичный механизм. Он называется Common Subexpression Elimination, и он работает концептуально, так как вы хотите, чтобы он работал в приведенном выше примере. Но это также плохая новость одним способом: если ситуация обнаружима, это неважно, потому что она уже оптимизирована компилятором!