Айтор: Роман Фомичев
Наборы инструментов для виджетов используются для упрощения процесса разработки графического интерфейса приложения, и GTK+ является одним из них. Именно этот проект я выбрал для своей первой статьи об анализаторе PVS-Studio. Я просканировал код GTK+ с помощью PVS-Studio на наличие возможных ошибок и получил довольно много сообщений об ошибках и подозрительных фрагментах. Некоторые из них довольно критичны. Общее количество багов слишком велико для статьи, поэтому я расскажу только о некоторых из них, которые являются наиболее типичными.
Введение
GTK+ (аббревиатура от GIMP ToolKit) — кроссплатформенный набор инструментов для создания графических пользовательских интерфейсов. Он лицензирован в соответствии с условиями LGPL, что позволяет использовать его как бесплатному, так и проприетарному программному обеспечению. Это один из самых популярных наборов инструментов для оконных систем Wayland и X11, наряду с Qt.
Мы просканировали код инструментария статическим анализатором PVS-Studio версии 6.02 и изучили диагностические сообщения.
Избыточный код
Для начала давайте обсудим предупреждения, связанные с формированием логических выражений. Такие проблемы не всегда являются ошибками; это просто дополнительные проверки, которые усложняют чтение и понимание условий. Выражения с такими проверками можно значительно упростить.
V728 Излишняя проверка может быть упрощена. Оператор || окружен противоположными выражениями !mount и mount. gtkplacesview.c 708
static void add_volume (....) { .... GMount *mount; .... if (!mount || (mount && !g_mount_is_shadowed (mount))) .... }
Этот код содержит дополнительную проверку указателя mount и может быть изменен следующим образом:
if (!mount || !g_mount_is_shadowed (mount)))
Еще похожий случай:
V728 Излишняя проверка может быть упрощена. Оператор || окружен противоположными выражениями ret и !ret. gtktreeview.c 13682
void gtk_tree_view_get_cell_area (....) { .... gboolean ret = ...; .... /* Get vertical coords */ if ((!ret && tree == NULL) || ret) .... }
Еще одна лишняя проверка; на этот раз это логическая переменная ret. Упростим код:
if (ret || tree == NULL)
V590 Рассмотрите возможность проверки ‘str[0] == ‘\0’ || str[0] != Выражение ‘U’’. Выражение является избыточным или содержит опечатку. gtkcomposetable.c 62
static gboolean is_codepoint (const gchar *str) { int i; /* 'U' is not code point but 'U00C0' is code point */ if (str[0] == '\0' || str[0] != 'U' || str[1] == '\0') return FALSE; for (i = 1; str[i] != '\0'; i++) { if (!g_ascii_isxdigit (str[i])) return FALSE; } return TRUE; }
Проверка str[0] == ‘\0’ является избыточной, так как это частный случай выражения str[0] != ‘U’. Мы можем упростить код, убрав лишнюю проверку:
if (str[0] != 'U' || str[1] == '\0') return FALSE;
Все эти проблемы на самом деле не являются ошибками. Код будет успешно выполнен; просто в нем есть какие-то ненужные проверки, которые тоже будут выполняться.
Повторное использование кода
Индустрия разработки программного обеспечения в значительной степени зависит от повторного использования кода. Действительно, зачем изобретать велосипед? Очень частым источником ошибок является техника копирования-вставки, когда блоки кода копируются, а затем немного редактируются. Программисты, как правило, пропускают такие блоки, забывая их исправлять, и это приводит к ошибкам. Одной из сильных сторон PVS-Studio является возможность обнаружения таких фрагментов.
Вот несколько примеров ошибок, вызванных неправильным использованием копипасты:
V523 Оператор тогда эквивалентен оператору еще. gtkprogressbar.c 1232
static void gtk_progress_bar_act_mode_enter (GtkProgressBar *pbar) { .... /* calculate start pos */ if (orientation == GTK_ORIENTATION_HORIZONTAL) { if (!inverted) { priv->activity_pos = 0.0; priv->activity_dir = 0; } else { priv->activity_pos = 1.0; priv->activity_dir = 1; } } else { if (!inverted) { priv->activity_pos = 0.0; priv->activity_dir = 0; } else { priv->activity_pos = 1.0; priv->activity_dir = 1; } } .... }
Блок оператора «if (ориентация == GTK_ORIENTATION_HORIZONTAL)» и соответствующий блок else содержат один и тот же код. Это может быть как неполный функционал, так и баг.
V501 Слева и справа от оператора › одинаковые подвыражения (box-›corner[GTK_CSS_TOP_RIGHT].horizontal). gtkcssshadowvalue.c 685
V501 Слева и справа от оператора › одинаковые подвыражения (box-›corner[GTK_CSS_TOP_LEFT].horizontal). gtkcssshadowvalue.c 696
static void draw_shadow_corner (.... GtkRoundedBox *box, ....) { .... overlapped = FALSE; if (corner == GTK_CSS_TOP_LEFT || corner == GTK_CSS_BOTTOM_LEFT) { .... max_other = MAX(box->corner[GTK_CSS_TOP_RIGHT].horizontal, box->corner[GTK_CSS_TOP_RIGHT].horizontal); .... } else { .... max_other = MAX(box->corner[GTK_CSS_TOP_LEFT].horizontal box->corner[GTK_CSS_TOP_LEFT].horizontal); .... } .... }
Макрос MAX получает в качестве аргументов идентичные переменные. Возможно, программист забыл заменить «GTK_CSS_TOP_RIGHT» и «GTK_CSS_TOP_LEFT» соответствующими постоянными значениями; или, может быть, сравнение должно было включать совсем другую переменную.
V501 Слева и справа от оператора | находятся одинаковые подвыражения G_PARAM_EXPLICIT_NOTIFY. gtkcalendar.c 400
static void gtk_calendar_class_init (GtkCalendarClass *class) { .... g_object_class_install_property (gobject_class, PROP_YEAR, g_param_spec_int ("year", P_("Year"), P_("The selected year"), 0, G_MAXINT >> 9, 0, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY| G_PARAM_EXPLICIT_NOTIFY)); .... }
В этом коде либо константа G_PARAM_EXPLICIT_NOTIFY была скопирована лишний раз, либо программист забыл заменить ее другой константой.
Другой похожий случай, связанный с константой G_PARAM_DEPRECATED:
V501 Слева и справа от оператора | одинаковые подвыражения G_PARAM_DEPRECATED. gtkmenubar.c 275
static void gtk_menu_bar_class_init (GtkMenuBarClass *class) { .... gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("internal-padding", P_("Internal padding"), P_("Amount of border space between ...."), 0, G_MAXINT, 0, GTK_PARAM_READABLE | G_PARAM_DEPRECATED|G_PARAM_DEPRECATED)); .... }
Ошибки, связанные с копированием и вставкой, часто можно обнаружить в длинных списках инициализации. Человеку их сложно заметить, и тут вам поможет статический анализатор.
Пример ниже содержит очень длинный список инициализации, поэтому неудивительно, что внутри него есть ошибка:
V519 Переменной impl_class-›set_functions два раза подряд присваиваются значения. Возможно, это ошибка. Проверить строки: 5760, 5761. gdkwindow-x11.c 5761
static void gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass) { .... GdkWindowImplClass *impl_class = GDK_WINDOW_IMPL_CLASS (klass); .... impl_class->set_decorations = gdk_x11_window_set_decorations; impl_class->get_decorations = gdk_x11_window_get_decorations; impl_class->set_functions = gdk_x11_window_set_functions; impl_class->set_functions = gdk_x11_window_set_functions; .... }
Сначала я подумал, что не хватает пары get-set, как и в предыдущем случае: set_functions и get_functions. Но оказалось, что в структуре GdkWindowImplClass нет поля get_functions. Возможно, программист по ошибке сделал лишнюю копию строки инициализации, а может быть, он хотел заменить ее каким-то другим кодом, но забыл об этом. В любом случае, им нужно убедиться, что они инициализируют все, что должно быть инициализировано, и при необходимости удалить лишний оператор.
Следующее предупреждение аналогично предыдущему:
V519 Переменной impl_class-›set_functions два раза подряд присваиваются значения. Возможно, это ошибка. Контрольные строки: 1613, 1614. gdkwindow-broadway.c 1614
static void gdk_window_impl_broadway_class_init (GdkWindowImplBroadwayClass *klass) { .... GdkWindowImplClass *impl_class = GDK_WINDOW_IMPL_CLASS (klass); .... impl_class->set_functions = gdk_broadway_window_set_functions; impl_class->set_functions = gdk_broadway_window_set_functions; .... }
Опять же, мы имеем дело с дублирующимся назначением «impl_class-›set_functions». Возможно, он был перенесен из предыдущего примера.
Иногда похожие функции копируются полностью, и программисты забывают модифицировать их тела. Давайте поможем этим забывчивым программистам и исправим найденные ошибки:
V524 Странно, что тело функции gtk_mirror_bin_get_preferred_height полностью эквивалентно телу функции gtk_mirror_bin_get_preferred_width. offscreen_window2.c 340
static void gtk_mirror_bin_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural) { GtkRequisition requisition; gtk_mirror_bin_size_request (widget, &requisition); *minimum = *natural = requisition.width; } static void gtk_mirror_bin_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural) { GtkRequisition requisition; gtk_mirror_bin_size_request (widget, &requisition); *minimum = *natural = requisition.width; }
В функции gtk_mirror_bin_get_preferred_height вместо requisition.width, вероятно, следует использовать «requisition.height». Тогда это должно выглядеть так:
*minimum = *natural = requisition.height;
Может, изначально так и было задумано и ошибки нет, но выглядит этот код странно.
Вот еще один пример, где, по-моему, тоже перепутаны ширина и длина:
V524 Странно, что тело функции gtk_hsv_get_preferred_height полностью эквивалентно телу функции gtk_hsv_get_preferred_width. gtkhsv.c 310
static void gtk_hsv_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural) { GtkHSV *hsv = GTK_HSV (widget); GtkHSVPrivate *priv = hsv->priv; gint focus_width; gint focus_pad; gtk_widget_style_get (widget, "focus-line-width", &focus_width, "focus-padding", &focus_pad, NULL); *minimum = priv->size + 2 * (focus_width + focus_pad); *natural = priv->size + 2 * (focus_width + focus_pad); } static void gtk_hsv_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural) { GtkHSV *hsv = GTK_HSV (widget); GtkHSVPrivate *priv = hsv->priv; gint focus_width; gint focus_pad; gtk_widget_style_get (widget, "focus-line-width", &focus_width, "focus-padding", &focus_pad, NULL); *minimum = priv->size + 2 * (focus_width + focus_pad); *natural = priv->size + 2 * (focus_width + focus_pad); }
Поскольку и высота, и ширина вычисляются одинаково, вероятно, лучше использовать одну функцию вместо двух. Но если эти выражения должны были отличаться, следует не забыть внести необходимые изменения.
В следующем примере не совсем понятно, какой аргумент нужно использовать вместо скопированного аргумента «имя_компонента», но явно странно сравнивать строку с самой собой:
V549 Первый аргумент функции strcmp равен второму аргументу. gtkrc.c 1400
GtkStyle * gtk_rc_get_style_by_paths (....) { .... pos = gtk_widget_path_append_type (path, component_type); if (component_name != NULL && strcmp (component_name, component_name) != 0) // <= gtk_widget_path_iter_set_name (path, pos, component_name); .... }
Продолжаем с предупреждениями, связанными с копированием кода:
V570 Переменная tmp_info присваивается самой себе. gtkimcontextxim.c 442
static GtkXIMInfo * get_im (....) { .... GtkXIMInfo *info; .... info = NULL; tmp_list = open_ims; while (tmp_list) { .... else { tmp_info = tmp_info; // <= break; } .... } if (info == NULL) { .... } .... }
Изучив этот код, можно сделать логичный вывод, что программист действительно хотел присвоить значение переменной «info»: только тогда код после «пока» имел бы смысл. Попробуем исправить:
info = tmp_info;
На этом мы закончили обсуждение ошибок, связанных с копированием кода. Один из выводов, который можно сделать из всего вышесказанного, заключается в том, что это очень распространенная схема ошибок, которая может долгое время оставаться скрытой. Эти ошибки, как правило, трудно найти, поскольку они не бросаются в глаза при беглом просмотре кода.
Обработка указателя
Следующая категория возможных ошибок связана с неправильным использованием указателей. Неосторожное обращение с указателем может привести к сбоям или неопределенному поведению.
V528 Странно, что указатель на тип char сравнивается со значением \0. Вероятно имелось в виду: *data-›groups[0] != ‘\0’. gtkrecentmanager.c 979
struct _GtkRecentData { .... gchar **groups; .... }; gboolean gtk_recent_manager_add_full (GtkRecentManager *manager, const gchar *uri, const GtkRecentData *data) { .... if (data->groups && data->groups[0] != '\0') .... .... }
По адресу data-›groups[0] находится gchar*, т.е. тоже указатель, который не идет ни в какое сравнение с ‘\0’. В этом примере указатель data-›groups[0] фактически сравнивается с нулевым указателем. Если программисту действительно нужно убедиться, что указатель не нулевой, то правильный способ сделать это следующий:
if (data->groups && data->groups[0] != NULL)
И если бы они хотели проверить символ, найденный по адресу ‘data-›groups[0]’, на то, что он является нулевым терминатором, то указатель должен был быть разыменован:
if (data->groups && *data->groups[0] != '\0')
Вот еще один похожий пример, который также имеет дело с некорректным сравнением:
V528 Странно, что указатель на тип char сравнивается со значением \0. Вероятно имелось в виду: *priv-›icon_list[0] == ‘\0’. gtkscalebutton.c 987
struct _GtkScaleButtonPrivate { .... gchar **icon_list; .... }; struct _GtkScaleButton { .... GtkScaleButtonPrivate *priv; }; static void gtk_scale_button_update_icon (GtkScaleButton *button) { GtkScaleButtonPrivate *priv = button->priv; .... if (!priv->icon_list || priv->icon_list[0] == '\0') .... }
Нужно быть осторожным при использовании указателей в C/C++. Если вы не уверены, что указатель действительно указывает на какие-либо данные, вы должны проверить его на нуль.
Доступ к блоку памяти с помощью нулевого указателя приведет к неопределенному поведению или сбою. Следующие диагностические сообщения предупреждают вас, когда может произойти такой опасный доступ.
V595 Указатель завершения использовался до того, как он был проверен на соответствие nullptr. Проверить строки: 2231, 2239. gtkentrycompletion.c 2231
static gboolean gtk_entry_completion_key_press (...., gpointer user_data) { .... GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data); if (!completion->priv->popup_completion) return FALSE; .... if (completion && completion->priv->completion_timeout) // <= { .... } .... }
В теле функции программист проверяет указатель завершения на значение null, а затем использует его:
if (completion && completion->priv->completion_timeout)
Эта проверка указывает на предположение программиста, что указатель может быть нулевым. Однако ранее в коде к этому указателю обращались без такой проверки:
if (!completion->priv->popup_completion) return FALSE;
Если указатель здесь окажется нулевым, мы получим неопределенное поведение. Либо отсутствует проверка указателя завершения ранее в коде, либо более поздняя проверка не имеет смысла и не нужна.
Таких случаев в коде тулкита более десятка, поэтому обсудим только один пример:
V595 Указатель dispatch-›backend использовался до того, как он был проверен на соответствие nullptr. Проверить строки: 1570, 1580. gtkprintbackendcups.c 1570
static void cups_dispatch_watch_finalize (GSource *source) { .... if (dispatch->backend->username != NULL) username = dispatch->backend->username; else username = cupsUser (); .... if (dispatch->backend) dispatch->backend->authentication_lock = FALSE; .... }
Указатель «dispatch-›backend» проверяется только после обращения к нему, поэтому этот код потенциально небезопасен.
Ниже приведен список других подобных проблем. Он не включает предупреждения о проверках указателей внутри макросов. Хотя у этих макросов тоже могут быть проблемы с использованием нулевых указателей, также возможно, что программист просто взял макросы, которые им подходили, вместе с проверками, которые им не нужны.
V595 Указатель impl-›toplevel использовался до того, как он был проверен на соответствие nullptr. Проверить строки: 514, 524. gdkwindow-x11.c 514
V595 Указатель pointer_info использовался до того, как он был проверен на соответствие nullptr. Проверить строки: 9610, 9638. gdkwindow.c 9610
V595 Указатель elt использовался до того, как он был проверен на соответствие nullptr. Проверить строки: 2218, 2225. gtktreemodelfilter.c 2218
V595 Указатель tmp_list использовался до того, как он был проверен на соответствие nullptr. Проверить строки: 5817, 5831. gtktreeview.c 5817
V595 Указатель «dispatch-›data_poll» использовался до того, как он был проверен на соответствие nullptr. Проверить строки: 1470, 1474. gtkprintbackendcups.c 1470
Другие ошибки
Наконец, мы обсудим группу разнообразных предупреждений о возможных алгоритмических ошибках или опечатках.
В следующем примере автор забыл написать операторы «break» в конце операторов «case»:
V519 Переменной type дважды подряд присваиваются значения. Возможно, это ошибка. Проверить строки: 187, 189. testselection.c 189
void selection_get (.... guint info, ....) { .... switch (info) { case COMPOUND_TEXT: case TEXT: type = seltypes[COMPOUND_TEXT]; case STRING: type = seltypes[STRING]; } .... }
Независимо от того, какое из трех значений будет присвоено «info», мы получим присваивание «type = seltypes[STRING];». Чтобы этого избежать, нам нужно добавить оператор «break»:
switch (info) { case COMPOUND_TEXT: case TEXT: type = seltypes[COMPOUND_TEXT]; break; case STRING: type = seltypes[STRING]; break; }
Следующий фрагмент очень подозрительный: одна переменная (‘i’) используется как счетчик и для внешнего, и для внутреннего цикла:
V535 Переменная i используется для этого цикла и для внешнего цикла. Проверить строки: 895, 936. gtkstyleproperties.c 936
void gtk_style_properties_merge (....) { .... guint i; .... for (i = 0; i < prop_to_merge->values->len; i++) { .... else if (_gtk_is_css_typed_value_of_type (data->value, G_TYPE_PTR_ARRAY) && value->value != NULL) { .... for (i = 0; i < array_to_merge->len; i++) g_ptr_array_add (array, g_ptr_array_index (array_to_merge, i)); } .... } .... }
Я не уверен, на какое значение будет ссылаться переменная «i» после выполнения внутреннего цикла и как после этого будет работать внешний цикл. Чтобы избежать этой неопределенности, внутренний цикл должен использовать собственный счетчик, например:
guint j; for (j = 0; j < array_to_merge->len; j++) g_ptr_array_add (array, g_ptr_array_index (array_to_merge, j));
Еще два случая потенциально небезопасного использования счетчиков циклов:
V557 Возможен переполнение массива. Значение индекса i + 1 может достигать 21. gtkcssselector.c 1219
V557 Возможен переполнение массива. Значение индекса i + 1 может достигать 21. gtkcssselector.c 1224
#define G_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) static GtkCssSelector * parse_selector_pseudo_class (....) { static const struct { .... } pseudo_classes[] = { { "first-child", 0, 0, POSITION_FORWARD, 0, 1 }, .... { "drop(active)", 0, GTK_STATE_FLAG_DROP_ACTIVE, } }; guint i; .... for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++) { .... { if (pseudo_classes[i + 1].state_flag == pseudo_classes[i].state_flag) _gtk_css_parser_error_full (parser, GTK_CSS_PROVIDER_ERROR_DEPRECATED, "The :%s pseudo-class is deprecated. Use :%s instead.", pseudo_classes[i].name, pseudo_classes[i + 1].name); .... } .... } .... }
Цикл основан на количестве элементов в массиве псевдо_классов, и я надеюсь, что он никогда не достигнет последнего элемента. В противном случае конструкция «pseudo_classes[i+1]» приведет к индексации за пределами массива.
Следующая потенциальная ошибка выглядит как опечатка:
V559 Подозрительное присваивание внутри условного выражения оператора если. gdkselection-x11.c 741
gboolean gdk_x11_display_utf8_to_compound_text (....) { .... GError *error = NULL; .... if (!(error->domain = G_CONVERT_ERROR && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)) .... }
Программисты часто ошибочно используют оператор присваивания «=» вместо оператора сравнения «==». Код будет скомпилирован, и некоторые компиляторы могут выдать предупреждение по этому поводу. Но вы не отключаете нежелательные предупреждения и не совершаете подобных ошибок, не так ли? Код, вероятно, должен выглядеть так:
if (!(error->domain == G_CONVERT_ERROR && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
В следующем примере анализатор предупреждает об условном if-операторе, содержащем выражение, которое всегда возвращает одно и то же значение:
V560 Часть условного выражения всегда ложна: !auto_mnemonics. gtklabel.c 2693
static void gtk_label_set_markup_internal (....) { .... gboolean enable_mnemonics = TRUE; gboolean auto_mnemonics = TRUE; g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)), "gtk-enable-mnemonics", &enable_mnemonics, NULL); if (!(enable_mnemonics && priv->mnemonics_visible && (!auto_mnemonics || (gtk_widget_is_sensitive (GTK_WIDGET (label)) && (!priv->mnemonic_widget || gtk_widget_is_sensitive (priv->mnemonic_widget)))))) .... }
На первый взгляд это не похоже на ошибку. Но если вы присмотритесь, то заметите, что переменная «enable_mnemonics» создается рядом с переменной «auto_mnemonics», а затем инициализируется значением из настроек. Возможно, значение для «auto_mnemonics» тоже должно быть получено аналогичным образом. А если нет, то проверку условия ‘!auto_mnemonics’ надо убрать, наверное.
Еще одно предупреждение по поводу переменной auto_mnemonics:
V560 Часть условного выражения всегда ложна: !auto_mnemonics. gtklabel.c 2923
static void gtk_label_set_pattern_internal (....) { .... gboolean enable_mnemonics = TRUE; gboolean auto_mnemonics = TRUE; .... g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)), "gtk-enable-mnemonics", &enable_mnemonics, NULL); if (enable_mnemonics && priv->mnemonics_visible && pattern && (!auto_mnemonics || (gtk_widget_is_sensitive (GTK_WIDGET (label)) && (!priv->mnemonic_widget || gtk_widget_is_sensitive (priv->mnemonic_widget))))) .... }
А вот и предупреждение об опасном сравнении беззнаковой переменной типа guint со знаковой константой:
V605 Попробуйте проверить выражение. Значение без знака сравнивается с числом -3. gtktextview.c 9162
V605 Попробуйте проверить выражение. Значение без знака сравнивается с числом -1. gtktextview.c 9163
struct GtkTargetPair { GdkAtom target; guint flags; guint info; }; typedef enum { GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS = - 1, GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT = - 2, GTK_TEXT_BUFFER_TARGET_INFO_TEXT = - 3 } GtkTextBufferTargetInfo; static void gtk_text_view_target_list_notify (....) { .... if (pair->info >= GTK_TEXT_BUFFER_TARGET_INFO_TEXT && pair->info <= GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS) .... }
Компиляторы тоже выдают предупреждения о подобных сравнениях, но они часто незаслуженно отключаются программистами. В этом примере знаковый тип будет неявно приведен к беззнаковому. Не так уж и плохо, если бы программист предусмотрел такую возможность при написании условия, но и тогда этот код далеко не хорош. Вы должны по крайней мере использовать явное преобразование, чтобы показать, что вы понимаете, что происходит. Если «pair-›info» можно присвоить значения только из перечисления «GtkTextBufferTargetInfo», то почему бы не сделать информационную переменную того же типа? А если ему могут быть присвоены и другие значения, то такой подход вообще небезопасен.
Последнее предупреждение, которое мы обсудим, касается перекрывающихся диапазонов в условиях if…elseif:
В условных выражениях возможны пересечения диапазонов V695. Пример: если (А ‹ 5) { …. } иначе если (A ‹ 2) { …. }. Контрольные строки: 580, 587. Broadway-server.c 587
static void parse_input (BroadwayInput *input) { .... while (input->buffer->len > 2) { .... if (payload_len > 125) { .... } else if (payload_len > 126) { .... } .... } }
Как видно из кода, любое значение «payload_len», превышающее 125, приведет к выполнению ветки «if (payload_len › 125)», в то время как ветка «else if (payload_len › 126)» является частным случаем исходного чек. Следовательно, код в условии elseif никогда не будет выполняться. Разработчики должны изучить и исправить это.
Вывод
Анализ кода инструментария GTK+ показывает, что в нем есть как обычные опечатки, так и более интересные ошибки, которые необходимо исправить. Статические анализаторы очень хорошо устраняют такие ошибки на ранних стадиях разработки; они помогают сэкономить время разработчиков, которое можно потратить на разработку новых функций вместо отладки и ручного поиска ошибок. Напоминаем, что вы можете бесплатно попробовать статический анализатор PVS-Studio, скачав его здесь.