Проверяем osu! и рассказываем про фишки статических анализаторов

Про существование инструментов статического анализа известно многим, но почему их часто используют и в чём конкретно заключается практическая польза? В этот раз мы предлагаем рассмотреть несколько основных особенностей этого инструмента на примере анализа исходного кода игры osu!
Первая особенность: экономит время
Одной из особенностей статических анализаторов является возможность сэкономить время на код-ревью за счёт схожего подхода (просмотра исходников), только за вас всё делает инструмент :)
Предлагаю начать с небольшой разминки: сможете ли вы самостоятельно найти ошибку?
public partial class TopScoreStatisticsSection : CompositeDrawable { public ScoreInfo Score { .... if (score == null && value == null) return; if (score?.Equals(value) == true) return; score = value; accuracyColumn.Text = value.DisplayAccuracy; maxComboColumn.Text = value.MaxCombo .ToLocalisableString(@"0\x"); ppColumn.Alpha = value.BeatmapInfo! .Status .GrantsPerformancePoints() ? 1 : 0; } }
Если нужна подсказка или хотите убедиться в своём варианте, можно посмотреть на предупреждение PVS-Studio:
V3125 [SEC-NULL] The 'value' object was used after it was verified against null. Check lines: 128, 120. TopScoreStatisticsSection.cs 128
Нашли? Ну я в вас и не сомневался :)
Для протокола давайте всё же разберём, что произошло. Выглядит как логическая ошибка, а их не так просто заметить, потому что не все захотят высматривать все возможные сценарии. В нашем же случае один из них сразу может вызвать проблемы.
В начале есть две проверки.
Первая проверка:
if (score == null && value == null) return;
Вторая проверка:
if (score?.Equals(value) == true) return;
Скорее всего, они предназначались для обработки двух переменных по разным сценариям (если score = null, если value = null, если они равны и т. д.). Но вот если комбинация будет score = "NotNull" и value = null, то первая и вторая проверки отработают без выхода из метода, и мы пойдём дальше по коду, где непременно наткнёмся на разыменовывание свежеполученного null
accuracyColumn.Text = value.DisplayAccuracy; maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x");
А это, в свою очередь, может привести к исключению NullReferenceException.
Хотите узнать еще?
Если вас заинтересовало какие еще есть особенности статических анализаторов и что еще мы смогли найти в osu! То предлагаю прочитать полную версию статьи.
