Как же программа работает со всеми этими ошибками?

    Как же программа работает со всеми этими ошибками?
    Наша команда проверяет большое количество открытых проектов, чтобы продемонстрировать возможности анализатора PVS-Studio в нахождении ошибок. После наших статей нередко звучит вопрос «Как же программа работает с этими ошибками?». Попробую на него ответить.

    Введение


    Пара вводных слов для читателей, ещё не знакомых с нашим инструментом. Мы разрабатываем анализатор PVS-Studio для нахождения ошибок в исходном коде приложений, написанных на C/C++. Самый лучший способ показать, что он умеет, это проверять открытые проекты и находить в них ошибки. Найденные ошибки мы собираем в базу. Если в проекте мы находим интересные на наш взгляд ошибки, то пишем статью. Всем желающим предлагаю взглянуть на обновляемый список статей.

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

    Самое важное


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

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

    Если из-за ошибки не рисуется большая кнопка в главном окне программы, то эту ошибку сразу заметят. Причем скорее всего ошибку исправит сам программист и она даже не доберётся до отдела тестирования.

    Если ошибка зависит от входных данных, то ей уже легче спрятаться. Представим, что программист разрабатывает графический редактор. Он протестировал работу приложения на картинках с разрешением 100x100 и 300x400. И хотя он написал плохой код, у него всё работало. К счастью в компании есть отдел тестирования, в котором заметили, что программа не работает с вытянутыми картинками разрешением 100x10000. Обратите внимание, что ошибка прожила немного дольше.

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

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

    Ответ на вопрос


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

    По-другому и не может быть. Возьмём для примера библиотеку Qt. Эта библиотека отлажена и протестирована. Её использует огромное количество разработчиков по всему миру. В ней просто не может быть ошибок, лежащих на поверхности. Поэтому мы найдём в ней с помощью PVS-Studio только те, которые редко проявляют себя. Рассмотрим, например, вот эту функцию:

    QV4::ReturnedValue
    QQuickJSContext2DPrototype::method_getImageData(....)
    {
      ....
      qreal x = ctx->callData->args[0].toNumber();
      qreal y = ctx->callData->args[1].toNumber();
      qreal w = ctx->callData->args[2].toNumber();
      qreal h = ctx->callData->args[3].toNumber();
      if (!qIsFinite(x) || !qIsFinite(y) ||
          !qIsFinite(w) || !qIsFinite(w))
      ....
    }

    Функция содержит ошибку. Не проверяется значение 'h'. Вместо этого дважды проверяется переменная 'w'. Это ошибка? Да, это ошибка. Но вероятность, что она проявит себя весьма мала.

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

    И поэтому она прожила в коде библиотеки Qt долгое время, прежде чем была обнаружена с помощью PVS-Studio. Кстати, если кто-то хочет прочитать подробнее о проверке библиотеки, то предлагаю взглянуть на нашу статью "Проверка фреймворка Qt 5".

    Подведём итог.

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

    Поэтому, когда мы запускаем анализатор PVS-Studio на известный надёжный проект, такой как Chromium, мы можем найти в нём только те ошибки, которые почти никогда не проявляют себя.

    То есть ошибки есть. Их даже много (проверка N1, N2, N3, N4). Но, запуская Chromium, вы вряд ли с ними столкнётесь. Нужно будет приложить усилие и, возможно, очень большое, чтобы суметь попасть в ветвь кода, содержащую ошибку.

    PVS-Studio не нужен?


    Дочитав до этого места можно поторопиться и сделать ошибочный вывод: «Раз PVS-Studio не нужен, раз находится несущественные, никак не проявляющие себя ошибки».

    Обычно, когда я чувствую, что можно сделать такой вывод, я отправляю читателя познакомиться со статьей "Лев Толстой и статический анализ кода". Но сейчас я ещё раз постараюсь сформулировать ответ другим словами.

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

    Самое плохое, что можно делать, это прогонять анализатор незадолго до релиза. В этом нет никакого прока. Огромное количество ошибок, которые мог бы найти анализатор, к этому моменту будут исправлены ценой пота и крови. Эти неэффективно (пример, как зря потратить 50 часов времени). А потом после бессонных ночей в отладчике, переписываний с тестерами, разработчики запускают анализатор и получают всего пару полезных сообщений. Ведь всё самое страшное они исправили самостоятельно, потратив массу времени и сил. Зачем? Это какой-то Epic Fail.

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

    Я не говорю, что анализатор может найти все ошибки. Он найдет только часть. Но зато сделает это сразу. Как только программист закончит писать очередной фрагмент кода.

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

    PVS-Studio нужен!


    Чтобы окончательно вас убедить, я приведу один практический пример. Недавно мы проверяли и исправляли все ошибки в игровом движке Unreal Engine. Большинство ошибок было некритичными. Это естественно. Было бы много критичных ошибок, никто бы не стал использовать Unreal Engine в своих разработках.

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

    static void GetArrayOfSpeakers(....)
    {
      Speakers.Reset();
      uint32 ChanCount = 0;
      // Build a flag field of the speaker outputs of this device
      for (uint32 SpeakerTypeIndex = 0;
           SpeakerTypeIndex < ESpeaker::SPEAKER_TYPE_COUNT,    //<==
           ChanCount < NumChannels; ++SpeakerTypeIndex)
      {
        ....
      }
      check(ChanCount == NumChannels);
    }

    Вместо оператора && случайно написали запятую. Незначительной такую ошибку назвать нельзя. Она попала в систему контроля версий и уверен доставила бы проблемы. К счастью, анализатор PVS-Studio оказался на страже.

    Желающим поподробнее узнать историю нашей работы с компанией Epic Games предлагаю познакомиться со статьёй "Как команда PVS-Studio улучшила код Unreal Engine".

    Заключение


    Предлагаю не откладывать и попробовать наш анализатор кода PVS-Studio на своём проекте. Вы можете скачать его здесь. Интерфейс анализатора весьма прост, но предлагаю взглянуть на статью "PVS-Studio для Visual C++". Возможно, вы почерпнете для себя полезные советы по использованию анализатора. Например, многие не догадываются как легко и быстро можно исключить из результатов анализа сторонние библиотеки.

    Если вы используете для сборки makefile или свою собственную сборочную систему, то проверить проект вам поможет PVS-Studio Standalone. В этом инструменте имеется механизм отслеживания запусков компилятора.

    Если вас пугает количество предупреждений, то попробуйте новый режим разметки сообщений, использующий специальную базу. Идея: все сообщения считаются не интересными и не отображаются. Вы видите только сообщения, относящиеся к новому коду. Вы быстро поймете, как полезен и удобен регулярный анализ кода. Подробности: «Как внедрить статический анализ в проект».

    Желаю удачи!
    PVS-Studio
    Static Code Analysis for C, C++, C# and Java

    Comments 43

      +5
      Прошу прощения за вопрос не по теме: Как вообще появился такой оператор, как ","?
        –3
        Нужен для древнего for-цикла:
        char *a, *b, *end;
        for (; a < end; ++a, ++b) *a = *b;
        
          –3
          м… т.е. для того, чтобы не выносить в блок for две строчки, ввели отдельный оператор?
            +9
            Вообще-то, запятая — это полноценный оператор Си еще с древних времен, означает он примерно «вычислить два выражения, отбросить результат первого, а результатом считать результат второго». Цикл for тут ни при чем.
              +1
              И, на всякий случай, добавлю, что, как и почти все другие операторы, запятую можно перегрузить (в c++).
                0
                И чаще всего, как и с некоторыми другими перегружаемыми операторами в C++, лучше так не делать )
                  0
                  Да, мы с коллегой как-то фантазировали на тему «шаблонного оператора запятая». Потом поняли, что игра не стоит свеч, потому что мультиметодов в плюсах нет :D Но естественно, это так, без практического применения.
                  Вроде в CBLASe он используется, кстати.
            +2
            это можно записать и без запятой
            for (; a < end; ) *a++ = *b++;
              –5
              в вашем варианте вероятен UB
                +1
                нет, это полный эквивалент предыдущей конструкции
                  +3
                  чем это принципиально отличается от хрестоматийного примера из «Язык программирования Си» Кернигана и Ричи?

                  void strcpy (char *s, char *t)
                  {
                      while (*s++ = *t++)
                  	;
                  }
                    0
                    qw1 дело говорит, UB тут не будет, хотя такой код я бы не писал. Инкременты, затем присваивание, так в Си (как минимум).
                    Вообще, эквивалентом будет такой код:
                    *a++ = *b++;
                    /////////////////////
                    int i = 0;
                    a[i] = b[i];
                    i++;
                      –4
                      нельзя забывать про то, что оператор инкремента может быть перегружен
                        +4
                        В данном примере, для типа char* — не может.
              0
              Пример, которому не стоит следовать, но я видел в одной числодробилке фреймворке для математического моделирования перегруженную запятую для matlab-like инициализаци векторов.

              MyVectorClass m = 1.0, 4.0, 3.0;
                0
                Хорошо, что с появлением std::initializer_list в с++1x эти костыли с перегрузкой запятой больше не нужны.
              +41
              Идея статического анализа в том, что многие ошибки и опечатки можно найти на самом раннем этапе. Используйте его регулярно, и вы существенно сократите время, которое тратите на поиск и устранение дефектов.
              Я вам сейчас один вещь скажу, вы только не упирайтесь. Берите пример с Resharper/CLion и вообще с продуктов JetBrains.

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

              Именно из-за удобства использования ни у кого не возникает вопроса, зачем в решарпере статический анализатор. Именно работа над юзабилити (а не над детектами новых типов дефектов) может вам дать рост полезности продукта в разы.
                +5
                >Показывайте предупреждения когда человек код печатает, а не компилирует. Запускайте компиляцию в фоне

                Спасибо, не надо такого счастья. Одно дело — обычный парсер, проверяющий стандартные ошибки типа обращения к несуществующим полям структуры и прочие банальности. И совсем другое — такая махина, как статический анализатор. Лично мне хватает дисциплины (и понимания того, что это нужно) для периодического запуска cppcheck и valgrind с несколькими профилями, а ещё один фоновый парсер во время работы совсем не нужен. Даже если PVS когда-нибудь появится под Linux :) Отдельного плагина для ручного запуска под IDE будет более чем достаточно.
                  0
                  Это можно сделать опцией. Вы не будете включать её.
                    +1
                    Можно. Только трудоёмкость изготовления конечного продукта будет различаться очень серьёзно. Наверное, прикрутить проверку в качестве внешней утилиты, работающей по отдельной кнопке и на готовом коде, не так сложно. А вот сделать фоновую проверку, да ещё кода, который в принципе к этому не готов — боюсь, тут придётся кроме программистов ещё штат телепатов подключать.
                      0
                      Если эта опция будет востребована — то трудоёмкость будет оправдана.

                      CLion и использующие clang api IDE как-то же справляются.
                        +5
                        Что-то засомневался, что мы говорим об одном и том же. Скачал триал CLion и не обнаружил там вообще ничего, похожего на анализатор PVS. Если речь о том, что находится под Code/Inspect Code и настраивается в File/Settings/Editor/Inspections, то каким оно тут боком? Оно ж прошлёпывает самые простейшие примеры ляпов из списка PSV.

                        Если же о чём-то другом, то где оно там запрятяно?

                        >трудоёмкость будет оправдана

                        Я говорю о том, что это не просто сложно, это архисложно. Проверки а-ля PVS имеют смысл только для кода, который уже дописан и готов к компиляции/запуску. А в какой момент он готов может решить только написатель этого кода, явно. Ну или такой AI, уровень которого мне представить сложновато :)
                          0
                          А в какой момент он готов может решить только написатель этого кода, явно. Ну или такой AI, уровень которого мне представить сложновато :)


                          И даже в случае Ai это будет только написатель кода :) Будет вообще весело: сам себя написал, сам себя проверил.
                    0
                    Он уже есть.
                      +3
                      clion подсвечивает базовые вещи, так же как и сама Idea (по работе java занимаюсь в основном)
                      когда же в clion дело доходит до плотной работы с шаблонами он на некоторых проектах просто зависает: поток анализа уходит в разворачивания шаблонов, ui ждет его, неважно сколько ты времени будешь ждать (ждал до 2х часов). работать в clion на том проекте невозможно, так как он зависает уже на навигации (к слову сказать раньше зависал уже через 2 минуты, сейчас и 5-10 может работать, но вызов анализа кода гарантированно вешает)

                      компиляторы работать с препроцессорами умеют в разы лучще, просто за счет того, что это одно из узких мест в компиляции c++ кода.

                      поэтому и стоит разделять:
                      1) простейшие подсказки со стороны ide работающие online
                      2) глубокий анализ offline, когда мы не связаны условиями к отзывчивости системы (не думаю, что кому-то понравится когда смена одного define поставит систему на колени переиндексацией)
                        +4
                        Вы сейчас говорите не о необходимости и удобстве фич, а о тормознутости существующих на рынке решений. Следует разделять эти понятия. И да, ReSharper C++ вроде как пошустрее CLion работает за счёт использования нативного, а не managed-парсера.
                          –1
                          Какое удобство может быть у статического анализатора, который работает во время редактирования исходников? Только тормоза, а смысла — ноль, до удобства тут дело даже не дойдёт. Его место — даже не когда программист сказал «всё, закончил, готов компилировать», а, скорее, после успешной(!) компиляции проекта.
                            +5
                            в большинстве случаев (допускаю, что у вас в команде это не так, но говорю про свой опыт) статический анализатор с максимумом проверок запускается:
                            1. перед комитом самим разработчиком
                            2. билдсистемой на ветке, даже если патч и не ломает билд/тесты, а всего лишь увеличивает количество warnings, то патч ещё нужно дорабатывать


                            почему я считаю, что мощный статический анализатор online не нужен:
                            1. считать все шаблоны и подстановки define в online это тяжело (тут думаю даже примеры приводить не нужно, достаточно по хабру поискать примеры кода которые не могут даже компиляторы переварить). сюда же можно записать, что изменение одного define для сборки может затронуть половину кода, в итоге мы кушаем все ядра и стараемся дать ответ максимально быстро или мы пересчитываем все на одном ядре к моменту, когда пользователь уже давно ушел с этого куска.
                            2. считать все пути выполнения кода и возможные значения входных переменных online тоже тяжело (нас не интересует разыменование указателя как таковое, везде ставить проверки устанешь, нас это может волновать, если анализатор скажет, что сюда в некоторых условиях может придти нулевой указатель и тогда все упадет)
                            3. в большинстве случаев online нужен с ограниченным функционалом, так как всё равно все сконцентрированы на том, чтобы реализовать работающую логику, пускай и без проверки граничных условий. дополнительные варнинги будут только сбивать с мысли, а уже когда видишь, что алгоритм вроде работает и начинаешь причесывать код к комиту, то тут и пора запускать анализатор и я как пользователь готов ждать и 5 минут (схожу за кофе) и 1 час (начну смотреть другой таск), пускай он в фоне работает и генерит вывод максимально корректно, а я по приходу посмотрю список «ошибок»


                            в моем понимании стоит разделять «базовые проверки» (когда мы в пределах блока кода или метода пытаемся что-то делать, та же копипаста в методах и одинаковые условия в if) и «мощный статический анализатор» (который анализирует весь control flow graph и указывает, что нам откуда-то что-то веселое приходит, а мы это совсем не ждем). первое уже везде в разном виде присутствует, а вот второе всегда было и останется offline, просто из-за того, что по мере того как фиксятся тормоза в одном месте и появляются ресурсные мощности, мы хотим добавить «ещё и вот эту проверочку».
                              +1
                              Для онлайн-анализа самое важное — это обнаружение опечаток.
                              Компилятор их найдёт, но это десятки секунд потерянного времени.

                              Но любой анализ, будь он глубоким или «базовым», должен работать абсолютно надёжно, иначе он будет только раздражать. В случае с с/с++ без процессинга всех include и define невозможно сказать, опечатка это или макрос. Поэтому «базовая проверка» всё равно будет очень тяжёлой и разница между ней и полной может быть совсем небольшая, чтобы полную проверку выносить в отдельный режим.
                          +1
                          Спасибо за подробный комментарий, Вы абсолютно правы. По всем пунктам.

                          Посмотрите, пожалуйста, на режим инкрементального анализа в PVS-Studio (в документации). На наш взгляд это как раз шаг в заданном направлении. Как думаете?
                            0
                            Инкрементальный анализ — это хорошо, но он является необходимым, но не достаточным шагом. Просто суть в том, что главный враг даже не проблемы в коде, а человеческая лень.
                          +3
                          Анализаторы кода нужны практически везде (как и грамотное тестирование приложений).

                          Так как вы привели пример с картинками, я тоже приведу похожий.
                          В системе Android картинки (кроме формата png, bmp, jpg и т.д.) могут быть описаны файлом в формате xml.
                          Обычно иконку приложения описывают как именно картинку, и это не вызывало проблем. Но появился разработчик, который вместо png файла вставил xml файл. Очевидно, что все должно было работать, ведь система поддерживает данный формат. Оно и работало — на большинстве телефонов, кроме модели от Sony. У данной модели была своя реализация UI для телефона, которая не предусмотрела xml как входной формат. Телефон пытался отобразить картинку, у него не получалось, он перезагружался и цикл повторялся.

                          Статический анализатор вполне мог бы указать, что switch-case блоки для всех типов в enum, кроме xml.
                            0
                            Статический анализатор вполне мог бы указать, что switch-case блоки для всех типов в enum, кроме xml.


                            Кланг так умет. GCC не помню, вроде бы тоже.
                            +1
                            А мне больше всего нравятся ошибки которые проявляются в зависимости от того какое количество раз выполняется тот или иной код. Например — четное количество раз результат выдается верный. Нечетное количество раз — результат неверный.
                            Словом, чем больше разноплановых систем контроля корректности работы продукта тем лучше. Сложность современных продуктов давно уже вышла за рамки, когда контролировать их качество в состоянии даже группа людей.
                              0
                              … Эта библиотека отлажена и протестирована. Её использует огромное количество разработчиков по всему миру. В ней просто не может быть ошибок, лежащих на поверхности...

                              Это вы слишком уж категорично заявляете. Свсем уж критических ошибок, да, не будет, но мелкие — вполне себе могут быть. Если взять ОС Windows 7, которую использует несчетное количество пользователей, то там прямо на поверхности лежит баг со сплиттером и курсором в интерфейсе, который бесит неимоверно, и до сих пор не поправлен.
                                +3
                                А что за баг такой?
                                  0
                                  Неприятность тут вот в чём — при перемещении сплиттеров-разделителей в эксплорере Windows 7 они не всегда захватываются мышью.
                                  Воспроизводится это так: запустите эксплорер (Win+E), подведите курсор к вертикальному разделителю между основным окном и окном предварительного просмотра, затем аккуратно сдвиньте курсор на пару писелов влево или вправо. Обратите внимание, что вид курсора — по-прежнему двойная стрелка, показывающая, что мы можем захватить разделитель. Теперь аккуратно нажмите кнопку мыши, стараясь не сдвинуть указатель — в результате курсор сменится на стрелочку и разделитель захвачен не будет. В обычной работе это проявляется так, что при перемещении сплиттеров туда-сюда они то захватываются то нет. Пользователь про себя матерится и делает перемещение снова. У меня трекболл вместо мыши и я вижу эту проблему в полный рост, потому что на трекболле при нажатии кнопки я шарик вообще не трогаю.

                                  Вот здесь на тостере я более подробно написал:
                                  Проблема со сплиттерами-разделителями в Windows Explorer (Win 7)

                                  Вынесу также коммент RaMMicHaeL из своих личных сообщений:
                                  "Я взглянул на проблему.
                                  Во первых, код, отвечающий за баг, принадлежит не проводнику, а файлу duser.dll (DirectUser). Для того чтобы решить, какой курсор показывать, вызывается функция DirectUI::HWNDElement::ElementFromPoint, которая возвращает неправильный элемент.
                                  Эта функция вызывает функцию DuVisual::FindFromPoint, которая также возвращает не то.
                                  Пока я это все писал, я нашел баг! :)
                                  Обрабатывая WM_SETCURSOR, для получения координат мыши в Windows 7 использовалась функция GetMessagePos, которая возвращала предыдущие координаты. Windows 8 использует GetCursorPos.
                                  Найти местоположение кода легко — нужно поставить бряк на SetCursor, GetMessagePos будет немного выше.
                                  "

                                  Зная о проблеме, я перемещаю сплиттеры аккуратно (всё руки никак не дойдут это дело поправить). Но страшно представить, какое огромное количество пользователей семёрки натыкаются на это дело (лежащее, согласитесь, совсем уж «на поверхности»), но списывают это дело на тремор в суставах, а на самом деле там просто откровенный баг. Причём выпустить патч — проблема небольшая. В майкрософт писал, но это дело осталось без ответа (да, собственно в восьмёрке пофиксено).
                                    0
                                    (да, собственно в восьмёрке пофиксено)
                                    Не совсем. В проводнике, на панели предпросмотра оно осталось, если выделить текстовый файл и вести в правую сторону. Только в таком случае можно повторить, во всех остальных случаях не повторяется.
                                      0
                                      Спасибо за коммент. Я сам это почти не проверял, так как до сих пор на семёрке сижу, но сходу устойчиво воспроизвести не удалось. Надо будет на досуге в Win10 посмотреть. В принципе такая же проблема есть и с выделением файлов и перетаскиванием их из окошка в окошко — иногда вместо ожидаемого перетаскивания они начинают выделяться. Впрочем тут это багом назвать сложно, скорее проблемка с юзабилити. Я просто время от времени на тестировке сижу и когда наблюдаемый результат отличается от ожидаемого — автоматически начинаю разбираться.
                                        0
                                        Кстати, проверил в Win 10. У меня пока Build 9841. Тоже не без греха — там сплиттер иногда тягается за курсором, вид которого не соответствует действию:

                                        Ну, окей, тут можно списать на Technical Preview, либо на VMWare. Но если именно так уйдёт в релиз — то будет грустно. Оно вроде мелочь, но такие залипушки в ПО такого уровня иметь место не должны.
                                • UFO just landed and posted this here
                                    +1
                                    Ознакомительный режим PVS-Studio.

                                    Ограничения время от времени меняются в порядке эксперимента и достижения лучшей конверсии. Поэтому здесь текущий вариант не пишу.

                                  Only users with full accounts can post comments. Log in, please.