﻿# PVS\-Studio научился анализировать Blazor компоненты

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

![1023_RazorArticle_ru/image1.png](https://import.viva64.com/docx/blog/1023_RazorArticle_ru/image1.png)

**О чем статья?**

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

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

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

Разметку Razor используют [Blazor/Razor компоненты](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-7.0) \(\.razor файлы\), а также [Razor Pages](https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-7.0&tabs=visual-studio) \(\.cshtml файлы\)\. Я указываю двойное название Blazor/Razor т\.к\. согласно [MSDN](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-7.0) оба названия приемлемы: "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:

```cpp
....
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](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression '\!string\.IsNullOrEmpty\("mud\-table\-row\-group\-indented\-1"\)' is always true\.

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

Issue 2:

```cpp
@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](https://pvs-studio.ru/ru/docs/warnings/v3076/) Comparison of 'result' with 'double\.NaN' is meaningless\. Use 'double\.IsNaN\(\)' method instead\.

Рассмотрим другой кейс на том же проекте\. Анализатор говорит, что сравнение с _double\.NaN_ – бессмысленно\. Но почему? Согласно [MSDN](https://learn.microsoft.com/en-us/dotnet/api/system.double.op_equality?view=netframework-4.8) сравнение двух _NaN_ значений через оператор '\=\=' всегда возвращает _false_\. Для корректного сравнения необходимо использовать метод _double\.IsNaN_\.

Issue 3:

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

```cpp
@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](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression '\!string\.IsNullOrEmpty\(exceptionTeam\)' is always false\. 
1. [V3127](https://pvs-studio.ru/ru/docs/warnings/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 файлах своих проектов, можете взять пробную версию анализатора [здесь](https://pvs-studio.ru/ru/pvs-studio/try-free/)\. Интересными находками делитесь в комментариях\.

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

На данный момент анализ \.cshtml файлов не реализован, но это не значит, что мы не возьмемся за это\. Если увидим заинтересованность пользователей, то обязательно попробуем поддержать и этот формат\. Если вам интересен анализ \.cshtml файлов, [напишите нам](https://pvs-studio.ru/ru/about-feedback/) об этом\.

## Заключение

В заключении прошу вас поделиться своим мнением\. Используете ли вы Blazor в своих проектах? Стоит ли нам добавлять поддержку анализа \.cshtml файлов или же это пустая трата времени и сил?

Также буду рад, если вы поделитесь своим мнением о Razor и Blazor в комментариях к статье\.