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

О чем статья?

Мы научили PVS-Studio анализировать код внутри компонентов Blazor приложений. В статье расскажем, как появилась идея, чем такой анализ полезен пользователям, какие возможности и ограничения есть в текущей реализации. Также предлагаем обсудить эту и будущие фичи PVS-Studio (об этом в конце статьи).

Давайте для начала освежим в памяти терминологию.

Razor – это синтаксис разметки, позволяющий встроить в веб-страницу C# код.

Разметку Razor используют Blazor/Razor компоненты (.razor файлы), а также Razor Pages (.cshtml файлы). Я указываю двойное название Blazor/Razor т.к. согласно MSDN оба названия приемлемы: "Blazor apps are built using Razor components, informally known as Blazor components".

Итак, наш разговор пойдет в первую очередь о файлах с расширением .razor.

.razor файл – основной строительный элемент Blazor приложения. Razor компоненты позволяют инкапсулировать логику и отображение для формирования элемента пользовательского интерфейса. Каждый Razor компонент является самостоятельным блоком, что позволяет использовать их для создания новых компонентов и переносить между проектами.

Зачем мне, как пользователю, анализ Razor компонентов?

Web-приложения, использующие Blazor, содержат в файлах .razor C# код. Этот код, как и любой другой, может содержать ошибки, недочеты и опечатки. Они в свою очередь могут быть трудны для поиска "вручную" и дороги в исправлении при обнаружении на поздних этапах разработки. Теперь же PVS-Studio анализирует Razor компоненты, помогая сохранять код в них безопасным и корректным.

Почему раньше не умели анализировать .razor файлы?

Анализ .razor файлов был проблемой из-за наличия конструкций разметки Razor. Они не позволяли корректно получить синтаксическую и семантическую модели соответственно. Из-за этого проверка .razor файлов не была доступна "из коробки". Теперь мы доработали анализатор и научили проверять C# код в этих файлах. На данном этапе анализ производится только в блоках @code{...}. Такое решение было принято по совокупности различных факторов. Основной — желание предоставить пользователям анализ Razor компонентов как можно быстрее, пусть и в виде MVP.

Почему решились взяться за анализ .razor файлов?

Размышляя о том, куда должен двигаться наш C# анализатор, мы раз за разом приходили к тому, что вектор развития должен соответствовать потребностям пользователей. Многие из них так или иначе связаны с web-разработкой. Поэтому мы подумали: "А почему бы не расширить функционал анализатора в области анализа web-проектов?". И, вуаля – анализ .razor файлов добавлен.

И что, в .razor файлах могут быть ошибки?

Да, могут. Давайте рассмотрим несколько наиболее наглядных ошибок в .razor файлах Open Source проектов, которые попались мне при беглой проверке.

Issue 1:

....
RenderFragment<T> child() => item =>
  @<text>
  @{
    var rowClass = new CssBuilder(RowClass)
      .AddClass(RowClassFunc?.Invoke(item, rowIndex))
      .AddClass(customClass, 
                !string.IsNullOrEmpty("mud-table-row-group-indented-1"))
      .Build();
    ....
  }
....

Сообщение анализатора: V3022 Expression '!string.IsNullOrEmpty("mud-table-row-group-indented-1")' is always true.

Начнем с довольно интересного на мой взгляд срабатывания анализатора в проекте MudBlazor. Анализатор предупреждает нас о том, что выражение !string.IsNullOrEmpty("mud-table-row-group-indented-1") всегда истинно. Спорить с этим вряд ли получится, так как строковый литерал не может стать пустым или обратиться в null. Тяжело сказать, что именно здесь имелось ввиду, но выглядит подозрительно...

Issue 2:

@code
{
  ....
  public void Evaluate()
  {
    ....
    var exp = new Expression(CalcExpression);
    var result = exp.Eval();
    if (result == double.NaN)
    {
      Current = "ERROR";
      return;
    }
    Current = Math.Round( result,8).ToString(CultureInfo.InvariantCulture);
    CalcExpression = Current;
  }
  ....
}

Сообщение анализатора: V3076 Comparison of 'result' with 'double.NaN' is meaningless. Use 'double.IsNaN()' method instead.

Рассмотрим другой кейс на том же проекте. Анализатор говорит, что сравнение с double.NaN – бессмысленно. Но почему? Согласно MSDN сравнение двух NaN значений через оператор '==' всегда возвращает false. Для корректного сравнения необходимо использовать метод double.IsNaN.

Issue 3:

Ну и куда же без упоминания собственных ошибок. Да-да, мы тоже допускаем ошибки и находим их благодаря собственному анализатору. После добавления анализа .razor файлов мы обнаружили интересные срабатывания в коде наших внутренних утилит.

@code
{
  async Task Load()
  {
    var exceptionCustomers = string.Empty;
    var exceptionTeam = string.Empty;                            <=
    ....
    (CustomerNotifier, exceptionCustomers) = DbProvider.GetMailApplication( 
      CustomerCoreLibrary
      .Data.Types
      .ConfigMailApplicationType
      .CustomerNotifier);

    if (!string.IsNullOrEmpty(exceptionCustomers))
      await MatDialogService.AlertAsync(exceptionCustomers);

    (TeamNotifier, exceptionCustomers) = DbProvider.GetMailApplication( 
      CustomerCoreLibrary
      .Data.Types
      .ConfigMailApplicationType
      .TeamNotifier);

    if (!string.IsNullOrEmpty(exceptionTeam))                    <=
      await MatDialogService.AlertAsync(exceptionTeam);
    ....
  }
}

Сообщения анализатора:

  1. V3022 Expression '!string.IsNullOrEmpty(exceptionTeam)' is always false.

  2. V3127 Two similar code fragments were found. Perhaps, this is a typo and 'exceptionTeam' variable should be used instead of 'exceptionCustomers'.

Здесь анализатор выдал два предупреждения, которые вместе полноценно описывают сложившуюся проблему.

Во-первых, было обнаружено, что условие !string.IsNullOrEmpty(exceptionTeam) всегда ложно. Действительно, переменной exceptionTeam присвоена пустая строка и далее, вплоть до проверки, значение переменной никак не изменяется. Но из-за чего возник такой участок кода? На этот вопрос отвечает второе предупреждение, предполагающее наличие опечатки и использование не той переменной. И правда! Стопроцентное попадание.

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

Что с поддержкой .cshtml файлов?

На данный момент анализ .cshtml файлов не реализован, но это не значит, что мы не возьмемся за это. Если увидим заинтересованность пользователей, то обязательно попробуем поддержать и этот формат. Если вам интересен анализ .cshtml файлов, напишите нам об этом.

Заключение

В заключении прошу вас пройти небольшой опрос по теме в конце статьи и поделиться своим мнением о Razor и Blazor в комментариях.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Aleksey Avdeev. PVS-Studio now analyzes Blazor components.

Комментарии (0)