Roslyn-basierter Ausnahmeanalysator

    Lange wollte ich mich mit Analysatoren auf Roslin-Basis beschäftigen. Darüber hinaus hatte ich bereits Erfahrung mit der Erstellung von Plugins für Resharper ( R # Contract Editor Extension ). Daher wollte ich verschiedene Infrastrukturen und die Benutzerfreundlichkeit vergleichen. Es gibt eine Idee, dieses Plugin mit Roslyns Analysatoren umzuschreiben, aber ich habe beschlossen, mit etwas Einfacherem zu beginnen.

    Ziel des wöchentlichen Projekts war es , einen einfachen Analysator zu erstellen, der typische Fehler bei der Ausnahmebehandlung aufzeigt. Die aus meiner Sicht schmerzhaftesten sind:

    • Ausnahmen mit throw ex erneut auslösen;
    • Schlucken Sie alle Ausnahmen mit leeren catch {} - oder catch (Exception) {} -Blöcken .
    • "Schlucken" von Ausnahmen in bestimmten Zweigen eines Fangblocks .
    • Speichern Sie nur ex.Message-Nachrichten in den Protokollen , während Sie möglicherweise wichtige Informationen darüber verlieren, wo die Ausnahme aufgetreten ist.
    • Neue Ausnahmen vom catch- Block falsch auslösen .



    Erste Schritte


    Um die Entwicklung des Analysators beginnen muss installiert sein VS2015 die CTP (der einfachste Weg , um bereit virtalku ), dann müssen Sie setzen VS 2015 SDK , .NET Compiler Platform Templates das SDK und .NET - Compiler Syntax Plattform Visualizer . Der Visualizer ist unerlässlich, um zu verstehen, wie Syntaxbäume aussehen, wie sie richtig analysiert werden und wie neue Bäume in Fixes generiert werden. Vor allem müssen Sie die richtigen Versionen der Tools installieren (für CTP6 müssen alle VSIX-Pakete für CTP6 sein). Die Homepage des Roslyn-Projekts enthält immer aktuelle Installationsanweisungen.

    Das Entwicklungsteam hat großartige Arbeit geleistet, damit die Erstellung von Analysatoren so einfach und bequem wie möglich ist. Es reicht aus, ein neues Projekt zu erstellen, die Vorlage Erweiterbarkeit -> Diagnose mit Code Fix (Nuget + VSIX) auszuwählen, den Namen des Analysators einzugeben und fertig. Als Ergebnis werden drei Projekte erstellt: der Analysator selbst, das Projekt mit Unit-Tests und das Projekt mit dem Installer (VSIX). Standardmäßig wurde dem Projekt ein Beispielanalysator hinzugefügt, der bei Typnamen mit Kleinbuchstaben eine Warnung anzeigt.

    Danach können Sie die Tests ausführen oder das VSIX-Projekt als Startprojekt auswählen und F5 drücken. Anschließend wird eine weitere Instanz von Visual Studio mit installiertem Analyzer gestartet, die eine Warnung für alle Typen mit Kleinbuchstaben ausgibt:



    Analysator-Installationsmethoden


    Es gibt drei Möglichkeiten, den Analyzer zu installieren:

    · Mit dem VSIX-Paket, das direkt von Visual Studio Gallary oder über „Extensions and Updates“ heruntergeladen werden kann .
    · Manuelles Installieren eines Analysators für jedes Projekt:


    · Analysatoren können auch zusammen mit der Bibliothek über NuGet verteilt oder einfach über verwaltete NuGet-Pakete installiert werden:


    In diesem Fall wird der Link zum Paket in die Quellcodeverwaltung übernommen, sodass alle Projektteilnehmer einen Analysatorsatz verwenden können.

    Testbarkeit

    Bei der Entwicklung eines Plug-ins für ReSharper fehlten mir vor allem einfache Unit-Tests. Das JetBrains-Team hat eine seriöse Testinfrastruktur entwickelt, aber alle Tests sind Integrationstests. Es gibt keine abstrakten Tests in R #, sie fallen alle in eine der Kategorien: Testen der Verfügbarkeit von Kontextaktionen, Testen des Ergebnisses einer "Korrektur" usw. Bei diesem Test muss die cs-Datei mit dem Code versehen werden, mit dem der Analysator und eine andere Datei ausgeführt werden, um die Ergebnisse zu vergleichen. Es ist unmöglich, Ihre Geschäftslogik isoliert zu testen!
    In Roslyn gingen sie einen einfacheren Weg. Die Analysatoren arbeiten mit Syntax- und Semantikbäumen (Syntax Tree und Semantic Tree), die in Tests einfach aus einer Datei oder einem String erstellt werden können. Infolgedessen können Sie im Test den Analyzer selbst oder Ihr Geschäftsmodell überprüfen, indem Sie Fragmente des Syntaxbaums an ihn übergeben:

    [TestMethod]
    public void SimpleTestWarningOnEmptyBlockThatCatchesException()
    {
        var test = @"
    using System;
    namespace ConsoleApplication1
    {
        class TypeName
        {
            public static void Foo()
            {
                try { Console.WriteLine(); }
                {on}catch(System.Exception) {}
            }
        }
    }";
        var warningPosition = test.IndexOf("{on}");
        var diagnostic = GetSortedDiagnostics(test.Replace("{on}", "")).Single();
        Assert.AreEqual(EmptyCatchBlockAnalyzer.DiagnosticId, diagnostic.Id);
        Assert.AreEqual("'catch(System.Exception)' block is empty. 
                          Do you really know what the app state is?",
             diagnostic.GetMessage());
        Assert.AreEqual(warningPosition, diagnostic.Location.SourceSpan.Start);
    }
    


    Es ist auch sehr erfreulich, dass die Tests schnell ausgeführt werden, da sie trotz mehrerer Millionen Codezeilen im Roslyn-Projekt gut strukturiert sind und nicht Dutzende zusätzlicher Assemblys laden müssen.

    Erhaltene Gelegenheiten

    Was kann der resultierende Analysator also tun? Derzeit werden sechs Grundregeln unterstützt:



    Jede davon lässt sich am einfachsten anhand eines Beispiels veranschaulichen. Einige Beispiele werden mit Animationen gezeigt, die alles andere als ideal sind. VS2015 ist auf der virtuellen Maschine installiert und die Bildaufnahme ist gleichzeitig leicht verzerrt. Aber das Wesentliche wird klar sein.

    1. Leeren Sie den Auffangblock, der als schädlich eingestuft wird !

    Der schwerwiegendste Codegeruch bei der Arbeit mit Ausnahmen ist die vollständige Unterdrückung aller Ausnahmen mit leerem catch oder catch (Exception) -Block :

    Bild

    2. Schlucken von Ausnahmen als schädlich
    Für diejenigen, die alle Ausnahmen mit dem catch {} -Block abfangen, sollte eine spezielle Stelle auf dem Boiler vorbereitet werden. Die Lösung in diesem Fall ist sehr einfach: Hinzufügen von throw;

    Bild

    3. Catch-Block verschluckt eine Ausnahme

    Ein anderes Analysegerät warnt vor der Unterdrückung von Ausnahmen. Der catch-Block darf nicht leer sein, solange er noch Ausnahmen verschluckt. In diesem Fall ist es ziemlich schwierig, die richtige Lösung zu finden, insbesondere wenn eine Ausnahme nur in einem der Zweige des catch-Blocks unterdrückt wird:

    Bild

    Ja, ein Versuch, einen solchen Analysator ohne Roslin zu erstellen, würde zu monatelanger Arbeit führen und wäre ohnehin in der Platine schief. Roslin verfügt über eine integrierte Unterstützung für die Kontrollflussanalyse, auf deren Grundlage es nicht schwierig war, diesen Analysator herzustellen.

    4. Exception erneut richtig auslösen

    Dies ist einer der häufigsten Fehler, wenn eine Ausnahme mit throw ex ausgelöst wird. nicht mit werfen . Nur für den Fall, ich erinnere Sie daran, dass im ersten Fall der Stack-Trace der ursprünglichen Ausnahme verloren geht und der catch-Block die Quelle zu sein scheint.

    Bild

    5. Verfolgung von ex.Message als schädlich

    Eine weitere häufige Ausnahme bei der Fehlerbehandlung, wenn nur ex.Message und ex.StackTrace in der Konsole oder im Protokoll gespeichert werden . Da Ausnahmen sehr oft einen Ausnahmebaum bilden, enthält eine Nachricht der obersten Ebene möglicherweise überhaupt nichts Nützliches!

    Diese Analyse generiert eine Warnung, wenn der catch-Block die Message- Eigenschaft verwendet ("überwacht"), aber nicht interessiert an den Details der Interna der Ausnahme.

    Bild

    Aufgrund eines so schönen Modells zum Aufspüren von Ausnahmen musste ich am 1. Januar zur Arbeit gehen und einen Hotfix auf das Produkt hochladen, um zu verdeutlichen, was mit dem System los war. Niemals nur ex.Message protokollieren ! NIEMALS!

    6. Erfasste Ausnahme als innere Ausnahme hinzufügen

    Der catch-Block kann eine Ausnahme auslösen , aber selbst dann können die Informationen zur ursprünglichen Ausnahme verloren gehen. Um dies zu vermeiden, sollte die neue Ausnahme die ursprüngliche Ausnahme als verschachtelte Ausnahme enthalten.
    Dieser Analyzer überprüft den Code zum Generieren einer neuen Ausnahme. Wenn die ursprüngliche Ausnahme nicht verwendet wird, wird vorgeschlagen, sie als verschachtelte Ausnahme hinzuzufügen (natürlich muss die generierte Ausnahme dazu einen Konstruktor haben, der einige Parameter akzeptiert - string und Exception ):

    Bild

    Anstelle einer Schlussfolgerung


    Ich möchte nicht auf die Entwicklung des Analysators selbst eingehen, zumindest nicht in diesem Beitrag. Es gibt ziemlich gute Beispiele im Internet (Links am Ende des Artikels), und außerdem betrachte ich mich selbst nicht als Experten in dieser Angelegenheit. In diesem Beitrag soll gezeigt werden, wie einfach es ist, einen Analysator mit Ihren eigenen Händen zu erstellen, da Roslyn die ganze Schmutzarbeit für Sie erledigt. Wenn Sie also plötzlich ein typisches Problem mit dem Code in Ihrem Team haben und einen bestimmten Codierungsstandard formalisieren möchten, ist es eine gute Idee, einen eigenen Analysator zu schreiben.

    Sitelinks




    Ein paar einführende Artikel im MSDN Magazine zum Erstellen von Parsern:


    Z.Y. Wenn Sie Wünsche für den Exception Analyzer haben, dann pfeifen Sie, ich füge sie gerne hinzu.

    Jetzt auch beliebt: