Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Кажется, анонс Visual Studio 2022 был только недавно, и вот она уже вышла. Это означало ровно одно – поддержать данную IDE нужно в ближайшем релизе PVS-Studio. О том, с какими сложностями пришлось столкнуться, а что прошло без проблем, мы сегодня и поговорим.
Впечатления от новой IDE
Скажу честно, мне очень понравилась Visual Studio 2022. Я просто наслаждаюсь новыми шрифтами, которые стали менее острыми что ли. Цвета тоже стали менее кислотными, и теперь уже не так больно ставить точку остановки при отладке.
Для сравнения ниже привожу несколько примеров. Вот так выглядит фрагмент кода в Visual Studio 2019:
Вот так выглядит в Visual Studio 2022:
Важно. Оба изображения сделаны на масштабе, равном 100 %.
Также в IDE подкорректировали тёмную тему – её сочетание с фиолетовым цветом выглядит крайне гармонично (слева VS 2019, справа VS 2022):
В общем, с внешней точки зрения новая VS выглядит целостно и свежо. Хотя я знаю, что староверам она не понравилась. :)
Ещё хочу упомянуть автодополнение кода. Оно вообще не мешается, а иногда реально дописывает то, что я хотел написать:
Если говорить про неудобства, я столкнулся с тем, что принятие автодополнения иногда приходится делать с помощью "стрелка вправо", а не tab. Почему-то в определенных случаях вместо принятия автодополнения происходит обычное нажатие на tab. Возможно, это не очень распространённая проблема – обсуждали с коллегой, у него не повторялась. Если вы знаете, как это исправить, пожалуйста, напишите в комментариях. Хочу заметить, что с момента первого preview таких моментов стало меньше и, возможно, это просто вопрос времени, когда всё поправят.
Поработав в этой IDE и получив удовольствие, я решил, что хочу взять запланированную задачу про её поддержку, чтобы побыстрее перейти на новую версию VS. Дело в том, что после VS2022 VS2019 открывать вообще не хотелось. Она казалась какой-то устаревшей (хотя сама по себе IDE очень годная). Как говорится, всё познаётся в сравнении.
Поддержка плагина
Здесь всё прошло максимально плавно. Изменения нас особо не задели. Поэтому я просто создал новое решение для плагина, обновил NuGet-пакеты до необходимых версий и занялся прочим XML-программированием (например, редактированием .csproj-файлов).
Пришлось, однако, и немного поправить код – решение плагина для VS2022 не собиралось примерно из-за 5 ошибок. Все они возникли из-за того, что Microsoft слегка поменяли API, но всё оказалось совсем не страшно. Например, было вот так:
int Hwnd = DTE.MainWindow.HWnd;
return new NativeWindowWrapper(new IntPtr(Hwnd));
Стало вот так:
IntPtr Hwnd = DTE.MainWindow.HWnd;
return new NativeWindowWrapper(Hwnd);
Остальное завелось из коробки. Когда всё собралось, я решил запустить плагин, чтобы посмотреть, как обстоят дела. Кнопочки нажимались, анализ запускался, результаты выводились. Так что можно считать, что обновление плагина прошло легко. Следующим шагом было обновление toolset'ов. Однако внезапно всплыл один любопытный момент, о котором я не могу не рассказать.
Самый интересный момент
Неочевидный и интересный момент, с которым я столкнулся, был связан с шаблонами файлов уровня решения. Это такие штуки, которые появляются, когда мы хотим добавить файл в решение:
Открывается это окошко, и всё, что мы в нём видим, — это шаблоны:
Как вы видите, у нас есть два своих шаблона. Не вдаваясь в детали, один нужен для более тонкой настройки анализатора, другой — для подавления предупреждений. Однако с ними и случилась проблема. Сами шаблоны легко добавились по аналогии со старыми плагинами, а вот картинка – нет:
Что, как, почему??? На эти вопросы у меня не было ответов. Поэтому пришлось глубже погрузиться в то, как они создаются.
Данные шаблоны имеют свой формат, и для тех, что изображены на картинке выше, код выглядит вот так:
Filters.pvsconfig_template| |PVS-Studio Filters File|1|
PVS-Studio C#/C++ filters file can be used to filter,
disable and modify C#/C++ analyzer messages on project and
solution.|{installDir}\PVSLoader.exe|#32512| |Filters.pvsconfig
SuppressionFile.suppress_template| |PVS-Studio Suppress File|2|
An empty PVS-Studio suppression file. You can suppress analyzer messages
in your code from 'PVS-Studio >Suppress Messages...' menu item.
Only one suppression file per solution is allowed.
Other files will be ignored.|{installDir}\PVSLoader.exe|#32512
| |SuppressionFile.suppress
Каждая позиция между | отвечает за определённый контент. Если стоит пробел, то контента не будет. Содержимое самого шаблона задаётся отдельно.
Давайте посмотрим, что собой представляет формат описания:
имя папки, шаблона или файла .vsz, например HeaderFile.h или MyWizard.vsz;
GUID пакета VSPackage;
название шаблона;
относительный порядок отображения шаблона;
описание;
путь до исполняемого файла, откуда нужно взять картинку;
ID картинки;
поле, использующиеся для отключения или включения полей "Name" и "Location" в диалоговом окне "Add New Item";
имя файла, созданного из шаблона.
Подробнее про этот формат вы можете почитать вот тут.
Так как у нас нет картинки, то подозрение падает на пункты 6 и 7. Вначале я решил почитать, как высчитать ID картинки в исполняемом файле и что-то приуныл. :( В этих ваших интернетах говорили, что нужно качать специальную программу. При этом все ссылки на подобные программы выглядели, скажем так, не очень официально. Сразу прибегать к этому способу не хотелось, поэтому я решил посмотреть на IL код с помощью IL DASM. Там я тоже ничего не нашёл :). Упоминания числа 32512 не было. Решил, может быть, это число записано в другой системе счисления, пробовал преобразовать и поискать, но опять мимо. Уф...
И тут мой мозг такой: "Давай попробуем просто другой .exe подсунуть". Да почему бы и нет? Беру и передаю другой файл, и, о чудо, оно работает! Как в итоге оказалось, всё дело в разрядности передаваемого файла. Все Visual Studio до 2022 были 32-битными, и приложение для картинки им нужно соответствующее. 2022 же стала 64-битной. Вот такая забавная проблема.
Поддержка нового toolset
Чтобы лучше понимать, что происходит, я настоятельно рекомендую почитать соответствующие разделы статей про поддержку Visual Studio 2017 и 2019.
В отличие от поддержки плагина, которая прошла достаточно мягонько, поддержка toolset'а оказалась тем ещё приключением. Сейчас объясню, в чём основная проблема. Смотрите, у нас есть файл MSBuild.exe.config, в котором мы прописываем разные свойства, необходимые для эвалюации (построения модели проекта). До поддержки VS2022 выглядел он примерно вот так:
<?xml version="1.0" encoding="utf-8"?>
....
<msbuildToolsets default="Current">
<toolset toolsVersion="Current">
....
<property name="VsInstallRoot" value="$(PVSBuildTools_InstallDir_VS2019)" />
<property name="MSBuildToolsRoot" value="$(VsInstallRoot)\MSBuild" />
<property name="VCTargetsPath"
value="$([MSBuild]::ValueOrDefault('$(VCTargetsPath)',
'$(MSBuildExtensionsPath32)\Microsoft\VC\v160\'))" />
....
</toolset>
<toolset toolsVersion="15.0">
....
<property name="VsInstallRoot" value="$(PVSBuildTools_InstallDir_VS2017)" />
<property name="MSBuildToolsRoot" value="$(VsInstallRoot)\MSBuild" />
<property name="VCTargetsPath"
value="$([MSBuild]::ValueOrDefault('$(VCTargetsPath)',
'$(VsInstallRoot)\Common7\IDE\VC\VCTargets\'))" />
....
</toolset>
</msbuildToolsets>
</configuration>
Это только кусочек файла, но его достаточно, чтобы продемонстрировать суть. Как вы видите, тут имеется 2 toolset'а: 'Current' для VS2019 и 15.0 для VS2017. Теперь Microsoft выпускает VS2022, и картина наименования начинает выглядеть вот так:
Обратите внимание, что имена Tools Version для VS2019 и VS2022 одинаковые. И это проблема, так как нельзя дать двум разным toolset'ам одинаковое название. Поэтому мы решили, что вместо названия будем динамически изменять содержимое. Оставляем 'Current', для инициализации toolset'ов, как и раньше, используем переменные окружения. Значения для этих переменных вычисляются на основе установленных в системе версий Visual Studio или того, из-под какой версии VS мы работаем в данный момент.
Как следствие, у нас один toolset 'Current', который может работать как с VS2019, так и с VS2022, а в будущем – и с новыми версиями IDE. Вот схемка, которая наглядно показывает этот процесс:
Ещё раз стоит подчеркнуть, что отдельно учитывается ситуация, когда задана переменная окружения VisualStudioVersion со значением 16.0 или 17.0. В этом случае мы высчитываем и используем для 'Current' значение указанной VS.
Таким образом удалось решить текущую проблему с одинаковым названием разных toolset'ов, а также сделать механизм универсальным и легко расширяемым (хочется верить) для новых VS.
Поддержка .NET 6, обновление библиотек Roslyn и MSBuild
Так как VS2022 и .NET 6 вышли вместе, в PVS-Studio поддержку того и другого тоже необходимо было добавить в рамках одного релиза. Как следствие, мы параллельно поддерживали Visual Studio 2022 и анализ проектов под .NET 6, работу с .NET 6 SDK, обновляли библиотеки MSBuild и Roslyn.
Как обычно, при поддержке новой версии .NET вылезло много специфики, связанной с тем, что мы используем собственные BuildTools. Кстати, о том, с чем мы столкнулись при поддержке .NET 5, можно почитать здесь.
Синхронизация наших BuildTools и BuildTools .NET
Зачем это нужно и как реализовано, описывается в статьях про поддержку VS2017 и VS2019. Может показаться, что собственные BuildTools – лишняя головная боль. Однако, как показала практика, такой подход даёт большую стабильность/гибкость.
К тому же, это неплохая возможность повозиться с .targets / .props файлами и начать лучше понимать соответствующую часть PVS-Studio и экосистему .NET в целом.
Отладка Roslyn и MSBuild
При работе с Roslyn / MSBuild иногда всплывают какие-то неочевидные проблемы (например, проект теряет часть ссылок), из-за чего приходится погружаться в исходники. У нас в проекте даже специальная конфигурация есть для отладки Roslyn и MSBuild, собранных из исходников. :)
То, что эти (как и многие другие) проекты от Microsoft имеют открытый исходный код, очень играет на руку. Если нужно что-то выяснить – можно подебажить код и посмотреть, как и что работает.
Поначалу может быть сложно, но со временем начинаешь неплохо ориентироваться в тех частях этих проектов, куда приходится возвращаться время от времени. Когда оказываешься в тех местах кода, где не был несколько лет, это ощущается в чём-то сродни возвращению домой.
Повторю то, что говорил выше – работа с такими задачами даёт лучшее понимание того, как работает компилятор / сборочная система.
Переход на .NET 6 и поддержка C# 10
Анализатор PVS-Studio для C# под Linux и macOS теперь работает под .NET 6. Кроме того, при обновлении Roslyn мы из коробки получили поддержку C# 10. Да, доработки диагностик, data-flow и т.п. придётся делать самим, но правильный разбор нового синтаксиса и понимание семантики – это тоже здорово. Обновление библиотек MSBuild даёт, соответственно, поддержку из коробки новых фич этой сборочной системы.
Так что, если вы вдруг пропустили это событие, теперь у вас есть возможность анализировать свои проекты под .NET 6. ;)
Заключение
Ещё раз хочу подчеркнуть, что новая Visual Studio, как по мне, очень хороша. Microsoft явно постарались. Обновление самого плагина под неё прошло мягко, и это не может не радовать. Обновление toolset позволило заглянуть в те вещи, в которые судьба может и не занести.
По прошествии месяца можно сказать, что Visual Studio 2022 была успешно поддержана, обратную совместимость не сломали и получили положительные отзывы. Отдельно спасибо всем тем, кто интересовался плагином для этой IDE и успел протестировать бета-версию.
Так что, если вы хотели попробовать связку PVS-Studio и Visual Studio 2022, но как-то не доходили руки – сейчас самое время.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Nikolay Mironov. Visual Studio 2022 — stylish and fresh. How PVS-Studio supported VS2022.