﻿# Roslyn API, или из\-за чего PVS\-Studio очень долго проект анализировал

Многие ли из вас использовали сторонние библиотеки при написании кода? Вопрос риторический, ведь без применения сторонних библиотек разработка некоторых продуктов затягивалась бы на очень\-очень большое время, потому что для решения каждой проблемы приходилось бы "изобретать велосипед"\. Однако в использовании сторонних библиотек кроме плюсов имеются и минусы\. Один из этих минусов недавно коснулся и анализатора PVS\-Studio для C\#\. Анализатор долгое время не мог закончить анализ большого проекта из\-за использования метода SymbolFinder\.FindReferencesAsync из Roslyn API в диагностике V3083\.

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

Жизнь в PVS\-Studio, как обычно, шла своим чередом\. Разрабатывались новые диагностики, улучшался статический анализатор, писались новые статьи\. Как вдруг\! У одного из пользователей нашего анализатора на его большом проекте в течение дня шёл анализ и никак не мог закончиться\. Alarm\! Alarm\! Свистать всех наверх\! И мы свистали, получили дампы от пользователя и начали разбираться в причинах долгого анализа\. При подробном изучении проблемы выяснилось, что дольше всех работали 3 C\# диагностики\. Одной из них оказалась диагностика под номером V3083\. К этой диагностике уже и раньше было повышенное внимание, но пора было предпринять конкретные действия\. V3083 предупреждает о некорректных вызовах C\# событий\. Например, в коде: 

```cpp
public class IncorrectEventUse
{
  public event EventHandler EventOne;  
  protected void InvokeEventTwice(object o, Eventers args)
  {
    if (EventOne != null)
    {
      EventOne(o, args);        
      EventOne.Invoke(o, args);
    }
  }
}
```

V3083 укажет на вызовы обработчиков события _EventOne_ в методе _InvokeEventTwice_\. О причинах опасности этого кода вы можете узнать более подробно в [документации](https://pvs-studio.ru/ru/docs/warnings/v3083/) этой диагностики\. Со стороны, логика работы V3083 очень простая:

* найти вызов события;
* проверить, корректно ли это событие вызывается;
* выдать предупреждение, если событие вызывается некорректно\.

Из\-за этой простоты становится еще интереснее понять причину долгой работы диагностики\.

## Причина замедления

На самом же деле логика чуть\-чуть сложнее\. V3083 в каждом файле для каждого типа создает только одно срабатывание анализатора на событие, куда записывает номера всех строк \(для навигации в различных плагинах: [Visual Studio](https://pvs-studio.ru/ru/blog/posts/cpp/0635/), [Rider](https://pvs-studio.ru/ru/docs/manual/0052/), [SonarQube](https://pvs-studio.ru/ru/docs/manual/0037/)\), где событие некорректно вызывается\. Получается, первым делом необходимо найти все места вызова события\. Для подобной задачи в Roslyn API уже имеется метод _SymbolFinder\.FindReferencesAsync_, который и был использован в V3083, чтобы не "изобретать велосипед"\. 

Этот метод советуют использовать во многих руководствах: [первое](https://dotnetdevlife.wordpress.com/2018/10/17/using-roslyn-to-find-all-references/), [второе](https://riptutorial.com/roslyn/example/30101/get-all-the-references-to-a-method), [третье](https://coderoad.ru/44167269/Roslyn-SymbolFinder-%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BC%D0%B5%D1%81%D1%82%D0%BE%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9-%D1%83%D0%B7%D0%B5%D0%BB) и т\. д\. Возможно, в каких\-то простых случаях скорости работы этого метода и достаточно\. Однако, чем больше кодовая база проекта, тем дольше этот метод будет работать\. На 100 % мы убедились в этом только после изменения V3083\.

## Ускорение V3083 после изменения

При изменении кода диагностики или ядра анализатора необходимо проверить, что ничего из того, что раньше работало, не сломалось\. Для этого у нас имеются позитивные и негативные тесты на каждую диагностику, юнит тесты для ядра анализатора, а также база open\-source проектов \(которых уже почти 90 штук\)\. Для чего нам база open\-source проектов? На ней мы запускаем наш анализатор для проверки его в "боевых условиях", а также этот прогон служит дополнительной проверкой, что мы ничего не сломали в анализаторе\. У нас уже имелся прогон анализатора на этой базе до изменения V3083\. Все, что нам осталось сделать, – это совершить аналогичный прогон после изменения V3083 и выяснить выигрыш во времени\. Результаты нас приятно удивили\. Без использования _SymbolFinder\.FindReferencesAsync_ в V3083 мы получили ускорение на тестах на 9 %\. Если кому\-то эти цифры показались незначительными, то вот вам характеристики компьютера, на котором производились замеры:

![0823_Roslyn_FindAllReferences_ru/image2.png](https://import.viva64.com/docx/blog/0823_Roslyn_FindAllReferences_ru/image2.png)

Думаю, после этого даже самые упертые скептики убедились в масштабах проблемы, которая тихо\-мирно жила в диагностике V3083\.

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

Пусть всем, кто использует Roslyn API, эта заметка будет предостережением\! И вы не допустите наших ошибок\. Причем, это касается не только метода _SymbolFinder\.FindReferencesAsync_, но и всех других методов класса _Microsoft\.CodeAnalysis\.FindSymbols\.SymbolFinder_, которые используют один и тот же механизм\.

Также советую всем разработчикам тщательней и внимательней изучать библиотеки, которые вы используете\. Я говорю это не просто так\! Чтобы понять, почему это так важно, советую изучить две других наших заметки: [первая](https://pvs-studio.ru/ru/blog/posts/cpp/0762/), [вторая](https://pvs-studio.ru/ru/blog/posts/csharp/0654/)\. В них этот вопрос рассмотрен более подробно\.

Кроме диагностик мы занялись другими оптимизациями анализатора PVS\-Studio, о чем расскажем в следующих статьях и заметках\. 

Изменение диагностики V3083 пока что не попало в релиз, поэтому версия анализатора 7\.12 работает с использованием _SymbolFinder\.FindReferencesAsync_\.

Как упоминалось ранее, замедление анализатора было найдено еще в двух C\# диагностиках, кроме V3083\. Ради интереса, предлагаю читателям оставлять свои предположения о том, какие это диагностики в комментариях\. Когда предположений станет больше 50, я приоткрою завесу тайны и назову номера этих диагностик\.