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

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

Сравнение строк указателей, не освобождённый буфер — такие ошибки выглядят так, как буд-то над кодом местами поработал студент-практикант.
а так вполне возможно и было — в MS много интернов, которым отдают в разработку некритичные части софта.
Юниоры писали.
НЛО прилетело и опубликовало эту надпись здесь
Скорее всего, бОльшая часть кода написана ещё до появления C#, всё же
НЛО прилетело и опубликовало эту надпись здесь
Хочу видеть историю их гита с 1989 года. Что пушилось, что мерджилось, где в форки ушло, и в какие форки. Когда происходил ремастер, т.к. в 10 калькулятор довольно сильно отличается от 95.
НЛО прилетело и опубликовало эту надпись здесь
К сожалению — да, но даже с 2005 года будет интересно.
Что не понятно, почему минусов за желание увидеть, пусть и синтезированную гит историю насыпалось.

"Хочу видеть" это просто неправильный посыл с т.з. Хабра. Все мы чего-то хотим.

НЛО прилетело и опубликовало эту надпись здесь
Ну глядя на калк в последней винде, в этом можно быть уверенным на 100%. По крайней мере в части, отвечающей за интерфейс…
НЛО прилетело и опубликовало эту надпись здесь
«В таких крупных компаниях, как Microsoft, Google, Amazon и других, работает много талантливых программистов, но...» не только они :)
Спасибо, Святослав, за оперативность — спасибо Микрософту за рабочую субботу? :)
Я ещё в пятницу начал)
НЛО прилетело и опубликовало эту надпись здесь
очень маленькая программа на языке C++

тут потерялась табличка сарказм, или 35 KSLOC сегодня считается маленькой программой (в частности, калькулятором)?
Возможно, этот калькулятор крупнее обычного, согласен. Но с C++ сталкиваюсь постоянно, 35 — небольшой проект.
На мой взгляд не так уж и плохо: я опасался, что сравнение указателей это лишь вершина айсберга. Как в старом анекдоте: «Ну да, ужас. Но совсем не УЖАС-УЖАС-УЖАС!»
Данная статья наглядно демонстрирует чрезмерный уровень проверки качества кода инструментом. Очевидно, что калькулятор за 25 лет существования был избавлен от пользовательски-значимых ошибок. При этом тестируемый анализатор кода показывает наличие множества десятистепенных ошибок, которые не нужно исправлять — их исправление не принесёт пользу пользователям, а значит лишь увеличит затраты на поддержку калькулятора.

Насколько я понял, это калькулятор из Windows 10, не думаю, что ему 25 лет

Очевидно, что калькулятор за 25 лет существования был избавлен от пользовательски-значимых ошибок.

откуда уверенность, что в выложенном куркуляторе много кода из предыдущих версий?
НЛО прилетело и опубликовало эту надпись здесь
При этом тестируемый анализатор кода показывает наличие множества десятистепенных ошибок, которые не нужно исправлять — их исправление не принесёт пользу пользователям, а значит лишь увеличит затраты на поддержку калькулятора.

Всё верно. Почти наверняка эти ошибки несущественны или находятся в неиспользуемом коде. Однако, комментарий может быть неправильно трактован, поэтому я хочу добавить.

Неэффективность правки старых ошибок, не означает неэффективность методологии статического анализа в целом.

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

Смысл статичного анализа в том, чтобы использовать его регулярно. И тогда ошибки будут находиться ещё на написании кода, а не в процессе отладки, тестерами, пользователями и т.д. Т.е. цена исправления ошибки снижается с десятки и сотни раз. Да, статический анализ не способен найти все виды дефектов и заменить другие виды поиска ошибок. Нужно использовать комплекс мер. Однако, если хотя бы половина опечаток и прочих ляпов исправляется сразу, это уже круто.

P.S. В PVS-Studio даже есть режим, когда он показывает ошибки только в новом и отредактированном коде. А всё старое, считается техническим долгом, с которым можно неспешно работать (или вообще не работать :).
P.S. В PVS-Studio даже есть режим, когда он показывает ошибки только в новом и отредактированном коде. А всё старое, считается техническим долгом, с которым можно неспешно работать (или вообще не работать :).

В PVS под Linux тоже есть такая возможность?
Да, конечно. Вот описание.
Такие ошибки могут спокойно жить в ядре системы и в критических сервисах, точно также по 10-15 лет а потом бац и очередной WannaCry стучится в порт.
А зачем вы написали кусок кода, в котором наличие ошибки ни на что не влияет?
Тут явно статическим анализом не отделаешься…
«был избавлен от пользовательски-значимых ошибок»
Тавышо! А ничего что калькулятор из метро приложений не умеет правильно считать? Я не прикалываюсь. Задайте ему непосильную задачу (2х2)*2 увидите много интересного.

Это есть и в старом калькуляторе, и было "объяснение, почему он считает по другому(типа не инженерный калькулятор), на инженерном виде все правильно считает.
Вроде задачка выглядела по другому (2+2)*2

2 + 2 * 2, без скобок. Должно получиться 6, если калькулятор знает о порядке действий, может получиться 8, если не знает
Это, конечно, флейм-баян, но калькулятору в винде не положено знать о порядке действий.
Он имитирует древние «бухгалтерские» кнопочные калькуляторы, которые не имели никакого буфера и при нажатии следующей операции выполняли предыдущую и использовали ее результат как первый операнд.

В инженерном режиме с порядком действий всё нормально (ответ 6), в обычном режиме — работает по бухгалтерскому варианту (ответ 8)

Если переключить калькулятор в режим «Инженерный» или «Программист», то всё считается правильно. Это не баг, это фича.

Вот, к примеру, мы пишем Java IDE. Вчера благодаря статическому анализу я обнаружил ошибку в процедуре рефакторинга Inline method. Код, на котором она проявляется, следующий (пытаемся инлайнить метод getTwice):


class Test {
    enum Foo {
        BAR(getTwice(() -> {
            return getNum();
        }));

        Foo(int val) {}
    }

    static int getNum() {
        return 4;
    }

    static int getTwice(IntSupplier fn) {
        int x = fn.getAsInt();
        int y = fn.getAsInt();
        return x + y;
    }
}

Чтобы баг проявился, необходимо выполнение следующих условий:


  • Метод, который мы инлайним, должен возвращать значение
  • Метод, который мы инлайним, должен быть сложнее, чем просто один return, должно быть минимум два оператора в его теле
  • Метод, который мы инлайним, должен вызываться в конструкторе enum-константы
  • Среди аргументов этого метода должно быть лямбда-выражение
  • В лямбда-выражении должен быть полноценный return (простая expression-lambda не подойдёт)
  • return в этой лямбде должен возвращать результат другого метода (не константу, не арифметическую операцию, не переменную, а именно результат метода)

Только при выполнении всех условий рефакторинг не срабатывает, а IDE выдаёт исключение. Рефакторингу больше 15 лет, им пользовались миллионы раз в самых разнообразных проектах, никто никогда не сообщил об этой проблеме. Возможно, никто просто ещё не написал настолько извратный Java-код. А статический анализатор играючи указал на ошибку в нашем коде. Исправление тривиальное, а вот чтобы сконструировать тест-кейс, где ошибка проявляется, я потратил около получаса. Стоило ли исправлять или дожидаться недовольного пользователя? Как считаете?

Это сколько же у них тогда ошибок в Офисе?
НЛО прилетело и опубликовало эту надпись здесь

V547 Expression 'm_resolvedName == L«en-US»' is always false.
Шах и мат:



(ничего не менял, склонировал, запустил, поставил бряку)
is always false говорите?

Ох какая непостоянная ветка, представьте себе, в C# тоже строки по ссылкам в первую очередь сравнивают :-))

В C# представьте себе все ref объекты сравниваются в первую очередь по ссылкам, а ещё есть такое понятие как интернирование строк.
В C# для строк перегружен оператор `==`.
Скорее всего, тут приколы со string interning.
Коллеги, исходный код уже изменён в репозитории! Будьте внимательны при игре в шахматы)
НЛО прилетело и опубликовало эту надпись здесь
Ммм… Возможно, но прошу дать ссылку на стандарт или другой документ где это уточняется. Сравниваются на равенство два указателя на wchat_t.
С точки зрения C/C++ это «always false».

Однако, в «военное время» компилятор (или даже линкер) может объединять константы (в том числе константные строки), и вот тогда сравнение будет работать правильно. Причем подобное не редкость в embedded-проектах.
И такая же фигня и в Java'у перекочевала:
    public static void main(String[] args) {
        String s1 = "test";
        String s2 = "test";
        if (s1 == s2) {
            System.out.println("OMG they equals");
        }
        else {
            System.out.println("You should always use String.equals when you want to compare strings for equality");
        }
    }


Что вот выведет?
НЛО прилетело и опубликовало эту надпись здесь

Серьезно? Формально, одинаковые строковые литералы в Java всегда равны по ссылке (пункт 3.10.5 language spec).


Можете привести пример JVM или настроек, при которых код выше выведет «You should always use String.equals when you want to compare strings for equality»?

Я когда писал приложение на Андроид случайно сравнил одинаковые строки через ==
Они оказались неравны.
Хотя это были переменные разных объектов.
Так что дело либо в этом, либо в Google JVM
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
String::equals уже по умолчанию использует сравнение по указателю для производительности, как в Oracle JDK, так и в Open JDK и «даже» в Android JDK. Если сорить такими сравнениями ещё и за пределами equals, в собственном коде, то статистически максимальный выигрыш будет стремиться к стоимости вызова единственного метода. Стоит ли овчинка выделки с учётом всей машинерии внутри — вопрос более чем открытый.
НЛО прилетело и опубликовало эту надпись здесь

На всякий случай, всё-таки поясню почему вы не правы.


Если компилятору разрешить объединять константы (см ниже), то он вправе совместить все байты всех констант, с учетом требований к выравниванию их типов. Более того, аналогичный функционал может быть параллельно или независимо реализован в линкере.


В рассматриваемом случае такое объединение точно произойдет, если где-то в пределах видимости компилятора (или "умного" линкера) есть константа с совпадающим названием локали.
В свою очередь, если m_resolvedName имеет тип const wchat_t* и соответствующее значение указателя было сохранено (не было создано исходной константы), то и результат сравнения будет true.


Всё вышеописанное повсеместно в коде проектов собираемых для "мелких" процессоров (PIC, ATmega и т.п.).


-fmerge-constants
Attempt to merge identical constants (string constants and floating-point constants) across compilation units.

This option is the default for optimized compilation if the assembler and linker support it. Use -fno-merge-constants to inhibit this behavior.

Enabled at levels -O, -O2, -O3, -Os.

-fmerge-all-constants
Attempt to merge identical constants and identical variables.

This option implies -fmerge-constants. In addition to -fmerge-constants this considers e.g. even constant initialized arrays or initialized constant variables with integral or floating-point types. Languages like C or C++ require each variable, including multiple instances of the same variable in recursive calls, to have distinct locations, so using this option results in non-conforming behavior.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Я как то читал давно. Что из за того что многие в функцию принт передавали std string и их вариант из mfc то ms просто поддержали и эти случаи, хотя это и ub. Возможно тут тоже компилятор заменяет ub на то что ожидает пользователь. Но это не точно.

Это навреное была моя статья "Большой брат помогает тебе".
В данном случае никакого UB нет. Один указатель на wchar_t сравнивается с другим указателем. Вот и всё. А вся дискуссия пошла из-за того, что в заголовочном файле массив символов m_resolvedName уже превратился в полноценную строку типа std::wstring. И теперь сравнение работает правильно.
Потому что эта ситуация не «always false», а «undefined behaviour».
Никак нет, потому что в коде
wchar_t buffer[100] = L"test";
if (buffer == L"test")

компилятор не имеет права этим двум строкам дать один адрес, т.к. у них разная const-ность.

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

НЛО прилетело и опубликовало эту надпись здесь
Кто ж вам виноват, что здесь вы сами посадили UB.
Так можно: wchar_t buffer[100] = L"test";
А так нельзя: wchar_t* buffer = L"test";

ISO C++ 11 does not allow conversion from string literal to 'wchar_t*' [clang-diagnostic-writeable-strings]

А вот так можно: const wchar_t* buffer = L"test";
Из контекста в коде калькулятора непонятно, массив там передаётся или ссылка на текстовую константу. Раз в сообщении выше показано, что точка останова сработала, значит как раз ссылка на константу и была передана в функцию
А посмотреть исходник? Благо даже скачивать не надо, github позволяет браузить репозиторий online. В оригинале был массив
wchar_t m_resolvedName[100];
Из контекста в коде калькулятора непонятно, массив там передаётся или ссылка на текстовую константу.

Ох… Ну сколько можно… В статье чётко написано:
wchar_t m_resolvedName[LOCALE_NAME_MAX_LENGTH];
...
if (m_resolvedName == L"en-US")

Такое условие всегда false.

Для кода:
wchar_t * buffer = L"test";
if (buffer == L"test")

Условие может сработать. Но это совсем другой случай. Сравнение может дать true, но это зависит от везения и фазы луны.

P.S. Условие if (m_resolvedName == L«en-US») сработало из-за того, что код уже успели поправить, заменив простой массив символов на std::wstring. Уже несколько раз это обсудили в комментариях.

НЛО прилетело и опубликовало эту надпись здесь
Вы осознаёте, что
const wchar_t *A = L"test";
wchar_t B[] = L"test";
принципиально разные сущности в C++?
НЛО прилетело и опубликовало эту надпись здесь
В статьях мы тщательно подходим к выписыванию фрагментов кода, делая их по возможности самодостаточными для демонстрации ошибки. С самого начала в статье приводился следующий фрагмент кода:
wchar_t m_resolvedName[LOCALE_NAME_MAX_LENGTH];

Platform::String^ GetEnglishValueFromLocalizedDigits(....) const
{
  if (m_resolvedName == L"en-US")
  {
    return ref new Platform::String(localizedString.c_str());
  }
  ....
}

Из этого пример можно видеть, что такое m_resolvedName. Мы поспешили и не проговорили как следует это текстом и скомкано описали ошибку. Это признаём. Но не надо свою собственную невнимательность превращать в:
В первоначальной редакции статьи никакого указания, что передаётся массив, не было. Поменяли, теперь стало иначе.


P.S. Эффект, возникающий из-за пула строк, кратко затронут в документации (семилетней давности) на диагностику V547. Только не говорите, что и ссылки на документацию тоже не было :).
НЛО прилетело и опубликовало эту надпись здесь
2. С массивом тоже будет «undefined behaviour», а не «always false».
Объясните, почему?
Массив — член класса его адрес считается как смещение поля от адреса объекта. Строковый литерал — статическое значение. Не важно, размещено в пуле или ещё где-то в секции read-only data. Почему адреса могут совпасть? Разве в стандарте запрещено сравнение указателя с литералом? Вероятно, тут CLion не прав.
НЛО прилетело и опубликовало эту надпись здесь
Ленивые присваивания строк.
Это касается только классов-обёрток (типа std::string), но никак не Си-шных «строк» (char*)
НЛО прилетело и опубликовало эту надпись здесь
Скорее всего не понадобится ни везение, ни фазы луны. VS использует string pooling при настройках по-умолчанию что в debug, что в release сборке.
Тут речь о том, что сообщение PVS-Studio некорректное или неполное, что привело к неправильной интерпретации этого сообщения даже у вас в статье. Сравнение не будет всегда false для сравнения указателей(!), это показывает практика string pooling в VS. В данном конкретном случае проблема не в сравнении указателей, а в том, что m_resolvedName это массив и в С++ нужно очень сильно извратиться, чтобы условие сработало, если это вообще возможно. В данном случае PVS-Studio должна была написать именно это — переменная массив и условие потенциально никогда не выполнится.

А вот если бы m_resolvedName было простым wchar_t*, то условие очень даже сработает, если есть string pooling или аналог в данном конкретном компиляторе. В этом случае PVS-Studio стоит предупредить, что это потенциально непереносимый код, который зависит от конфигурации и типа компилятора. Работать будет, но лучше исправить.
В сообщении сложно уместить описание проблемы. Однако, этот вопрос рассматривается в документации к диагностике V547. А в статье просто не аккуратно написано, потому что очень торопились. Перед переводом подретушируем. Не хватает вокруг этого случая ещё и дискуссии на англоязычных форумах устраивать… :)
«m_resolvedName is an array» очень легко уместить и это, я думаю, сняло бы вопросы и утверждение «always false» было бы действительно верно. Опять же, будь переменная простым указателем, диагностика была бы вообще некорректная от слова совсем. В документации упоминается пулинг строк, но не упоминается ситуация с массивами, которые в С++ ведут себя совсем иначе, нежели в С.

Как я понял, товарищ Delics именно это и хотел донести, но его почему-то минусуют.
В данном случае PVS-Studio должна была написать именно это — переменная массив и условие потенциально никогда не выполнится.
Может, для кого-то и надо так разжёвывать, но как по мне «expression is always false» тут самое лучшее. Про массивы сложно было бы понять, что мне хочет сказать программа.

Опять же, будь переменная простым указателем, диагностика была бы вообще некорректная от слова совсем.
Никто же не проверил, как ведёт себя PVS-Studio в ситуации с указателем, и считают, что оно выдаст сообщение аналогичное этой ситуации (которое сейчас для массива абсолютно корректно).

А я вот проверил. И действительно, PVS-Studio пишет то же самое сообщение «V547 Expression '...' is always false. To compare strings you should use wcscmp() function». Так что Andrey2008, справедливо отметивший, что wchar_t* и wchar_t[] в C++ разные вещи, теперь должен объяснить это своей программе )))

Кстати, конкурент в этом кейсе более корректен — пишет UB:
Result of comparison against a string literal is unspecified (use strncmp instead) [clang-diagnostic-string-compare]
Единственное замечание, что он предлагает сравнивать wide-строки через strncmp, а не wcscmp (и почему strncmp с длиной, это не замена оператору == ).
Может, для кого-то и надо так разжёвывать, но как по мне «expression is always false» тут самое лучшее

Тут оно действительно лучшее, если бы второй кейс имел другое описание. С моей стороны была догадка, но, получается, она сбылась и какое-то из диагностических сообщений надо исправлять. Проще всего конечно «always false» заменить на UB и ничего не надо объяснять никому.
Не комплексуйте. Устраивайте дискуссии.
Их на самом деле не… бурили достаточно. Очень похожую ошибку как — то нашел у них в документации. Они её поправили, а мне в ответ пишут «Ваше замечание бессмысленно». Моя попытка напомнить публике, что надо сказать «спасибо» натолкнулась на корпоративное «фи», мою учетную запись забанили.
Так что понаглее, локти по-шире и коленкой вперед! :)))))))))))
Удачи.
Так undefined behavior же
Обратите внимание на сигнатуры в вашем коде — wstring против wchar_t из статьи. wstring поддерживает структурное равенство через ==, массив wchar_t — нет.
Именно такие малозаметные изменения и опечатки говорят в пользу статического анализа кода.

Ставлю сотку приложение спрототипировали на C# и потом переписали на С++ CLI/CX, отсюда таки перлы, благо незаход в ветку никак не ломал приложение — это была лишь оптимизация насколько я понял.

Ставлю сотку приложение спрототипировали на C# и потом переписали на С++ CLI/CX
Но зачем?
Хороший ответ на «эти ошибки никому не мешают». Человек не поленился в выходной поправить ошибку в рабочем проекте.
Ну стыдно же:) Если в вашем проекте такое найдут и в новостях напишут, я думаю вы тоже подорветесь править.
Мне только интересно, эти исправления сделаны на публику в гитхабе, или они попали во внутренние репозитории MS, из которых собирается продукт. Узнаем со следующим апдейтом, будет ли туда включен calc.exe.
в выходной

В США 8 марта выходным не является. Поэтому все же в обычный рабочий день

А перевод этой статьи на английский есть? Порылся в вашем блоге, но не нашел.

Ещё нет, но будет. Решили опубликовать пока идёт бурное обсуждение новости, не дожидаясь перевода.
Я опубликовал свеженаписаннаю статью. Перевод подъедет чуть позже.
Планируете еще и на хабр перевод выложить? С открытием англоязычного хабра это имеет смысл.
Конечно. В будни будет готов перевод.

Интересно было бы теперь для сравнения проверить код юниксового калькулятора bc.

Вы можете это сделать абсолютно самостоятельно, чтобы была статья от независимого автора. Так будет даже интереснее! И PVS-Studio доступна, и исходники калькулятора есть. Поможете нам сделать серию статей о проверке калькуляторов? ;-)

Спасибо за идею, обязательно попробую. Заодно освою новый для себя инструмент. Честно говоря, не знал о существовании триальной версии PVS :)

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Прикольно. В MS это писали «студенты». Как повысить качество кода? Выложить в открытый доступ. Тут же набросились профессионалы, и вуаля — код улучшился. Красиво и дёшево.
Ну нашли 100500 банальных опечаток. Бизнес-пользы в этом никакой, разве что платить дополнительно сотрудникам, которые промониторят найденные ошибки и внесут исправления в репозиторий.

Другое дело, если бы кто-то нашёл архитектурные ошибки и глобально отрефакторил — тогда бы код улучшился и его дешевле было бы развивать в будущем. Но что-то таких героев не видно.
Главная мысль это то что для коммерческого успеха идеальный код не нужен. Это на заметку собеседющим, главное это человеческие качества, имхо основное это умение довести до конца.
Ну и приложение из стандартной поставки Windows это тоже не «коммерческий успех», прошу заметить — кто его самостоятельно ставить-то или покупать будет? Так что мысль хорошая, только далековата от предмета статьи =)
Стандартная поставка влияет на коммерческий успех Windows. Другое дело что калькулятор вряд ли вносит хоть сколько-то заметный вклад в этот успех, поэтому практически вы правы.
А что это за интересный синтаксис:
String^
Это C++/CLI.
Спасибо. Оказывается добавляет ref-поведение, необходимое для работы в .NET:
This feature is necessary to give a ref class the semantics for operator overloading expected from .NET ref classes. (In reverse, this also means that for .NET framework ref classes, reference operator overloading often is implicitly implemented in C++/CLI.)
Докатились. Калькулятор не можем написать без кучи багов и неоднозначного кода.
А сразу правильно что не пишется? Благо логики в калькулаторе минимум.
Похоже, калькулятор это первые шаги перед переводом чего-то большого в опенсорс. Так же было вначале с .net, сначала библиотеки, unit тесты и тд.

Выложат ядро винды в опенсорс. На kernel.com

… и к нему тоже заведут issue #1 «Failed to compile on Linux»?
К тому времени они MSVC отопенсорсят и под линукс портируют. С# компилятор уже там.

у меня комп флешку не отдаёт пока калькулятор не закроешь в некоторых случаях (похоже когда в тотал коммандере открыта флешка и я запускаю с панели тотала иконку калькулятора)

Видимо, тотал ставит калькулятору флешку в качестве текущей директории, отчего она становится занятой.
Поэтому я когда-то забросил С++ и перешел на Java. Ибо слишком глуп и невнимателен для плюсов. Легкость с которой можно сделать ошибку сравнима с игрой в super meat boy на хардкорных настройках. Особенно когда ты пытаешься вклинить свой код в огромный проект, написанный такими же раздолбаями как и ты сам.

Часто возникали ситуации «Мой код не работает — ПОЧЕМУ?!» и «Мой код работает — ПОЧЕМУ?!». И чем больше я изучал плюсы тем активнее искал из них выход.

Все-таки хороший плюсовик очень редкая птица. Их нужно ценить, беречь и давать молоко за вредность.
НЛО прилетело и опубликовало эту надпись здесь
Это как в классическом анекдоте: «А вы продаете или покупаете?» (:
Это не тот калькулятор который бы мне хотелось увидеть. Последний раз нормальный калькулятор был в XP кажется. А в этом так удобно — надо тебе взять синус из числа 5Eh, ты сначала переключить в режим для программистов, конвертируй число, ЗАПОМНИ ЕГО, перейди в режим научного калькулятора (при этом число в калькуляторе очистится), введи число заново, и посчитай синус. То есть вместо двух кликов — куча ненужных действий, включая ползание по менюшкам. Какая разница, сколько в нём ошибок, если им пользоваться неудобно? И самое обидное — раньше хоть на сайте микрософта можно было скачать так называемый Calculator Plus, который точная копия того самого старого калькулятора, только зачем-то с дополнительным скином (чудовищно выглядящим и установленным по умолчанию). А теперь и его удалили. Благо хоть инсталляха сохранилась. И пусть молодёжь смеётся что хранить хлам на винте прошлый век, всё же можно скачать.

Вот вам сорцы того самого калькулятора!

Спасибо. Было бы интересно проверить и его (есть подозрение что с ним ситуация будет ещё хуже), но для этого хорошо бы знать как его правильно компилить. Я конечно засунул его в визуал студию, и даже смог собрать что-то относительно работоспособное, но не до конца понятно где косяки исходного кода а где — мои кривые руки.
Ну это была утечка какого-то куска Windows 2000. Никто не гарантировал, что она полная и работоспособная.
Анализатор обнаружил две функции, которые реализованы одинаково. По названиям функций Convert() и ConvertBack() можно предположить, что они должны выполнять разные действия, но разработчикам виднее.

Здесь как раз все верно. Если у нас конвертер инвертирует булево значение, то очевидно, что операция обратная инвертированию является инвертированием :) Т.е. x == !!x.

Эта диагностика выяляет 2 проблемы:
  1. Функции имеют одинаковую реализацию (не всегда ошибка);
  2. Дублирование кода само по себе плохо. Проблемы начинаются, когда необходимо изменять код.
2. Плохо, но в этом конкретном случае я бы выносить это в третью функцию не стал. Впрочем, анализатор, конечно, все кейсы различать не научишь.

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

Как писавший немало конвертеров для XAML, скажу что тут нет никакого задела. BooleanNegationConverter — полностью законченная и самостоятельная вешь, нужная, если хочешь забиндить состояние чекбокса на булевскую переменную, но не на её значение, а на инверсию. Просто биндишь через этот конвертор, и всё. А что функция конверсии получается такая длинная (вместо одной строки bool result = !input), это издержки технологии, открывающие многие дополнительные возможности, когда они будут нужны.
НЛО прилетело и опубликовало эту надпись здесь
Ваши слова да Роскосмосу в уши.
Немного не по теме, но планируется ли в PVS-Studio поддержка PHP?
Пока нет. Если решим поддержать новый язык, то проведём опросник. Следите за нашим блогом)
НЛО прилетело и опубликовало эту надпись здесь
3 месяца назад добавили анализатор Java. Некоторое время мы будем заниматься развитием и поддержкой существующих языков, прежде чем выберем новый.
Жаль.
НЛО прилетело и опубликовало эту надпись здесь

У меня есть предположение по эксепшинам.
Возможно std:: exception был специально запривачен, что бы исключения нельзя было перехватит через catch( std::exception&).
М.б. они они хотели калькулятор в виде либы сделать.
А их реализация запривачена, без dll import.


Ошибки с условиями очень странные.
Где (а && б ||! а &&! б)…
Возможно ранее, эти переменные были числами. И им нужно было проверить, что они равны или не равны нулям. А между собой, равенства не имеют. От чего, сравнение a == b не возможно.


По поводу указателей.
Тут наверное надо документацию по clr прочесть надо.
М.б. для wchar_t и литерала L там прописаны свои правила компиляции операторов сравнения

Да не было тут никакой предыстории. Просто люди ошибаются. Как выявили эти проблемы — сразу исправили. Применение статического анализа должно улучшить ситуацию.
НЛО прилетело и опубликовало эту надпись здесь
По количеству просмотров и комментариев чувствуется, что калькуляторы волнуют людей :). И автор этой статьи уже пишет заметку про калькулятор Qalculate! и делает facepalm-ы. Причина скоро станет понятна. Если совсем кратко: там всё хуже, чем в калькуляторе от Microsoft.

Но я вот к чему. Не обязательно ждать, когда мы проверим тот или иной проект. Есть различные варианты бесплатного использования PVS-Studio. Плюс можно триальную версию использовать. Можно и что-то проверить и статью написать. Или ошибки в любом проекте поправить. Подробнее: Бесплатные варианты лицензирования PVS-Studio.
А насколько скоро статья будет?

Кстати, по теме бесплатной версии: раньше были ограниченные переходы в бесплатной версии. Потом, в какой-то момент после переустановки системы, я поставил PVS и ограничение переходов вообще отсутствовало. Это был баг или вы ограничение убрали?
Ограничение переходов (кликов) относилось к демо-версии. Видимо она и была у Вас установлена. А в бесплатных версиях никакого ограничения на клики не было и нет.
Так я не выполнял никакие действия из бесплатной лицензии, так что обычная демо и была.
Уже 2 готовы, скоро начнём постить.

Да, калькулятор сильно людям понравился. Вроде обычная статья от PVS-Studio, каких много, а столько плюсиков и комментариев!


An odd precise comparison: ratio == threshold

Мой опыт показывает, что эта инспекция слишком шумная и сделать её адекватной, то есть чтобы она указывала на реальные баги и не указывала на всякий мусор, довольно сложно. Но как минимум сравнения с нулём крайне редко бывают полезными предупреждениями. Я не помню ни единого случая из своей программистской практики, когда написанное программистом doubleValue == 0.0 приводило к багам, а abs(doubleValue) < someEpsilon исправляло эти баги. Рекомендую при нуле не предупреждать. Аналогично при единице. Среди тысяч найденных вами ошибок за годы работы есть хоть одна, где явное сравнение с единицей было проблемой?


Теперь вернёмся к threshold. Эта переменная имеет значение 0 во всех формах и только в форме UnitConverter имеет значение 1. Соответственно данное сравнение тоже скорее всего безопасно.


Даже не так. В случае если ActiveIfEqual == false, isActive = ratio > threshold. В случае если ActiveIfEqual == true, isActive = ratio >= threshold. Скажите, вы ругаетесь на операцию >=? Ведь здесь фактически она и есть.

Да, V550 (An odd precise comparison) весьма шумная диагностика. Поэтому она относится к уровню Low, который отключен по умолчанию. Однако, это одним она не интересна, а другим бывает ой как даже нужна и полезна. Цитата из статьи "О том, как мы опробовали статический анализ на своем проекте учебного симулятора рентгенэндоваскулярной хирургии":

V550 An odd precise comparison: t != 0. It's probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. objectextractpart.cpp 3401

D3DXVECTOR3 N = VectorMultiplication(
                  VectorMultiplication(V-VP, VN), VN);
float t = Qsqrt(Scalar(N, N));
if (t!=0)
{
  N/=t;
  V = V - N * DistPointToSurface(V, VP, N);
}
Подобные ошибки повторяются достаточно часто в данной библиотеке. Не скажу, что это стало для меня неожиданностью. Уже ранее наталкивался на некорректную работу с числами с плавающей точкой в этом проекте. Однако систематически проверять исходники на этот счет не было ресурсов. По результатам проверки стало ясно, что нужно дать разработчику почитать что-то для расширения кругозора в части работы с числами с плавающей точкой. Скинул ему ссылки на пару хороших статей. Посмотрим на результат. Сложно однозначно сказать, вызывает ли эта ошибка реальные сбои в работе программы. Текущее решение выставляет ряд требований к исходной полигональной сетке артерий, по которым моделируется растекание рентгеноконтрастного вещества. Если требования не выполнены, то возможны падения программы или явно некорректная работа. Часть из этих требований получена аналитически, а часть эмпирически. Не исключено, что эта вторая часть требований растет как раз из некорректной работы с числами с плавающей точкой. Нужно отметить, что не все найденные случаи употребления точного сравнения чисел с плавающей точкой являлись ошибкой.

P.S. При желании уровень отдельных предупреждений в PVS-Studio можно изменить. Т.е. можно сделать V550 уровнем High.
> Я не помню ни единого случая из своей программистской практики, когда написанное программистом doubleValue == 0.0 приводило к багам, а abs(doubleValue) < someEpsilon исправляло эти баги.

В вычислительной математике — таки очень часто.
Хотя там нужно откровенно, конечно, ляпнуть, чтобы сравнивать с нулём — это уже ошибка уровня студента младших курсов. Видимо, поэтому и редко на практике — если уже пишется код, то такие вещи вбиты «в ДНК».
V728 An excessive check can be simplified. The '(A && B) || (!A && !B)' expression is equivalent to the 'bool(A) == bool(B)' expression

Вы же всё время хвастаетесь, что концентрируетесь на реальных багах, а не на стиле кодирования? А это чисто стилистическое. Некоторым людям трудно представить булево значение в контексте любой операции кроме &&, ||,!.. Ну просто мозг не поворачивается в эту сторону. Поэтому они пишут длинно.

Да, это может быть не ошибкой. Но ошибки тяготеют к такому коду. :)
Было бы интересно проанализировать когда-то давным-давно уплывшие исходники ядра Win2000…
Подсудное дело…
Почему? Вы же не украли их сами и не используете в коммерческом проекте, вы их только изучаете. Вроде бы изучение материалов не считается чем-то плохим независимо от их происхождения. Можно конечно сказать что публикуя результат изучения вы таким образом делаете рекламу статическому анализатору, было бы интересно увидеть какой-то внятный комментарий о том какие здесь есть проблемы.
Это к юристам.
Проанализировать-то может и легально, но вот выкладывать результат такового анализа мне кажется, ой как сложно, чтобы ничего не нарушить. Могут нагреть за публикацию даже пары строк кода.

Да и вопрос — зачем? Показать что в таком огромном проекте туева хуча опечаток допущенных по глупости? Реклама статическому анализатору для разработчиков из Microsoft? Ну, может быть, только как-то сомнительно выглядит. Лучше все же как сейчас, на максимально широкую аудиторию работать.
Для изучения есть абсолютно легальный WRK.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий