Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top
>
>
>
PVS-Studio научился анализировать Blazo…

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

10 Янв 2023

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

1023_RazorArticle_ru/image1.png

О чем статья?

Мы научили 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);
    ....
  }
}

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

  • V3022 Expression '!string.IsNullOrEmpty(exceptionTeam)' is always false.
  • 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 файлов, напишите нам об этом.

Заключение

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

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

Популярные статьи по теме


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

Следующие комментарии next comments
close comment form
close form

Заполните форму в два простых шага ниже:

Ваши контактные данные:

Шаг 1
Поздравляем! У вас есть промокод!

Тип желаемой лицензии:

Шаг 2
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности
close form
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Бесплатная лицензия PVS‑Studio для специалистов Microsoft MVP
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Мне интересно попробовать плагин на:
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
check circle
Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте, отфильтровано ли письмо в одну из следующих стандартных папок:

  • Промоакции
  • Оповещения
  • Спам