Статьи

Написание и распространение анализаторов Roslyn с помощью MyGet

Довольно мило: MyGet только что объявил, что поддержка Vsix включена для всех клиентов MyGet! Я хотел поработать над забавным примером для этой новой функции и придумал: как мы можем использовать MyGet для создания и распространения анализатора Roslyn и исправления кода? Давайте посмотрим.

Разработка анализатора Roslyn и исправление кода

Анализаторы Roslyn и исправления кода позволяют командам разработчиков и частным лицам применять определенные правила в базе кода. Используя исправления кода, также можно предоставить автоматические «исправления» для проблем, обнаруженных в коде. При написании кода, использующего DateTime , часто лучше использовать DateTime.UtcNow вместо DateTime.Now . Первый использует часовой пояс UTC, а второй использует местный часовой пояс компьютера, на котором выполняется код, часто вводя неприятные ошибки, связанные с временем. Давайте напишем анализатор, который обнаруживает использование DateTime.Now !

Вам понадобится Visual Studio 2015 RC и установленный Visual Studio 2015 RC SDK . Вам также понадобится пакет VSIX SDK Templates для получения шаблонов проекта Visual Studio. Как только вы их получите, мы можем создать новый Анализатор с Code Fix .

image_thumb [2]

Будет создано решение с 3 проектами: анализатор и исправление кода, модульные тесты и проект Vsix. Давайте начнем с первого: обнаружение DateTime.Now в коде и его диагностика. Это на самом деле довольно просто: мы говорим Roslyn, что хотим проанализировать узлы IdentifierName, и он передаст их нашему коду. Затем мы можем увидеть, является ли идентификатор «Сейчас», а родительский узел — «Дата / время». Если это так, верните диагностику:

public override void Initialize(AnalysisContext context)
    {
        context.RegisterSyntaxNodeAction(AnalyzeIdentifierName, SyntaxKind.IdentifierName);
    }

    private void AnalyzeIdentifierName(SyntaxNodeAnalysisContext context)
    {
        var identifierName = context.Node as IdentifierNameSyntax;
        if (identifierName != null)
        {
            // Find usages of "DateTime.Now"
            if (identifierName.Identifier.ValueText == "Now"
                && ((IdentifierNameSyntax)((MemberAccessExpressionSyntax)identifierName.Parent).Expression).Identifier.ValueText == "DateTime")
            {
                // Produce a diagnostic.
                var diagnostic = Diagnostic.Create(Rule, identifierName.Identifier.GetLocation(), identifierName);

                context.ReportDiagnostic(diagnostic);
            }
        }
    }

Если мы скомпилируем наше решение и добавим созданный пакет NuGet в другой проект, код DateTime.Now будет помечен. Но давайте также сначала осуществим исправление кода. Мы хотим предоставить исправление кода для синтаксического узла, который мы только что обнаружили. И когда мы вызываем его, мы хотим заменить узел «Сейчас» на «UtcNow». Немного возиться с синтаксическим деревом Рослина:

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
    {
        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

        var diagnostic = context.Diagnostics.First();
        var diagnosticSpan = diagnostic.Location.SourceSpan;

        // Find "Now"
        var identifierNode = root.FindNode(diagnosticSpan);

        // Register a code action that will invoke the fix.
        context.RegisterCodeFix(
            CodeAction.Create("Replace with DateTime.UtcNow", c => ReplaceWithDateTimeUtcNow(context.Document, identifierNode, c)),
            diagnostic);
    }

    private async Task<Document> ReplaceWithDateTimeUtcNow(Document document, SyntaxNode identifierNode, CancellationToken cancellationToken)
    {
        var root = await document.GetSyntaxRootAsync(cancellationToken);
        var newRoot = root.ReplaceNode(identifierNode, SyntaxFactory.IdentifierName("UtcNow"));
        return document.WithSyntaxRoot(newRoot);
    }

Вот и все. Теперь у нас есть анализатор и исправление кода. Если мы попробуем это (снова, добавив сгенерированный пакет NuGet в другой проект), мы увидим оба в действии:

image_thumb [6]

Теперь давайте распространим это на нашу команду!

Распространение анализатора Roslyn и исправление кода с помощью MyGet

Анализаторы Roslyn можно распространять в двух форматах: в виде пакетов NuGet, чтобы их можно было включить для отдельного проекта, и в качестве расширения Visual Studio, чтобы во всех проектах, с которыми мы работаем, были включены анализатор и исправление кода. Вы можете построить на машине разработчика, CI-сервере или использовать MyGet Build Services . Давайте выберем последнее, так как это самый простой способ достижения нашей цели: компилировать и распространять.

Создать новый канал на www.myget.org . Далее, на вкладке Build Services мы можем добавить репозиторий GitHub в качестве источника. Мы открыли наш пример по адресу https://github.com/myget/sample-roslyn-with-vsix, поэтому не стесняйтесь добавлять его в свой канал в качестве теста. После добавления вы можете начать сборку. Просто так. MyGet выяснит, что это анализатор Roslyn, и создаст как пакет NuGet, так и расширение Visual Studio.

image_thumb [9]

Милая! Теперь вы можете добавить анализатор Roslyn и исправление кода для каждого проекта, установив пакет NuGet из канала ( https://www.myget.org/F/datetime-analyzer/api/v2 ). И при регистрации в Visual Studio ( https://www.myget.org/F/datetime-analyzer/vsix/ ), открыв Инструменты | Параметры … Меню и Окружающая среда | Панель Расширения и обновления , вы также можете установить полное расширение.

image_thumb [12]