Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A .NET Fordítóplatform SDK biztosítja azokat az eszközöket, amelyekre szükség van az egyéni diagnosztikák (elemzők), a kódjavítások, a kód újrabontása és a C# vagy Visual Basic kódot célzó diagnosztikai elnyomók létrehozásához. Az elemző olyan kódot tartalmaz, amely felismeri a szabály megsértését. A kódjavítás tartalmazza a szabálysértést kijavító kódot. A implementált szabályok lehetnek a kódszerkezettől a kódolási stíluson át az elnevezési konvenciókig és egyebek. A .NET fordítói platform biztosítja az elemzés futtatásának keretrendszerét, amíg a fejlesztők kódot írnak, valamint a Visual Studio összes felhasználói felületének funkcióját a kód javításához: a szerkesztőben megjelenő hullámvonalak, a Visual Studio hibalistájának feltöltése, a "villanykörte" javaslatok létrehozása és a javasolt javítások gazdag előnézetének bemutatása.
Ebben az oktatóanyagban egy elemző és egy kapcsolódó kódjavítás létrehozását fogja megvizsgálni a Roslyn API-k használatával. Az elemzők segítségével forráskódelemzést végezhet, és problémákat jelenthet a felhasználónak. Szükség esetén kódjavítás társítható az elemzőhöz, amely a felhasználó forráskódjának módosítását jelzi. Ez az oktatóanyag létrehoz egy elemzőt, amely megkeresi azokat a helyi változódeklarációkat, amelyek deklarálhatók a const módosítóval, de nem. A kísérő kódjavítás módosítja ezeket a deklarációkat a const módosító hozzáadásához.
Előfeltételek
- Visual Studio 2019 16.8-es vagy újabb verzió
Telepítenie kell a .NET Fordítóplatform SDK-t a Visual Studio Installeren keresztül:
Telepítési utasítások – Visual Studio Installer
A .NET Fordítóplatform SDK-t kétféleképpen keresheti meg a Visual Studio Installerben:
Telepítés a Visual Studio Installer – Munkaterhelések nézet használatával
A .NET Fordítóplatform SDK nincs automatikusan kiválasztva a Visual Studio bővítményfejlesztési számítási feladatainak részeként. Választható összetevőként kell kiválasztania.
- Visual Studio Installer futtatása
- Válassza a Módosítás lehetőséget
- Ellenőrizze a Visual Studio bővítményfejlesztési számítási feladatát.
- Nyissa meg a Visual Studio bővítményfejlesztési csomópontot az összefoglaló fán.
- Jelölje be a .NET Fordítóplatform SDK jelölőnégyzetét. Az opcionális összetevők alatt fogja utoljára megtalálni.
Szükség esetén azt is szeretné, hogy a DGML-szerkesztő diagramokat jelenítsen meg a vizualizációban:
- Nyissa meg az Egyes összetevők csomópontot az összefoglaló fán.
- A DGML-szerkesztő jelölőnégyzetének bejelölése
Telepítés a Visual Studio Installer – Egyéni összetevők lap használatával
- Visual Studio Installer futtatása
- Válassza a Módosítás lehetőséget
- Az Egyes összetevők lap kiválasztása
- Jelölje be a .NET Fordítóplatform SDK jelölőnégyzetét. A fordítók, a buildelési eszközök és a futtatókörnyezetek szakasz tetején találja meg.
Szükség esetén azt is szeretné, hogy a DGML-szerkesztő diagramokat jelenítsen meg a vizualizációban:
- Jelölje be a DGML szerkesztő jelölőnégyzetet. Ezt a Kódeszközök szakaszban találja.
Az elemző létrehozásának és érvényesítésének több lépése is van:
- Hozza létre a megoldást.
- Regisztrálja az elemző nevét és leírását.
- Jelentéselemző figyelmeztetései és javaslatai.
- Implementálja a kódjavítást a javaslatok elfogadásához.
- Az elemzés javítása egységtesztekkel.
A megoldás létrehozása
- A Visual Studióban válassza az Új > projekt fájlja>... lehetőséget az Új projekt párbeszédpanel megjelenítéséhez.
- A Visual C# > Bővíthetőség területén válassza az Analyzer with code fix (.NET Standard) lehetőséget.
- Nevezze el a projekt "MakeConst" nevét, és kattintson az OK gombra.
Megjegyzés:
Fordítási hibát kaphat (MSB4062: A "CompareBuildTaskVersion" feladat nem tölthető be). A probléma megoldásához frissítse a NuGet-csomagokat a megoldásban a NuGet Package Managerrel, vagy használja Update-Package a Package Manager konzolablakában.
Az elemzősablon felfedezése
A kódjavítási sablonnal rendelkező elemző öt projektet hoz létre:
- Az elemzőt tartalmazó MakeConst.
- A kódjavítást tartalmazó MakeConst.CodeFixes.
- MakeConst.Package, amely nuGet-csomag létrehozására szolgál az elemzőhöz és a kódjavításhoz.
- MakeConst.Test, amely egy egységteszt projekt.
- A MakeConst.Vsix az alapértelmezett indítási projekt, amely elindítja a Visual Studio egy második példányát, amely betöltötte az új elemzőt. Nyomja le az F5 billentyűt a VSIX-projekt elindításához.
Megjegyzés:
Az elemzőknek a .NET Standard 2.0-t kell célozniuk, mert .NET Core-környezetben (parancssori buildekben) és .NET-keretrendszer-környezetben (Visual Studio) futtathatók.
Jótanács
Az elemző futtatásakor elindítja a Visual Studio második példányát. Ez a második példány egy másik beállításjegyzék-struktúra használatával tárolja a beállításokat. Ez lehetővé teszi, hogy megkülönböztessük a vizuális beállításokat a Visual Studio két példányában. Választhat egy másik témát a Visual Studio kísérleti futtatásához. Emellett ne barangoljon a beállítások között, és ne jelentkezzen be a Visual Studio-fiókjába a Visual Studio kísérleti futtatásával. Ez különbözően tartja a beállításokat.
A kaptár nem csak a fejlesztés alatt álló elemzőt, hanem a megnyitott korábbi elemzőket is tartalmazza. A Roslyn-hive alaphelyzetbe állításához manuálisan kell törölnie a %LocalAppData%\Microsoft\VisualStudio fájlból. A Roslyn hive mappaneve Roslyn-ra fog végződni, például 16.0_9ae182f9Roslyn. Előfordulhat, hogy a hive törlése után meg kell tisztítania és újra kell építenie a megoldást.
Az imént elindított második Visual Studio-példányban hozzon létre egy új C#-konzolalkalmazás-projektet (minden célkeret működni fog – az elemzők a forrásszinten működnek.) Vigye az egérmutatót a jogkivonat fölé hullámos aláhúzással, és megjelenik az elemző által megadott figyelmeztető szöveg.
A sablon létrehoz egy elemzőt, amely figyelmeztetést jelenít meg minden olyan típusdeklaráción, ahol a típusnév kisbetűket tartalmaz, ahogy az alábbi ábrán látható:
A sablon egy kódjavítást is biztosít, amely minden kisbetűs karaktert tartalmazó típusnevet az összes nagybetűre módosít. A javasolt módosítások megtekintéséhez kattintson a figyelmeztetéssel megjelenő villanykörtére. A javasolt módosítások elfogadása frissíti a típus nevét és a megoldásban az adott típusra mutató összes hivatkozást. Most, hogy működés közben látta a kezdeti elemzőt, zárja be a második Visual Studio-példányt, és térjen vissza az elemzőprojekthez.
Nem kell elindítania a Visual Studio második példányát, és új kódot kell létrehoznia az elemző minden változásának teszteléséhez. A sablon egy egységtesztelési projektet is létrehoz Önnek. Ez a projekt két tesztet tartalmaz.
TestMethod1 Egy olyan teszt tipikus formátumát jeleníti meg, amely diagnosztika nélkül elemzi a kódot.
TestMethod2 Megjeleníti a diagnosztikát aktiváló teszt formátumát, majd alkalmazza a javasolt kódjavítást. Az elemző és a kódhibajavítás fejlesztése során különböző kódstruktúrákra vonatkozó teszteket fog írni a munka ellenőrzéséhez. Az elemzők egységtesztjei sokkal gyorsabbak, mint interaktívan tesztelni őket a Visual Studióval.
Jótanács
Az elemzőegység-tesztek nagyszerű eszköznek számítanak, ha tudja, mely kódszerkezeteknek kell és melyeknek nem szabad aktiválniuk az elemzőt. Az elemző betöltése a Visual Studio egy másik példányába nagyszerű eszköz az olyan szerkezetek felfedezéséhez és megtalálásához, amelyekre még nem gondolt.
Ebben az oktatóanyagban egy elemzőt ír, amely jelentést készít a felhasználónak a helyi állandókká konvertálható helyi változódeklarációkról. Vegyük például a következő kódot:
int x = 0;
Console.WriteLine(x);
A fenti kódban állandó érték van hozzárendelve, x és soha nem módosul. A const módosító használatával deklarálható.
const int x = 0;
Console.WriteLine(x);
Az elemzés annak megállapítására, hogy egy változó konstanssá tehető-e, szintaktikai elemzést, az inicializáló kifejezés állandó elemzését és adatfolyam-elemzést igényel annak érdekében, hogy a változó soha ne legyen megírva. A .NET Fordítóplatform olyan API-kat biztosít, amelyek megkönnyítik az elemzés elvégzését.
Elemzőregisztrációk létrehozása
A sablon létrehozza a kezdeti DiagnosticAnalyzer osztályt a MakeConstAnalyzer.cs fájlban. Ez a kezdeti elemző minden elemző két fontos tulajdonságát jeleníti meg.
- Minden diagnosztikai elemzőnek meg kell adnia egy
[DiagnosticAnalyzer]attribútumot, amely leírja, hogy milyen nyelven működik. - Minden diagnosztikai elemzőnek (közvetlenül vagy közvetve) az DiagnosticAnalyzer osztályból kell származnia.
A sablon az elemzők alapvető funkcióit is megjeleníti:
- Műveletek regisztrálása. A műveletek olyan kódmódosításokat jelölnek, amelyeknek aktiválnia kell az elemzőt a kód megsértéseinek vizsgálatához. Amikor a Visual Studio egy regisztrált műveletnek megfelelő kódrészleteket észlel, meghívja az elemző regisztrált metódusát.
- Diagnosztikát hozzon létre. Amikor az elemző szabálysértést észlel, létrehoz egy diagnosztikai objektumot, amelyet a Visual Studio használ a felhasználó értesítésére a szabálysértésről.
A műveletek a metódus felülírásában regisztrálódnak DiagnosticAnalyzer.Initialize(AnalysisContext). Ebben az oktatóanyagban felkeresi a szintaxiscsomópontokat , és megkeresi a helyi deklarációkat, és láthatja, hogy ezek közül melyek rendelkeznek állandó értékekkel. Ha egy deklaráció állandó lehet, az elemző létrehoz egy diagnosztikai jelentést.
Az első lépés a regisztrációs állandók és a Initialize metódus frissítése úgy, hogy ezek az állandók jelezzék a "Make Const" analizátort. A sztringállandók többsége a sztringerőforrás-fájlban van definiálva. A könnyebb honosítás érdekében kövesse ezt a gyakorlatot. Nyissa meg a MakeConst Analyzer-projekt Resources.resx fájljának megnyitását. Ekkor megjelenik az erőforrás-szerkesztő. Frissítse a karakterlánc erőforrásokat az alábbiak szerint:
- Váltson
AnalyzerDescription"Variables that are not modified should be made constants."-ra. - Váltson
AnalyzerMessageFormat"Variable '{0}' can be made constant"-ra. - Váltson
AnalyzerTitle"Variable can be made constant"-ra.
Ha végzett, az erőforrás-szerkesztőnek az alábbi ábrán látható módon kell megjelennie:
A fennmaradó módosítások az elemzőfájlban találhatók. Nyissa meg a MakeConstAnalyzer.cs a Visual Studióban. Módosítsa a regisztrált műveletet a szimbólumokon működőről a szintaxist használó műveletre. A MakeConstAnalyzerAnalyzer.Initialize metódusban keresse meg a szimbólumokon végrehajtott műveletet regisztráló sort:
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
Cserélje le a következő sorra:
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.LocalDeclarationStatement);
A módosítás után törölheti a metódust AnalyzeSymbol . Ez az elemző a SyntaxKind.LocalDeclarationStatement-t vizsgálja, nem a SymbolKind.NamedType állításokat. Figyelje meg, hogy AnalyzeNode alatt piros hullámos vonal van. Az imént hozzáadott kód egy AnalyzeNode még nem deklarált metódusra hivatkozik. Deklarálja ezt a metódust a következő kóddal:
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
}
Módosítsa a Category jelölőt "Usage"-ra a MakeConstAnalyzer.cs fájlban az alábbi kódban látható módon:
private const string Category = "Usage";
Keresse meg azokat a helyi deklarációkat, amelyek lehetnek const-ként deklarálhatók
Itt az ideje, hogy megírja a AnalyzeNode metódus első verzióját. Egyetlen helyi deklarációt kell keresnie, amely lehetne const, de nem az, mint a következő kód:
int x = 0;
Console.WriteLine(x);
Az első lépés a helyi deklarációk megkeresése. Adja hozzá a következő kódot a AnalyzeNodeMakeConstAnalyzer.cs:
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
Ez a kasztolás mindig sikeres, mert az elemző regisztrálva van a helyi deklarációk változásainak figyelésére, és csak azokéra. Más csomóponttípus nem indítja el a metódus hívását AnalyzeNode . Ezután ellenőrizze a deklarációban az esetleges const módosítókat. Ha megtalálta őket, azonnal térjen vissza. A következő kód a helyi deklarációban const szereplő módosítókat keresi:
// make sure the declaration isn't already const:
if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
{
return;
}
Végül ellenőriznie kell, hogy a változó lehet-e const. Az azt jelenti, hogy soha ne legyen hozzárendelve az inicializálás után.
Az elemzést a SyntaxNodeAnalysisContext-val/vvel fogod végezni. Az argumentum segítségével context meghatározhatja, hogy a helyi változó deklarációja elvégezhető-e const. Az A Microsoft.CodeAnalysis.SemanticModel egyetlen forrásfájl összes szemantikai információját jelöli. A szemantikai modelleket ismertető cikkben többet is megtudhat. Az adatfolyam-elemzést a helyi deklarációs utasításon a Microsoft.CodeAnalysis.SemanticModel segítségével fogja elvégezni. Ezután ennek az adatfolyam-elemzésnek az eredményeivel biztosíthatja, hogy a helyi változó sehol máshol ne legyen megírva új értékkel. Kérje meg a GetDeclaredSymbol bővítmény tagját, hogy kérje le a ILocalSymbol változót, és ellenőrizze, hogy nincs-e benne az DataFlowAnalysis.WrittenOutside adatfolyam-elemzés gyűjteményében. Adja hozzá a következő kódot a metódus végéhez AnalyzeNode :
// Perform data flow analysis on the local declaration.
DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis region.
VariableDeclaratorSyntax variable = localDeclaration.Declaration.Variables.Single();
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
Az imént hozzáadott kód gondoskodik arról, hogy a változó ne legyen módosítva, ezért létrehozható const. Itt az ideje, hogy elvégezzük a diagnosztikát. Adja hozzá a következő kódot az utolsó sorként a következőben AnalyzeNode:
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), localDeclaration.Declaration.Variables.First().Identifier.ValueText));
A folyamat előrehaladását az F5 billentyű lenyomásával ellenőrizheti az elemző futtatásához. Betöltheti a korábban létrehozott konzolalkalmazást, majd hozzáadhatja a következő tesztkódot:
int x = 0;
Console.WriteLine(x);
Megjelenik a villanykörte, és az elemzőnek jelentenie kell a diagnosztikát. A Visual Studio verziójától függően azonban a következőt fogja látni:
- A sablon által létrehozott kódjavítást továbbra is használó villanykörte azt fogja mondani, hogy nagybetűssé tehető.
- A szerkesztő tetején egy szalagcím, amely szerint a "MakeConstCodeFixProvider" hibát észlelt, és le lett tiltva." Ennek az az oka, hogy a kódjavítás-szolgáltatót még nem módosították, és továbbra is
TypeDeclarationSyntaxelemeket várLocalDeclarationStatementSyntaxelemek helyett.
A következő szakasz a kódjavítás írását ismerteti.
A kódjavítás írása
Az elemzők egy vagy több kódjavítást is biztosíthatnak. A kódjavítások olyan szerkesztést határoznak meg, amely a jelentett problémát kezeli. A létrehozott elemzőhöz megadhat egy kódjavítást, amely beszúrja a const kulcsszót:
- int x = 0;
+ const int x = 0;
Console.WriteLine(x);
A felhasználó kiválasztja a szerkesztő villanykörte felhasználói felületéről, és a Visual Studio módosítja a kódot.
Nyissa meg a CodeFixResources.resx fájlt, és váltson CodeFixTitle "Make constant"-ra.
Nyissa meg a sablon által hozzáadott MakeConstCodeFixProvider.cs fájlt. Ez a kódjavítás már be van állítva a diagnosztikai elemző által létrehozott diagnosztikai azonosítóhoz, de még nem implementálja a megfelelő kódátalakítást.
Ezután törölje a metódust MakeUppercaseAsync . Ez már nem érvényes.
Az összes kódjavítási szolgáltató a forrásból CodeFixProviderszármazik. Mindenki felülbírálja CodeFixProvider.RegisterCodeFixesAsync(CodeFixContext) az elérhető kódjavítások jelentésére. Ebben az esetben a RegisterCodeFixesAsync elemben módosítsa a keresett őscsomópont típusát LocalDeclarationStatementSyntax-re, hogy megfeleljen a diagnosztikának:
var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<LocalDeclarationStatementSyntax>().First();
Ezután módosítsa az utolsó sort a kódjavítás regisztrálásához. A javítás létrehoz egy új dokumentumot, amely a const módosító meglévő deklarációhoz való hozzáadását eredményezi:
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
title: CodeFixResources.CodeFixTitle,
createChangedDocument: c => MakeConstAsync(context.Document, declaration, c),
equivalenceKey: nameof(CodeFixResources.CodeFixTitle)),
diagnostic);
A most hozzáadott kódban észre fogod venni a piros hullámos vonalakat a MakeConstAsync szimbólumon. Adjon hozzá egy deklarációt MakeConstAsync az alábbi kódhoz hasonlóan:
private static async Task<Document> MakeConstAsync(Document document,
LocalDeclarationStatementSyntax localDeclaration,
CancellationToken cancellationToken)
{
}
Az új MakeConstAsync metódus átalakítja a Document felhasználó forrásfájlját egy új Document-vá, amely most egy const deklarációt tartalmaz.
Létre kell hoznia egy új const kulcsszó tokent a deklarációs utasítás elején. Ügyeljen arra, hogy először távolítsa el a bevezető jelentéktelen elemeket a deklaráció első tokenéből, és csatolja azokat a const tokenhez. Adja hozzá a következő kódot a MakeConstAsync metódushoz:
// Remove the leading trivia from the local declaration.
SyntaxToken firstToken = localDeclaration.GetFirstToken();
SyntaxTriviaList leadingTrivia = firstToken.LeadingTrivia;
LocalDeclarationStatementSyntax trimmedLocal = localDeclaration.ReplaceToken(
firstToken, firstToken.WithLeadingTrivia(SyntaxTriviaList.Empty));
// Create a const token with the leading trivia.
SyntaxToken constToken = SyntaxFactory.Token(leadingTrivia, SyntaxKind.ConstKeyword, SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker));
Ezután adja hozzá a const tokent a deklarációhoz a következő kóddal:
// Insert the const token into the modifiers list, creating a new modifiers list.
SyntaxTokenList newModifiers = trimmedLocal.Modifiers.Insert(0, constToken);
// Produce the new local declaration.
LocalDeclarationStatementSyntax newLocal = trimmedLocal
.WithModifiers(newModifiers)
.WithDeclaration(localDeclaration.Declaration);
Ezután formázza az új deklarációt a C# formázási szabályainak megfelelően. A módosítások meglévő kódnak megfelelő formázása jobb élményt nyújt. Adja hozzá a következő utasítást közvetlenül a meglévő kód után:
// Add an annotation to format the new local declaration.
LocalDeclarationStatementSyntax formattedLocal = newLocal.WithAdditionalAnnotations(Formatter.Annotation);
Ehhez a kódhoz új névtérre van szükség. Adja hozzá a következő using irányelvet a fájl elejéhez:
using Microsoft.CodeAnalysis.Formatting;
Az utolsó lépés a szerkesztés végrehajtása. A folyamat három lépésből áll:
- A meglévő dokumentumhoz való hozzáférés megszerzése.
- Hozzon létre egy új dokumentumot úgy, hogy lecseréli a meglévő deklarációt az új deklarációra.
- Adja vissza az új dokumentumot.
Adja hozzá a következő kódot a metódus végéhez MakeConstAsync :
// Replace the old local declaration with the new local declaration.
SyntaxNode oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode newRoot = oldRoot.ReplaceNode(localDeclaration, formattedLocal);
// Return document with transformed tree.
return document.WithSyntaxRoot(newRoot);
A kódjavítás készen áll a kipróbálásra. Az F5 billentyű lenyomásával futtassa az elemzőprojektet a Visual Studio egy második példányában. A második Visual Studio-példányban hozzon létre egy új C#-konzolalkalmazás-projektet, és adjon hozzá néhány állandó értékekkel inicializált helyi változódeklarációt a Main metódushoz. Láthatja, hogy a jelentések figyelmeztetésként jelennek meg az alábbiak szerint.
Sok előrelépést ért el. A deklarációk alatt vannak hullámos aláhúzások, amelyeket meg lehet tenni.const De még mindig van mit tenni. Ez akkor működik jól, ha a const elemet hozzáadja a i kezdetű deklarációkhoz, majd a j és végül a k elemet. Ha azonban más sorrendben adja hozzá a const módosítót, kezdve a k-el, az elemző hibát okoz: k nem deklarálható const, kivéve, ha i és j mindkettő már const érvényesül. Több elemzést kell végeznie annak érdekében, hogy kezelni tudja a változók deklarálható és inicializálható különböző módjait.
Egységtesztek készítése
Az elemző és a kódjavítás egy egyszerű esetben működik, ahol egyetlen deklaráció konstanssá tehető. Számos lehetséges deklarációs utasítás létezik, ahol ez a megvalósítás hibákat követ el. Ezeket az eseteket a sablon által írt egységteszttár használatával kezelheti. Sokkal gyorsabb, mint a Visual Studio második példányának ismételt megnyitása.
Nyissa meg a MakeConstUnitTests.cs fájlt az egységtesztelési projektben. A sablon két tesztet hozott létre, amelyek az elemző és a kódjavítási egység tesztelésének két gyakori mintáját követik.
TestMethod1 Egy teszt mintáját jeleníti meg, amely biztosítja, hogy az elemző ne jelentsen diagnosztikát, amikor nem kellene.
TestMethod2 A diagnosztika jelentésére és a kódjavítás futtatására szolgáló mintát jeleníti meg.
A sablon a Microsoft.CodeAnalysis.Testing csomagokat használja az egységteszteléshez.
Jótanács
A tesztelési kódtár egy speciális korrektúraszintaxist támogat, beleértve a következőket:
-
[|text|]: azt jelzi, hogy diagnosztikát jelent a következőhöz:text. Alapértelmezés szerint ez az űrlap csak akkor használható elemzők tesztelésére, ha pontosan egyDiagnosticDescriptorvan megadvaDiagnosticAnalyzer.SupportedDiagnosticsáltal. -
{|ExpectedDiagnosticId:text|}: azt jelzi, hogy egy diagnosztikai jelentést IdExpectedDiagnosticIdjelentettéktextszámára.
Cserélje le az MakeConstUnitTest osztály sablontesztjeit a következő tesztelési módszerre:
[TestMethod]
public async Task LocalIntCouldBeConstant_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
class Program
{
static void Main()
{
[|int i = 0;|]
Console.WriteLine(i);
}
}
", @"
using System;
class Program
{
static void Main()
{
const int i = 0;
Console.WriteLine(i);
}
}
");
}
Futtassa ezt a tesztet annak ellenőrzéséhez, hogy az megfelel-e. A Visual Studióban nyissa meg a Test Explorert aWindows>Test Explorer tesztelése> gombra kattintva. Ezután válassza az Összes futtatása lehetőséget.
Tesztek létrehozása érvényes deklarációkhoz
Általános szabály, hogy az elemzőknek a lehető leggyorsabban ki kell lépnie, minimális munkát végezve. A Visual Studio meghívja a regisztrált elemzőket, amikor a felhasználó módosítja a kódot. A válaszkészség kulcsfontosságú követelmény. A kódnak számos olyan tesztesete van, amelyek nem emelik a diagnosztikát. Az elemző már több ilyen tesztet is kezel. Adja hozzá az alábbi vizsgálati módszereket az ilyen esetek megjelenítéséhez:
[TestMethod]
public async Task VariableIsAssigned_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i = 0;
Console.WriteLine(i++);
}
}
");
}
[TestMethod]
public async Task VariableIsAlreadyConst_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
const int i = 0;
Console.WriteLine(i);
}
}
");
}
[TestMethod]
public async Task NoInitializer_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i;
i = 0;
Console.WriteLine(i);
}
}
");
}
Ezek a tesztek azért sikeresek, mert az elemző már kezeli ezeket a feltételeket:
- Az inicializálás után hozzárendelt változókat az adatfolyam-elemzés észleli.
- A már kiszűrt deklarációkat a
constkulcsszóval történő ellenőrzéssel zárjuk ki. - Az inicializáló nélküli deklarációkat az adatfolyam-elemzés kezeli, amely a deklaráción kívüli hozzárendeléseket észleli.
Ezután adjon hozzá tesztelési módszereket a még nem kezelt feltételekhez:
Deklarációk, amelyekben az inicializáló nem állandó, mert nem lehetnek fordítási időállandók:
[TestMethod] public async Task InitializerIsNotConstant_NoDiagnostic() { await VerifyCS.VerifyAnalyzerAsync(@" using System; class Program { static void Main() { int i = DateTime.Now.DayOfYear; Console.WriteLine(i); } } "); }
Ez még bonyolultabb lehet, mert a C# több deklarációt is lehetővé tesz egyetlen utasításként. Vegye figyelembe a következő esetsztring-állandót:
[TestMethod]
public async Task MultipleInitializers_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i = 0, j = DateTime.Now.DayOfYear;
Console.WriteLine(i);
Console.WriteLine(j);
}
}
");
}
A változó i konstanssá tehető, de a változó j nem. Ezért ez az állítás nem tehető const deklarációvá.
Futtassa újra a teszteket, és látni fogja, hogy az utolsó két teszteset el fog bukni.
Frissítse az elemzőt, hogy figyelmen kívül hagyja a helyes deklarációkat.
Az elemző AnalyzeNode metódusának néhány továbbfejlesztésére van szüksége az ilyen feltételeknek megfelelő kód kiszűréséhez. Ezek mind kapcsolódó feltételek, így a hasonló módosítások kijavítják ezeket a feltételeket. Végezze el a következő módosításokat:AnalyzeNode
- A szemantikai elemzés egyetlen változódeklarációt vizsgált meg. Ennek a kódnak egy
foreacholyan ciklusban kell lennie, amely az ugyanabban az utasításban deklarált összes változót megvizsgálja. - Minden deklarált változónak inicializálóval kell rendelkeznie.
- Minden deklarált változó inicializálójának fordítási időállandónak kell lennie.
A módszerben AnalyzeNode cserélje le az eredeti szemantikai elemzést:
// Perform data flow analysis on the local declaration.
DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis region.
VariableDeclaratorSyntax variable = localDeclaration.Declaration.Variables.Single();
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
a következő kódrészlettel:
// Ensure that all variables in the local declaration have initializers that
// are assigned with constant values.
foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables)
{
EqualsValueClauseSyntax initializer = variable.Initializer;
if (initializer == null)
{
return;
}
Optional<object> constantValue = context.SemanticModel.GetConstantValue(initializer.Value, context.CancellationToken);
if (!constantValue.HasValue)
{
return;
}
}
// Perform data flow analysis on the local declaration.
DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables)
{
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis region.
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
}
Az első foreach hurok minden változódeklarációt szintaktikai elemzéssel vizsgál. Az első ellenőrzés garantálja, hogy a változó inicializálóval rendelkezik. A második ellenőrzés garantálja, hogy az inicializáló állandó. A második hurok az eredeti szemantikai elemzéssel rendelkezik. A szemantikai ellenőrzések külön ciklusban vannak, mert nagyobb hatással vannak a teljesítményre. Futtassa újra a teszteket, és látnia kell, hogy mindegyik megfelel.
A végső simítások elvégzése
Már majdnem kész. Még néhány feltételt kell kezelnie az elemzőnek. A Visual Studio meghívja az elemzőket, miközben a felhasználó kódot ír. Gyakran előfordul, hogy az elemző nem lefordított kódhoz lesz meghívva. A diagnosztikai elemző metódusa AnalyzeNode nem ellenőrzi, hogy az állandó érték átalakítható-e a változó típusára. Így a jelenlegi implementáció boldogan átalakít egy helytelen deklarációt, például int i = "abc" egy helyi állandót. Adjon hozzá egy tesztmetódust ehhez az esethez:
[TestMethod]
public async Task DeclarationIsInvalid_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int x = {|CS0029:""abc""|};
}
}
");
}
Ezenkívül a referenciatípusok kezelése nem megfelelő. Egy referenciatípus esetében az egyetlen megengedett állandó érték a null. Azonban a System.String esetén sztring literálokat is engedélyez. Más szóval, const string s = "abc" jogi, de const object s = "abc" nem. Ez a kódrészlet a következő feltételt ellenőrzi:
[TestMethod]
public async Task DeclarationIsNotString_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
object s = ""abc"";
}
}
");
}
Az alaposság érdekében hozzá kell adnia egy másik tesztet annak biztosítása érdekében, hogy létre tudjon hozni egy konstans deklarációt egy karakterlánchoz. A következő kódrészlet határozza meg a diagnosztika kapcsán felmerülő kódot és a javítás alkalmazása utáni kódot is:
[TestMethod]
public async Task StringCouldBeConstant_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
class Program
{
static void Main()
{
[|string s = ""abc"";|]
}
}
", @"
using System;
class Program
{
static void Main()
{
const string s = ""abc"";
}
}
");
}
Végül, ha a kulcsszóval var deklarál egy változót, a kódjavítás helytelenül cselekszik, és létrehoz egy const var deklarációt, amelyet a C# nyelv nem támogat. A hiba kijavításához a kódjavításnak a kulcsszót a var következtetett típus nevére kell cserélnie:
[TestMethod]
public async Task VarIntDeclarationCouldBeConstant_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
class Program
{
static void Main()
{
[|var item = 4;|]
}
}
", @"
using System;
class Program
{
static void Main()
{
const int item = 4;
}
}
");
}
[TestMethod]
public async Task VarStringDeclarationCouldBeConstant_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
class Program
{
static void Main()
{
[|var item = ""abc"";|]
}
}
", @"
using System;
class Program
{
static void Main()
{
const string item = ""abc"";
}
}
");
}
Szerencsére a fenti hibák mindegyike ugyanazokkal a technikákkal kezelhető, mint az imént tanultak.
Az első hiba kijavításához először nyissa meg a MakeConstAnalyzer.cs , és keresse meg a foreach hurkot, ahol a helyi deklaráció inicializálóinak mindegyikét ellenőrzi, hogy állandó értékekkel vannak-e hozzárendelve. Közvetlenül az első foreach ciklus előtt hívja meg a context.SemanticModel.GetTypeInfo()-et a helyi deklarált típusra vonatkozó részletes információk lekéréséhez.
TypeSyntax variableTypeName = localDeclaration.Declaration.Type;
ITypeSymbol variableType = context.SemanticModel.GetTypeInfo(variableTypeName, context.CancellationToken).ConvertedType;
Ezután a foreach cikluson belül ellenőrizze az egyes inicializálókat, hogy az átalakítható-e a változótípusra. Adja hozzá a következő ellenőrzést, miután meggyőződett arról, hogy az inicializáló állandó:
// Ensure that the initializer value can be converted to the type of the
// local declaration without a user-defined conversion.
Conversion conversion = context.SemanticModel.ClassifyConversion(initializer.Value, variableType);
if (!conversion.Exists || conversion.IsUserDefined)
{
return;
}
A következő módosítás az utolsóra épül. Az első foreach ciklus záró kapcsos zárójele előtt adja hozzá a következő kódot a helyi deklaráció típusának ellenőrzésére, amikor az állandó karakterlánc vagy null értékű.
// Special cases:
// * If the constant value is a string, the type of the local declaration
// must be System.String.
// * If the constant value is null, the type of the local declaration must
// be a reference type.
if (constantValue.Value is string)
{
if (variableType.SpecialType != SpecialType.System_String)
{
return;
}
}
else if (variableType.IsReferenceType && constantValue.Value != null)
{
return;
}
A kulcsszó megfelelő típusnévre való lecseréléséhez egy kicsit több kódot kell írnia a var kódjavítás-szolgáltatóba. Térjen vissza a MakeConstCodeFixProvider.cs. A hozzáadni kívánt kód a következő lépéseket hajtja végre:
- Ellenőrizze, hogy
vardeklaráció-e, és ha igen: - Hozzon létre egy új típust a következtetett típushoz.
- Győződjön meg arról, hogy a típusdeklaráció nem alias. Ha igen, akkor a deklarálása
const vartörvényes. - Győződjön meg arról, hogy
varez nem egy típusnév ebben a programban. (Ha igen,const varakkor jogi). - A teljes típusnév egyszerűsítése
Ez úgy hangzik, mint egy csomó kód. Nem így van. Cserélje le azt a sort, amely deklarálja és inicializálja a newLocal-t a következő kóddal. Az newModifiers inicializálása után azonnal elindul.
// If the type of the declaration is 'var', create a new type name
// for the inferred type.
VariableDeclarationSyntax variableDeclaration = localDeclaration.Declaration;
TypeSyntax variableTypeName = variableDeclaration.Type;
if (variableTypeName.IsVar)
{
SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
// Special case: Ensure that 'var' isn't actually an alias to another type
// (e.g. using var = System.String).
IAliasSymbol aliasInfo = semanticModel.GetAliasInfo(variableTypeName, cancellationToken);
if (aliasInfo == null)
{
// Retrieve the type inferred for var.
ITypeSymbol type = semanticModel.GetTypeInfo(variableTypeName, cancellationToken).ConvertedType;
// Special case: Ensure that 'var' isn't actually a type named 'var'.
if (type.Name != "var")
{
// Create a new TypeSyntax for the inferred type. Be careful
// to keep any leading and trailing trivia from the var keyword.
TypeSyntax typeName = SyntaxFactory.ParseTypeName(type.ToDisplayString())
.WithLeadingTrivia(variableTypeName.GetLeadingTrivia())
.WithTrailingTrivia(variableTypeName.GetTrailingTrivia());
// Add an annotation to simplify the type name.
TypeSyntax simplifiedTypeName = typeName.WithAdditionalAnnotations(Simplifier.Annotation);
// Replace the type in the variable declaration.
variableDeclaration = variableDeclaration.WithType(simplifiedTypeName);
}
}
}
// Produce the new local declaration.
LocalDeclarationStatementSyntax newLocal = trimmedLocal.WithModifiers(newModifiers)
.WithDeclaration(variableDeclaration);
Egy using direktívát kell hozzáadnia, hogy használhassa a Simplifier típust.
using Microsoft.CodeAnalysis.Simplification;
Futtassa le a teszteket, és mindegyiknek át kell mennie. Örülj, hogy végre futtatod az elemzőt. Nyomja le a Ctrl+F5 billentyűkombinációt az elemzőprojekt futtatásához a Visual Studio egy második példányában a Roslyn Preview bővítmény betöltésekor.
- A második Visual Studio-példányban hozzon létre egy új C#-konzolalkalmazás-projektet, és adja hozzá
int x = "abc";a Main metódushoz. Az első hibajavításnak köszönhetően nem kell figyelmeztetést jelenteni ehhez a helyi változódeklarációhoz (bár a várt módon fordítóhiba történt). - Ezután adja hozzá
object s = "abc";a Fő metódushoz. A második hibajavítás miatt nem kell figyelmeztetést jelenteni. - Végül adjon hozzá egy másik helyi változót, amely a kulcsszót
varhasználja. Megjelenik egy figyelmeztetés, és egy javaslat jelenik meg a bal oldalon. - Helyezze a kurzort a hullámos aláhúzás fölé, majd nyomja meg a Ctrl+ billentyűt. a javasolt kódjavítás megjelenítéséhez. A kódjavítás kiválasztásakor vegye figyelembe, hogy a
varkulcsszó kezelése most már megfelelően történik.
Végül adja hozzá a következő kódot:
int i = 2;
int j = 32;
int k = i + j;
A módosítások után csak az első két változón kap piros hullámvonalakat. Adja hozzá a const-t mind a i-hez, mind a j-höz, és új figyelmeztetést kap a k-on, mert az mostantól const lehet.
Gratulálok! Létrehozta az első .NET-fordítóplatform-bővítményt, amely menet közbeni kódelemzést végez a probléma észleléséhez, és gyors javítást biztosít a hiba elhárításához. Az út során megismerte a .NET Fordítóplatform SDK (Roslyn API-k) részét képező kód API-kat. Az kész mintán ellenőrizheti a munkáját az adattárunk GitHub mintagyűjteményében.