Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Строки кода должны помещаться на экране

Дата публикации: 01-12-2025 08:48:16

О вечном — о разумной длине строк кода. Мы недавно встретили ошибку, которая одновременно демонстрирует, чем плох "код-колбаса", "эффект последний строки" и последствия неудачного copy-paste. Читать далее

Основное содержимое страницы с новостью.

О вечном — о разумной длине строк кода. Мы недавно встретили ошибку, которая одновременно демонстрирует, чем плох "код-колбаса", "эффект последний строки" и последствия неудачного copy-paste.

Видишь баг?

Видишь баг?

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

Однако мы не только реализуем новые диагностические правила (детекторы), но и постепенно дорабатываем старые. Например, в декабрьский релиз войдёт улучшение в C# одой из самых старых диагностик — V3001. Мы научили её корректнее определять ситуации, когда дополнительные круглые скобки ни на что не влияют. Результат — выявление новых ошибок, с одной из которых мы сейчас познакомимся.

Баг найден в проекте Space Engineers, который входит в базу проектов для регрессионного тестирования нашего анализатора. Если интересны подробности, то недавно мы касались нашей системы регрессионного тестирования в вебинаре, посвящённом функциональному тестированию.

Мы используем старую зафиксированную версию проекта, так как нам требуется анализировать отличие работы PVS-Studio на одной и той же кодовой базе. Впрочем, если заглянуть в свежую версию кода, то можно обнаружить, что ошибка по-прежнему присутствует. См. функцию Contains в BoundingBox.cs.

Видите суслика баг? Думаю, нет. А он есть!

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

public ContainmentType Contains(BoundingSphere sphere)
{
  Vector3 result1;
  Vector3.Clamp(ref sphere.Center, ref this.Min,
                ref this.Max, out result1);
  float result2;
  Vector3.DistanceSquared(ref sphere.Center, ref result1, out result2);
  float num = sphere.Radius;
  if ((double)result2 > (double)num * (double)num)
    return ContainmentType.Disjoint;
  return (double)this.Min.X + (double)num > (double)sphere.Center.X ||
         (double)sphere.Center.X > (double)this.Max.X - (double)num ||
         ((double)this.Max.X - (double)this.Min.X <= (double)num ||
          (double)this.Min.Y + (double)num > (double)sphere.Center.Y) ||
         ((double)sphere.Center.Y > (double)this.Max.Y - (double)num ||
          (double)this.Max.Y - (double)this.Min.Y <= (double)num ||
         ((double)this.Min.Z + (double)num > (double)sphere.Center.Z ||
          (double)sphere.Center.Z > (double)this.Max.Z - (double)num)) ||
         (double)this.Max.X - (double)this.Min.X <= (double)num ?
           ContainmentType.Intersects : ContainmentType.Contains;
}

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

(double)this.Max.X - (double)this.Min.X <= (double)num

Это дубликат третьей строчки. Верхнее условие заключено в дополнительные скобки, но они никак не влияют на результат вычислений, так как все проверки объединяются с помощью ИЛИ (оператор ||).

На самом деле, задумывалось, что в последней строке должна проверяться координата Z:

(double)this.Max.Z - (double)this.Min.Z <= (double)num

PVS-Studio теперь замечает эту аномалию и выдаёт предупреждение:

V3001 There are identical sub-expressions '(double)this.Max.X - (double)this.Min.X <= (double)num' to the left and to the right of the '||' operator.

Хорошая демонстрация, как статический анализатор дополняет обзор кода, в процессе которого сложно заметить опечатку в такой длинной строке. Я называю это "кодом-колбасой" и уже писал заметку о том, как такая "колбаса" притягивает баги. Хочется привести здесь картинку из той публикации.

p48uhkmovuyd2xvqyh4h2acw6ue.png

Как видите, эта ситуация относится и к C# программистам :)

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

Ошибка возникла из-за написания кода copy-paste подходом: подвыражения, видимо, писались с помощью копирования предыдущих. В одном месте в копию не были внесены требуемые изменения касательно координаты Z.

Но это ещё не всё! Вся эта прекрасная длинная строка с ошибкой ещё раз была размножена копированием. Её можно наблюдать в соседней функции Contains несколькими строками ниже:

public void Contains(ref BoundingSphere sphere, out ContainmentType result)
{
  ....
  if ((double)result2 > (double)num * (double)num)
    result = ContainmentType.Disjoint;
  else
    result = (double)this.Min.X + (double)num > (double)sphere.Center.X ||
             (double)sphere.Center.X > (double)this.Max.X - (double)num ||
             ((double)this.Max.X - (double)this.Min.X <= (double)num ||
              (double)this.Min.Y + (double)num > (double)sphere.Center.Y) ||
             ((double)sphere.Center.Y > (double)this.Max.Y - (double)num ||
              (double)this.Max.Y - (double)this.Min.Y <= (double)num ||
             ((double)this.Min.Z + (double)num > (double)sphere.Center.Z ||
              (double)sphere.Center.Z > (double)this.Max.Z - (double)num)) ||
             (double)this.Max.X - (double)this.Min.X <= (double)num ?
               ContainmentType.Intersects : ContainmentType.Contains;
}

Всё то же самое, и анализатор указал на вторую ошибку таким же предупреждением.

Заключение

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

  1. Разработке в компании регламента (стандарта написания кода), где будет запрет на слишком длинные строки кода. Можно посмотреть, как это написано у нас.

  2. Регулярному использованию статического анализатора PVS-Studio для дополнительной проверки кода.

  3. Форматированию кода таблицей.

  4. Удалению лишних операций. Например, чтобы не выполнять везде приведение типа (double)num, можно было сразу объявить переменную num как double.

  5. Вынесению единообразного кода в функции.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Ошибка, которую никто никогда не совершал. Ведь так, да?-111.3217-07-2026
2Давайте заглянем в этот самый вайб-код08.4420-03-2026
3Подножка для AI в виде UTF-8010.0714-10-2025
4Ваши тесты упали по причине JavaScript011.7717-11-2025
5Ааа, всё пропало! AI создаёт дырявый код! Что же делать?011.4316-06-2026
6Баги на диком западе: топ-10 ошибок в C и C++ проектах за 2025 год08.0630-12-2025
7Ревью вайб-кода с гнильцой, который притворяется оптимизированным С++ кодом09.0821-04-2026
8Ошибка в коде, на которую приходится не обращать внимание-16.7208-06-2026
9Топ-10 ошибок, найденных в C# проектах за 2025 год015.7229-12-2025
10Наследие кода: разбор С и С++ модулей Erlang, которые работают десятилетиями09.5107-11-2025

Классификация: Мнения. Схожих патентов: 0. Схожих новостей: 10. Тональность: 0. Информативность: 13.58. Источник: habr.com.