Как стать автором
Обновить

Комментарии 108

Спасибо! Сейчас пофиксю.
вот это скорость!

PS: Нескромный вопрос: а у вас в отделе используются какие-нибудь статические анализаторы, или как обычно: все проверки — глазами код-ревьювера?
есть статические анализаторы. я пытался прикрутить PVS, сломался на интеграции в некоторых местах.

времени нет, хочу сделать еще одну попытку — после чего выбить соответствующее финансирование, чтоб хотя бы на год в нашей подветке его потестить…
С проектами типа Chromium никакие инструменты из коробки не работают. Это не моя мысль — это говорили ребята из московской разработки Google, пока ее не разогнали.

Это я к чему. Нет смысла самому прикручивать пытаться, надо нам написать и все подскажем. Судя по количеству наших статей про Chromium, все-таки PVS-Studio прикручивается :-).

Ну проверку-то разово я тогда прогнал и даже два бага пофиксил. Вот только шума слишком много, и не автоматизировано, надо-то чтоб оно само. В общем, пару выходных я все же убью ещё раз, благо прошлые черновики сохранились.


И обязательно ещё за ключиком схожу ;) дабы демонстрация работала…

Вот только шума слишком много

Да, такое бывает. При этом, не надо героически преодолевать шум. Надо настраивать анализатор. Например, на Chromuim половина! всех ложных срабатываний связана с макросом DCHECK. Если только один этот макрос доработать напильником, жизнь уже сильно улучшится. Я про это скоро напишу.

И в мыслях не было :)
Шум надо устранять возле его появления, иначе инструмент только мешает, фильтры для известных вещей, и т.п. — ежу понятно.
Макросы одна беда, вторая — местоположение дефолтных библиотек, которые точно надо игнорировать.

Неужели парсер / валидатор не покрыт тестами? Тут они прям так и просятся.
покрыты. и тесты на валидацию есть. только почему-то исключительно валидные случаи проверялись — со всеми вытекающими :)
А что если дата локальная и 60 +1 или +2 секунды в какой-то момент это норма?:)

Основа формата — число секунд с точки Х. Не зря там в тестах после заполнения структуры конвертирует туда и обратно.
Поэтому изначально произвольный таймстамп невозможен

А это такой стиль принят в protobuf, что код и доп тесты в отдельных PR идут? Может не обращал внимание, но вроде бы не попадалось. И если это так сделано намеренно, то в чём основной аргумент за?
В первую очередь — увидеть что тесты падают до фикса и не падают после.
У меня на рабочем лаптопе нет ключа от персонального гитхаба, поэтому редактировал онлайн :D

зы: я не настоящий сварщик, я работаю над BigQuery.
А в чём смысл двойной проверки Non-leap year и Leap year?

У високосного года есть не только "делится на 4" но ещё условия

Но ведь эти тесты не для проверки работы IsLeapYear, по идее? Вот там эти проверки были бы уместны (и они там есть), а здесь они только вопросы вызывают — разве поведение этой функции меняется в зависимости от того, какой именно високосный год ей дадут?

Нет, по крайней мере сейчас. Но это один из антипаттернов — тестировать код, зная как он устроен. Такие тесты ломаются или не ловят поломки при рефакторинге или смене алгоритма.
Мои тесты тоже не верх качества, так как в идеале надо проверять на все месяцы независимо.

Спасибо, сообразил.
Good. Я скоро ещё принесу. Около 250 штук. :)

Врядли я этим займусь, я лучше время на интеграцию ещё раз потрачу, если смогу выкроить :)
Пользы будет больше…

Andrey2008, попадали ли ошибки, найденные PVS Studio под программы Bug Bounty, или в базы наподобие CVE?
Ждёте ли Вы перед публикацией, пока ошибки проанализируют на возможность эксплутирования хотя бы в таких активных и критически важных проектах, как Chromium?
Насколько вообще реально с помощью статического анализатора типа PVS-Studio наткнуться на серьёзную проблему безопасности в открытых проектах?
таких активных и критически важных проектах

наткнуться на серьёзную проблему безопасности в открытых проектах

Навряд ли. Серьёзные проблемы безопасности (уязвимости то бишь), это не опечатка в коде. Это целая цепочка сложных, на первый взгляд (часто) не связанных между собой шагов плюс уязвимости в third-party продуктах.
Если бы всё было так просто — любые уязвимости покрывались бы тестами :)

они точно попадают под программу «Buy License» в качестве Bounty
Да, анализатор PVS-Studio может выявить уязвимости. Раз он выявляет большое количество CWE-дефектов, то часть из них вполне могут превращаться в CVE. Подробнее.

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

Я конечно дико извиняюсь, но юнит тестов нету?

В них наверное тот же самый код :)
А то, что в минуте бывает 61 секунда — учитывать не надо?
А то, что «1 февраля 1918» на территории ExUSSR — это неверная дата?
Во всех книгах по криптографии написано — «не надо придумывать свою систему шифрования, потому что вы по определению напишете хуже и наделаете кучу ошибок». Вот с разбором дат надо такое-же везде писать.

И вообще со всем чем угодно. Не нужно писать то, что уже кем то написано

Бывает только 60я секунда. Даже если в году две високосных секунды — между ними пол года будет
В 1 минуте обычно 60 секунд.
Leap second — 61-ая секунда, не поверите.
Для информации — например в 2016 году 31 декабря на часах было 23:59:60
Ага. Мы о разном говорим. Я не про количество оных, а про индекс.
То есть секунда==60 валидна (в очень продвинутых либах), секунда равная 61 нет.
Просто это известная ошибка в библиотеке явы, которая позволяет секунду равную 61.

Но нет, насколько я понимаю, протобаф не жуёт високосные секунды.
секунда равная 61

Вы не поверите, но это 62-ая. Они на часах с нуля идут.

См.мой комментарий. Индекс vs количество. "первая секунда" не детерминированная фраза, может означать и нулевую и первую.

Не поверим. Логично, что 1я — это между 00:00 и 00:01, 60я — между 00:59 и 01:00.
Бывают случаи, когда в 1 минуте возможно не 60, а 64 секунды, да вообще, когда на входе задана невалидная дата, и с этой датой нужно полноценно работать, а не поднимать лапки.

Например, в FAT32 или ZIP-архивах можно назначить время создания файла 27:60:62. Будет весело, если программа, агрессивно валидирующая дату, будет падать на таких файлах.

Ну или когда контроллер телекоммуникационной стойки без тени сомнения говорит, что на его часах 12:13:60 — с этим тоже нужно уметь работать.

Нет, это не в одной минуте, а где-то нужно хранение невалидных дат. Там надо подходить соответствующим образом для эмуляции багов

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Вот с разбором дат надо такое-же везде писать.
Было бы хорошо, но есть такая интерасная штука, как время в формате PM/AM:
11:00 AM
12:00 AM
01:00 AM

02:00 AM
03:00 AM

10:00 AM
11:00 AM
12:00 PM
01:00 PM

02:00 PM…

Т.е. 12 PM почти всегда меньше 01 PM.
А 12 AM меньше 01 AM.
И всё бы хорошо, можно захардкодить, НО: в некоторых странах(Австралия), где то же принято использовать AM/PM, нумерация может отличаться, и идти логически правильно:
11 PM
12 PM
01 AM

Поэтому не получится «один раз написать» и забыть, надо еще страну как минимум учитывать)
Ну, это уже, ИМХО, вопрос парсинга/генерации локализованных строковых форматов (немного другая тема), а не проверки валидности структуры для хранения.

То есть time.day по-любому будет от 0 до 23, а вот то, что в локализованной строке time.day=12 может записываться и как «12 AM», и как «12 PM» (в зависимости от страны), та и разделители/порядок могут отличаться — это отдельная тема.

Time.hour а не .day ;))

Ой, ну да.
Я просто копировал из текста статьи дважды: первый раз Ctrl+C не отработало, а второй раз, видимо, моё внимание.
Хочу заметить, что это хоть и контринтуитивно, но 12 AM после 11 PM это именно что «логически правильно» так-как любой момент сразу после полуночи или полудня будет уже после, а сами по-себе полдень и полночь временной продолжительности не имеют. Если на то пошло, то идеально точное 12:00 будет просто meridiem без ante или post.

Я терпеть не могу, когда в 11:17 говорят «уже 12й час идёт», но ведь так оно и есть на самом деле и здесь то же самое. В 12:00 AM идёт первая минута до полудня.
но 12 AM после 11 PM это именно что «логически правильно»
Ну ок, допустим. Но тогда совсем логически неправильно это:
12 AM
01 AM

Как будто часы вдруг отмотали на 11 часов назад. Почему 01 больше чем 12, но меньше чем 11? Никакой логики.
Я так понимаю это возникло из-за повсеместного использования стрелочных часов у которых вместо ноля было 12. Мне тоже это не нравится, в том числе на стрелочных часах.
НЛО прилетело и опубликовало эту надпись здесь
В физике есть «теория размерностей», которая говорит, что килограммы нельзя сравнивать с метрами (хотя в общей теории поля размерности пропадают — например, метры переходят в секунды со скоростью света).

Так вот, в программировании давно нужны языки, в которых переменные имеют что-то типа размерности. Чтобы номер месяца и номер дня в месяце нельзя было ни складывать, ни сравнивать.
В языке Паскаль было что-то такое: например, можно было создать перечисляемые типы «месяц» и «день_недели»; их возможные значения выглядели как предопределённые константы, а в применении — контролировался тип.

Например в Rust есть

Можно через кастомные типы много где сделать. Вопрос в том, что мало кто заморачивается.

Просто вместо «int day» нужно использовать «class Day day». Или class enum Day (не помню синтаксис для строгих перечислений), но полноценный класс мне больше в данном случае нравится. Другое дело, что слишком много неудобств появляется, помимо кучи удобств.
Когда я пытался понять как можно было бы избежать такой проблемы в общем, моя первая мысль тоже была, что нужно чтобы все данные имели разный тип,
чтобы например было time.year.isLeap(), time.month.isFebruary().

Вот в чем недостаток примитивных типов в яве. Не в том, что это плохо соотносится с идеей ооп, где все должно быть обьектом. Просто человеку проще сделать сравнение на примитивных числовых типах, чем писать сравнительные методы. Если по пожарной лестнице добраться до квартиры быстрее чем на лифте — это рано или поздно плохо кончится. Схожая история с динамически типизированными языками. Они дают возможность запилить пару багов которые всплывут только во время выполнения.
Ну и конечно много значит сам подход. Попытка быстро избавится от симптома, без особого стремления к качественному улучшению (да, качественное улучшение не тоже самое, что улучшение качества). О, дырка — заклеим. О, пятно — закрасим. О, грязь — подметем. Это же наша работа. А в дипломе написано инженер. Но разве инженер это в первую очередь не подход к решению проблем? Я пишу это как напоминание самому себе.

По работе мне приходится в основном писать тестировочный код и я пришел к тому, что в коде теста («клиенте») должен присутствовать минимум логики. В идеале вообще никакой. Вся логика в методах классов. Сам тест состоит из последовательных вызовов методов. Это повышает поддерживаемость («maintainability») и изменяемость(«changeability) кода. Можно из консоли создать обьекты и перед составлением теста пройти сценарий в шаговом режиме. Улучшается ортогональность: можно увеличивать количество классов и количество тестов независимо друг от друга. Распределять работу: один имплементирует, другой использует в месте назначения (клиенте).

Ну и про сложносоставные логические выражения. Это конечно круто выглядит. Больше сказать о них нечего.
Проблема даже в том, что если не сделать преобразование в/из примитивных типов, то пользоваться будет невозможно, а если сделать, пусть и явным, преобразование, то проблема чуть уменьшится, но никуда не денется.
НЛО прилетело и опубликовало эту надпись здесь
Фразу «в программировании давно нужны языки» надо понимать как «эти языки надо использовать», а не как «надо, чтобы эти языки просто были где-то в музее никогда не использовавшихся языков».
НЛО прилетело и опубликовало эту надпись здесь
Вы опять меня не поняли. Вопрос не в том, чтобы эти языки использовал я. Вопрос в том, чтобы языки без строжайшей типизации вообще не использовали в сложных и важных проектах. В идеале — хорошо бы запретить это гос.законом; но это нереально, т.к. гос.структуры работают сами знаете как: например, вместо того, чтобы уничтожить спамеров, принимают «закон Яровой».

Наверно, проблему надо решать на уровне корпораций, примерно так:
1) Разработчик письменно обязуется использовать языки со строжайшей типизацией.
2) Разработчик имеет право (и это право оговорено в обязательстве, про которое говорит первый пункт) использовать языки без строжайшей типизации. Однако, в этом случае он будет очень строго наказан (допустим, большими штрафами) в случае ошибок, которые не были бы возможны в языках со строжайшей типизацией.

Про автомобили сравнительно недавно сообщали, что там можно перехватить управление через FM-радиоприёмник — вот такая там верификация. Интересно, где там была допущена ошибка, и могла ли строжайшая типизация предотвратить эту уязвимость.
Так в любом языке, где есть User Defined Types — заводишь, и вперед. В той же Джаве для этого отлично подходят перечисления
Интересная находка, мощный у вас инструмент!

P.S. Мне кажется решение через if было бы не самым плохим:
	if ( Month == 2 )
		return Day <= 28 + isLeap ();
	else
		return Day <= ( Month < 8 ? 30 + Month % 2 : 31 - Month % 2 );

Опечатку в таком коде с еще большей вероятностью не заметил бы ни программист, ни PVS

Может и так, но это можно сказать про любой код. Определение дней в месяце легко вычисляется и логику можно сосредоточить в одном месте, а не глобальный массив с 13 элементами для 12 месяцев только невисокосного года, который еще 'доучитывается' в теле функции.
Слишком примитивно. Даёшь магию во все поля!
	if ( Month == 2 )
		return Day <= 28 + isLeap ();
	else
		return Day <= (int)(sin(Month * M_PI * 0.87) / 2 + 1) + 30;
Даёшь магию во все поля!
Да пожалуйста:
return Day <= (((Month & 1) ^ (Month >> 3)) | 30) + 
(~(((Month - 2) | (2 - Month)) >> 4) & (isLeap(Year) - 2));
Парни, шутки-шутками, но определить, что число четное от 1 до 12 с поправкой на месяц…ну ведь это с++, а не c++script…
Не хочу критиковать, но и массив, с закладкой что месяц не индекс, плюс отдельная подработка внутри функции…неужели такое красивое решение?
А июль-август?
да
Прекрасно! Вот только функция isLeap меня смущает. Слишком очевидно и легко сопровождаемо. Нужно избавиться и от неё. Естественно, всякие
(Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0)
это слишком примитивно. Можно остановиться на
!(Year & 3) - !(Year % 100) + !(Year % 400)
Но можно пойти ещё чуть дальше:
(Year & 1 | (Year >> 1) & 1) ^ 1 - !(Year % 100) + !(Year % 400)
Тут у меня фантазия пока остановилась. Может, кто-нибудь придумает, как избавиться от этих слишком очевидных 100 и 400?

P.S. Можно, конечно, использовать и старые добрые синусы:
(int)(sin(-(Year - 1) * M_PI / 2) / 2 + .5) - (int)(sin(-(Year - 25) * M_PI / 50) / 2 + .5) + (sin(-(Year - 100) * M_PI / 200) / 2 + .5)

Но рядом с битовыми операциями выглядит несколько эклектично.
Так как я хочу, чтобы мой код ещё и поняли, я не буду писать всё в одну строку.
static inline int f(int x) {
//	return x ? -1 : 0;
	static const int INT_BIT = CHAR_BIT*sizeof(int) - 1;
	return (x|-x) >> INT_BIT;
}
static inline int isLeap(int Year) {
	int YearDiv100 = Year * 1374389535LL >> 37;
	return 1 + (f(Year & 3) ^ f(Year - YearDiv100 * 100) \
	       ^ f(Year - (YearDiv100 >> 2) * 400));
}

P.S.
(Year & 1 | (Year >> 1) & 1) ^ 1 - !(Year % 100) + !(Year % 400)
^ имеет приоритет ниже чем -, пропущены скобки
P.P.S.
!(Year & 3) - !(Year % 100) + !(Year % 400)

(Year & 1 | (Year >> 1) & 1) ^ (1 - !(Year % 100) + !(Year % 400))

hdfan2, Хоть это и эквивалентные строчки, но логический переход весьма нетривиален!
return Day <= 28 + isLeap ();


не знаю насчет языка, для которого это написано, но вообще говоря isLeap() должен (вроде как из названия следует) возвращать boolean и применять к нему арифметическую операцию нехорошо
В C++ bool преобразуется в 0, если false и 1, если true

Если повезёт и булево значение не является кастомным дефайном. Ну и гарантировать что преобразованное значение компилятор точно превратит что-то такое в правильное значение


bool isLeap()
{
   return bool(0xDEADBEEF);
}
Если повезёт и булево значение не является кастомным дефайном

Это тогда уже не булево значение.
Ну и гарантировать что преобразованное значение компилятор точно превратит что-то такое в правильное значение
bool isLeap()
{
   return bool(0xDEADBEEF);
}


Точно превратит, стандарт говорит, что 0xDEADBEEF преобразуется в true, а true преобразуется в 1.
Точно превратит, стандарт говорит, что 0xDEADBEEF преобразуется в true, а true преобразуется в 1.
Если у нас действительно C++, а не C, где не было «настоящего логического типа», так что false == 0, а про true было известно только, что оно не равно нулю.
(Если, например, isLeap() берётся из какой-то старой библиотеки, то там всякого можно ожидать.)

Если интерпретировать выражение «не самым плохим» буквально, то тут я согласен, можно написать ещё хуже (чуть выше есть примеры) :)
Как насчёт проверить новый Firefox (тот что на Quantum)?
У них все новые компоненты стараются писать на Rust. Статического анализатора для него ещё нету(имею ввиду PVS).
Если глобально смотреть, то календарь у нас так себе. Постоянно сдвиги, на середину недели могут выпадать стыки кварталов/месяцев… А поменять никаких шансов.
Да, в 1950-х пытались, но по религиозным причинам не вышло.
Кстати Protocol Buffers (protobuf) используеться в TensorFlow, возможно из-за бага с валидацией даты, моя нейросеть не может найти мне девушку…
Я уверен что если проанализировать код самого TensorFlow то найдуться ошибки покритичней, ибо чуствуеться еще сыроватость проэкта.
Благодарю, интересная статья, если выходит что само C++ ядро у TensorFlow стабильное значит Python обертка не отшлифованная. Я недавно выхвачивал segmentation fault только из-за того что переименовал имя checkpoint файла из “model.data-00000-of-00001” в “model”. Я уже собирался анализировать core дампы с помощью GDB или побайтно смотреть diff-ы checkpoint файлов. Но потом понял что просто Machine Learning Python программисты не любят мирские входные проверки.
возможно из-за бага с валидацией даты, моя нейросеть не может найти мне девушку…

Подозреваю, что проблема не в нейросети :)
Уверен, что если бросить писать нейронку, выйти на улицу или, на крайний случай, поставить Tinder, то больше шансов найти девушку! ;)

UPD: коментарий для JaroslavB

Молодцы ребята, серьёзно :-) Я прокачиваю dataflow для Java в IntelliJ IDEA. Когда IDEA подобное находит, я пищу от радости. У нас, к сожалению, нет ни чтения элемента массива после инициализации, ни выполнения сложения на интервалах. Чтение конкретного элемента в принципе несложно поддержать (частично уже есть, скажем, if(idx == 1) a[idx] = 5; <...> if(a[1] > 0) подсветит). Создание интервала значений по интервалу индексов интересная мысль, вроде тоже несложно, просто в голову не приходило, что это может такую пользу принести. А вот делать сложение/вычитание на интервалах я пока опасаюсь, потому что состояния опять будут расходиться, если операция в цикле, и надо будет с этим бороться. Плюс у нас анализ в онлайн-режиме, при редактировании кода, поэтому мы несколько ограничены в ресурсах (все и так жалуются, что Идея тормозит =)). Я пока поддержал только битовое И и остаток от деления, потому что эти операции могут существенно сжать интервал значений и расхождений в циклах с ними не наблюдается. Остаток от деления выловил, например, смешной баг вроде if(count % 10 == 11).

Ну вот, теперь и мы чего-то можем :-)


image

А нам даже ничего специально делать не пришлось. Возможность сама «подхватилась» из C++ — ядра.

image

Так что думаю наш анализатор сможет порадовать пользователей. IDEA уже 10 лет, но понадобилась наша статья, чтобы добавить полезную диагностику. В общем, мы привнесём свежую кровь в сообщество Java анализаторов :).

Ну, положим, вы кривите душой. Вам специально пришлось подрубать Spoon, генерировать JNI-обёртки с помощью Swig'а и чёрт знает что ещё. А вот давайте блэкбокс-тестирование проведём, мне интересно. Вот вам исходник A и исходник B. Сохраняются ли варнинги в этих случаях?

На самом деле, генерировать JNI обертки с помощью SWIG не так уж сложно.
Ну и очевидно, что Spoon мы юзаем, из-за того, что наш C++ парсер не очень подходит для разбора Java =).
А вот в механизме Data-Flow ничего специально мы не делали, чтобы поддержать такое в Java.

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

Заметку я читал, конечно, она тут совсем ни при чем. Отсутствие варнинга как раз ожидаемо. Откуда же dataflow знает про джавовые уровни доступа?

Эта информация собирается визитором на стороне Java. А механизм Data-Flow просто не будет задействован в таком случае. Думаю, по возможности, мы как-нибудь расскажем обо всем поподробнее.

Ну так существенная часть моего патча состояла в том, чтобы определить, когда можно доверять значениям из массива, а когда нельзя. Собственно в dataflow может один вызов добавился. Визитор и вам написать пришлось, так что вы всё-таки что-то сделали специально ;-)

Товарищи, исправили уже нарушения GNU GPL? Те, связанные со статической линковкой glibc?

Сами не планируете выпуститься под GNU GPL?
Мы не планируем выпускаться под открытыми лицензиями. Сам продукт соблюдает все лицензии с момента первого релиза.

Всё-таки ошибка возникла не при написании этого кода, а несколько раньше — при планировании проекта. Такие вещи как представление время, часовые пояса, календарь (которые несут в себе не столько сложную функциональность, сколько стандарты, договоренности и культурные особенности) должны находиться только в базовой библиотеке (такой как libc в Линуксе), которую использует миллиард пользователей и просматривает миллион пар глаз. Когда каждый проект начинает городить свои поделки, ошибки статистически неизбежны.


Здесь еще повезло, что PVS заметила странное сравнение, а если бы где-то вместо 30 было написано 31, то вот ну никто бы не помог, потому что код формально верен и имеет смысл, но несет в себе альтернативный стандарт.

Неочевидно, хорошо ли это. Если это реализация строгой спецификации, то лучше навелосипедить, чем полагаться на стороннюю библиотеку и потом напороться на различия в деталях. Например, java.time при всей своей детальной проработанности официально по спецификации не учитывает високосные секунды, допускает годы от -999,999,999 до +999,999,999 и считает, что календарь всегда был Грегорианским (например, отбросит 29 февраля 1400 года как некорректную дату). Соответствует ли это вашей спецификации? Деталей может быть много и всегда есть шанс, что сторонняя реализация их трактует не так, как требуется вам.

Если java.time действительно так себя ведет, то ее реализация как раз нарушает основной принцип единственности источника спецификации.


Деталей может быть много и всегда есть шанс, что сторонняя реализация их трактует не так, как требуется вам.
Календарь и время — это как раз тот случай, когда не нужно хотеть своей спецификации. То есть своя реализация стандартной спецификации — это еще полбеды (там могут вкрасться трудноуловимые ошибки, которые когда-нибудь можно исправить), но вот своя спецификация — это совсем за гранью.
Кстати, вдруг вспомнил, что в своё время встретили VirtualBox другую разновидность бага работы с днями.

static const uint8_t g_acDaysInMonths[12] =
{
  /*Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dec */
    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

static const uint8_t g_acDaysInMonthsLeap[12] =
{
  /*Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dec */
    31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

static PRTTIME rtTimeNormalizeInternal(PRTTIME pTime)
{
  ....
  unsigned cDaysInMonth = fLeapYear
    ? g_acDaysInMonthsLeap[pTime->u8Month - 1]          // <=
    : g_acDaysInMonthsLeap[pTime->u8Month - 1];         // <=
  ....
}


V583 The '?:' operator, regardless of its conditional expression, always returns one and the same value: g_acDaysInMonthsLeap[pTime->u8Month — 1]. time.cpp 453
Следует задаться вопросом: как можно улучшить стиль, чтобы защититься от подобных ошибок?

Например заводить для дней тип day_t, а для месяцев month_t, чтобы они напрямую не сравнивались. Но это, конечно, накладные расходы на написание таких типов.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий