V2661. MISRA. A 'for' loop should be well-formed.
Диагностическое правило основано на руководстве MISRA (Motor Industry Software Reliability Association) по разработке программного обеспечения.
Правило актуально только для языка C.
Цикл for должен быть отформатирован в соответствии со следующими правилами:
Первый оператор цикла (Init-statement) должен:
- либо быть пустым;
- либо не иметь постоянных побочных эффектов;
- либо определять и инициализировать счётчик цикла.
Второй оператор (Condition) должен:
- не иметь постоянных побочных эффектов;
- использовать счётчик или его же совместно с флагом управления циклом;
- не использовать объекты, которые модифицируются в теле цикла.
Третий оператор цикла (Expression) должен:
- не иметь постоянных побочных эффектов, кроме модификации счетчика цикла;
- не использовать объекты, которые модифицируются в теле цикла.
Соблюдение правил оформления циклов for снижает вероятность ошибок, а также упрощает процесс отладки и обзора кода. Такой подход позволяет сразу видеть всю логику работы цикла в его заголовке, исключая непредсказуемое поведение.
Примечание. Допустимо использовать цикл с тремя пустыми операторами for( ; ; ).
Рассмотрим пример некорректного кода:
for (size_t i = 0;
i < users.size() && retry_count < MAX_RETRIES; // <=
i += (retry_count > 0 ? 0 : 1)) // <=
{
if (!checkUser(users[i]))
{
retry_count += 1; // <=
}
else
{
retry_count = 0; // <=
}
// ....
}
Опасность кода заключается в неочевидном зависании цикла на проблемном пользователе. Если функция checkUser постоянно возвращает false при обработке какого-то пользователя, цикл застревает на одной позиции, пока не исчерпает все попытки. Это приводит к пропуску всех последующих пользователей, что бывает сложно обнаружить при обзоре кода.
В данном случае правильное форматирование запрещает использование retry_count во втором и третьем операторе цикла for, поскольку эта переменная модифицируется в теле цикла. Чтобы исправить проблему, можно вынести механизм валидации в отдельную функцию. Тогда цикл будет работать предсказуемо и обрабатывать всех пользователей.
Исправленный код:
bool checkUserWithRetry(const std::string& user) {
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
if (checkUser(user)) return true;
}
return false;
}
// ....
for (size_t i = 0; i < users.size(); i++) {
if (!checkUserWithRetry(users[i])) {
logError("Failed to process user: " + users[i]);
}
}
// ....
Данная диагностика классифицируется как:
|