Oktatóanyag: Az első elemző és kódjavítás megírása
A .NET Compiler Platform SDK biztosítja azokat az eszközöket, amelyekre szüksége 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 megcélzó diagnosztikai letiltó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 javí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 egyebekig. A .NET Fordítóplatform biztosítja az elemzés futtatásának keretrendszerét, miközben a fejlesztők kódot írnak, valamint a Visual Studio összes felhasználói felületi funkcióját a kód javításához: a szerkesztőben megjelenő hullámos megjelenítést, a Visual Studio hibalistájának feltöltését, a "villanykörte" javaslatok létrehozását és a javasolt javítások részletes előnézetének megjelenítését.
Ebben az oktatóanyagban egy elemző és egy kapcsolódó kódjavítás létrehozását ismerheti meg a Roslyn API-k használatával. Az elemzővel forráskódelemzést végezhet, és jelentheti a problémát 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ó
A .NET Compiler Platform SDK-t a Visual Studio telepítőjével kell telepítenie:
Telepítési utasítások – Visual Studio Installer
A .NET Compiler Platform SDK-t kétféleképpen keresheti meg a Visual Studio Installerben:
Telepítés a Visual Studio Telepítő – Számítási feladatok nézetével
A .NET Compiler Platform SDK nincs automatikusan kiválasztva a Visual Studio bővítményfejlesztési számítási feladatának részeként. Választható összetevőként kell kiválasztania.
- A 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ópontját az összefoglaló fában.
- Jelölje be a .NET Fordítóplatform SDK jelölőnégyzetét. Az utolsót az opcionális összetevők alatt találja.
Igény szerint azt is szeretné, hogy a DGML-szerkesztő gráfokat jelenítsen meg a vizualizációban:
- Nyissa meg az Egyes összetevők csomópontot az összegző fában.
- Jelölje be a DGML-szerkesztő jelölőnégyzetét
Telepítés a Visual Studio Installer – Egyes összetevők lap használatával
- A Visual Studio Installer futtatása
- Válassza a Módosítás lehetőséget
- Válassza az Egyes összetevők lapot
- Jelölje be a .NET Fordítóplatform SDK jelölőnégyzetét. Ezt a Fordítók, buildelési eszközök és futtatókörnyezetek szakasz tetején találja.
Igény szerint azt is szeretné, hogy a DGML-szerkesztő gráfokat jelenítsen meg a vizualizációban:
- Jelölje be a DGML-szerkesztő jelölőnégyzetét. 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ége területen válassza az Analyzer with code fix (.NET Standard) (Kódjavítással rendelkező elemző (.NET Standard) lehetőséget.
- Adja a projektnek a "MakeConst" nevet, majd kattintson az OK gombra.
Megjegyzés
Fordítási hiba jelenhet meg (MSB4062: A CompareBuildTaskVersion feladat nem tölthető be)." A probléma megoldásához frissítse a nuGet-csomagokat a megoldásban a NuGet-csomagkezelővel, vagy használja Update-Package
a Csomagkezelő konzol ablakában.
Az elemzősablon felfedezése
A kódjavítási sablonnal rendelkező elemző öt projektet hoz létre:
- Az elemzőt tartalmazó MakeConst.
- MakeConst.CodeFixes, amely tartalmazza a kódjavítást.
- 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égtesztelési projekt.
- MakeConst.Vsix, amely 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.
Tipp
Az elemző futtatásakor elindítja a Visual Studio egy 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 a Visual Studio két példányának vizualizációs beállításainak megkülönböztetésére. 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. Így a beállítások másként lesznek.
A struktúra nem csak a fejlesztés alatt álló elemzőt, hanem a korábban megnyitott elemzőket is tartalmazza. A Roslyn hive alaphelyzetbe állításához manuálisan kell törölnie a %LocalAppData%\Microsoft\VisualStudio mappából. A Roslyn hive mappaneve például a következő lesz Roslyn
: 16.0_9ae182f9Roslyn
. Vegye figyelembe, hogy a hive törlése után szükség lehet a megoldás tisztítására és újraépítésére.
Az imént elindított második Visual Studio-példányban hozzon létre egy új C#-konzolalkalmazás-projektet (bármely 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 biztosított figyelmeztető szöveg.
A sablon létrehoz egy elemzőt, amely figyelmeztetést jelent minden olyan típusdeklaráción, ahol a típusnév kisbetűket tartalmaz, az alábbi ábrán látható módon:
A sablon egy kódjavítást is biztosít, amely minden kisbetűs karaktert tartalmazó típusnevet módosít az összes nagybetűre. 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 látta a kezdeti elemzőt működés közben, 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 létrehoznia az elemző minden módosítá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 jellemző formátumát jeleníti meg, amely diagnosztika aktiválása nélkül elemzi a kódot. TestMethod2
megjeleníti a diagnosztikát kiváltó teszt formátumát, majd alkalmazza a javasolt kódjavítást. Az elemző és a kódjavítás összeállítása 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.
Tipp
Az elemzőegység-tesztek kiváló eszköznek számítanak, ha tudja, hogy milyen kódszerkezeteket kell használnia, és mit nem szabad aktiválnia az elemzőnek. 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 kereséséhez, amelyekről még nem is gondoltak.
Ebben az oktatóanyagban egy elemzőt fog írni, amely jelentést készít a felhasználónak a helyi állandókká alakítható 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 a rendszer állandó értéket rendel hozzá, x
és soha nem módosítja. Deklarálható a const
módosító használatával:
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 biztosításához, hogy a változó soha ne legyen beírva. A .NET Fordítóplatform olyan API-kat biztosít, amelyek megkönnyítik az elemzés végrehajtá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) a 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 hozhat létre. Amikor az elemző szabálysértést észlel, létrehoz egy diagnosztikai objektumot, amelyet a Visual Studio a felhasználó értesítésére használ a szabálysértésről.
A műveleteket a metódus felülbírálásában regisztrálja DiagnosticAnalyzer.Initialize(AnalysisContext) . Ebben az oktatóanyagban felkeresi a szintaxiscsomópontok helyi deklarációit, és láthatja, hogy ezek közül melyek rendelkeznek állandó értékekkel. Ha egy deklaráció állandó lehet, az elemző létrehoz és jelent egy diagnosztikát.
Az első lépés a regisztrációs állandók és Initialize
a metódus frissítése, hogy ezek az állandók a "Make Const" elemzőt jelezzék. 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 sztringerő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 többi módosítás az elemzőfájlban van. Nyissa meg a MakeConstAnalyzer.cs fájlt a Visual Studióban. Módosítsa a regisztrált műveletet a szimbólumokon működő műveletről a szintaxist használó műveletre. A metódusban MakeConstAnalyzerAnalyzer.Initialize
keresse meg azt a sort, amely regisztrálja a műveletet a szimbólumokon:
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, nem SymbolKind.NamedType az utasításokat vizsgálja. Figyelje meg, hogy AnalyzeNode
alatta piros hullámos hullám 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
"Usage" értéket a MakeConstAnalyzer.cs fájlban az alábbi kódban látható módon:
private const string Category = "Usage";
Olyan helyi deklarációk keresése, amelyek konstansok lehetnek
Ideje megírni a metódus első verzióját AnalyzeNode
. Egyetlen helyi deklarációt kell keresnie, amely lehet const
, de nem, mint az alábbi 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 AnalyzeNode
MakeConstAnalyzer.cs fájlhoz:
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
Ez a leadás mindig sikeres, mert az elemző regisztrált a helyi deklarációk módosítására, és csak a helyi deklarációkra. Más csomóponttípus nem indítja el a metódus hívását AnalyzeNode
. Ezután ellenőrizze a módosítók deklarációját const
. Ha megtalálta őket, azonnal térjen vissza. A következő kód a helyi deklarációban szereplő módosítókat keresi const
:
// 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
. Ez azt jelenti, hogy az inicializálás után soha ne rendelje hozzá.
A használatával szemantikai elemzést fog végezni.SyntaxNodeAnalysisContext Az argumentum használatával context
meghatározhatja, hogy a helyi változó deklarációja végrehajtható-e const
. Az A Microsoft.CodeAnalysis.SemanticModel egyetlen forrásfájlban található összes szemantikai információt jelöli. A szemantikai modelleket ismertető cikkben többet is megtudhat. A használatával Microsoft.CodeAnalysis.SemanticModel adatfolyam-elemzést végezhet a helyi deklarációs utasításban. Ezután ennek az adatfolyam-elemzésnek az eredményeivel biztosíthatja, hogy a helyi változó máshol ne legyen megírva új értékkel. Hívja meg a GetDeclaredSymbol bővítménymetódust, hogy lekérje 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 AnalyzeNode
metódus végéhez:
// 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 biztosítja, hogy a változó ne legyen módosítva, és így létre is hozható const
. Itt az ideje a diagnosztikának. 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));
Az előrehaladás ellenőrzéséhez nyomja le az F5 billentyűt 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 diagnosztikát kell jelentenie. A Visual Studio verziójától függően azonban a következőt fogja látni:
- Az izzó, amely továbbra is a sablon által létrehozott kódjavítást használja, azt fogja mondani, hogy nagybetűssé tehető.
- A szerkesztő tetején egy szalagcímes üzenet, amely szerint a MakeConstCodeFixProvider hibát észlelt, és le lett tiltva." Ennek az az oka, hogy a kódjavítási szolgáltatót még nem módosították, és továbbra is elemek helyett
LocalDeclarationStatementSyntax
elemeket keresTypeDeclarationSyntax
.
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 megoldja a jelentett problémát. 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ó a szerkesztő villanykörte felhasználói felületén választja ki, é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. Mindegyik felülbírálja CodeFixProvider.RegisterCodeFixesAsync(CodeFixContext) a rendelkezésre álló kódjavítások jelentését. A fájlban RegisterCodeFixesAsync
módosítsa a keresett őscsomóponttípust a LocalDeclarationStatementSyntax diagnosztikának megfelelőre:
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 szimbólumhoz MakeConstAsync
hozzáadott kódban piros hullámos hullámokat fog látni. Adjon hozzá egy deklarációt MakeConstAsync
a következő 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, deklarációt const
tartalmazó fájlláDocument.
Létre kell hoznia egy új const
kulcsszó-jogkivonatot a deklarációs utasítás elejére. Ügyeljen arra, hogy először távolítsa el a kezdő triviálisokat a deklarációs utasítás első jogkivonatából, és csatolja azt a const
jogkivonathoz. 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
jogkivonatot 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óddal való 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ér szükséges. 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. A folyamatnak három lépése van:
- Fogópont lekérése a meglévő dokumentumhoz.
- 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 MakeConstAsync
metódus végéhez:
// 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átni fogja, hogy a jelentések figyelmeztetésként jelennek meg az alábbiak szerint.
Sokat haladtál. A deklarációk alatt vannak hullámos kapcsolók, amelyek végrehajthatók const
. De még van tennivaló. Ez jól működik, const
ha hozzáadja a deklarációkat a következővel kezdődően i
: , majd j
végül k
. Ha azonban a const
módosítót egy másik sorrendben adja hozzá, kezdve a következővelk
: az elemző hibát hoz létre: k
nem deklarálható const
, kivéve, ha j
i
mindkettő már const
létezik. 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 összeállítása
Az elemző és a kódjavítás egyetlen deklaráció egyszerű esetén dolgozik, amelyet konstansként lehet megadni. Számos lehetséges nyilatkozat 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 fogja kezelni. 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
megjeleníti a diagnosztikát és a kódjavítás futtatásának mintáját.
A sablon a Microsoft.CodeAnalysis.Testing csomagokat használja az egységteszteléshez.
Tipp
A tesztelési kódtár egy speciális korrektúraszintaxist támogat, beleértve a következőket:
[|text|]
: azt jelzi, hogy a diagnosztika a következőhöztext
tartozik: . Alapértelmezés szerint ez az űrlap csak olyan elemzők tesztelésére használható, amely pontosan azDiagnosticDescriptor
általDiagnosticAnalyzer.SupportedDiagnostics
biztosított.{|ExpectedDiagnosticId:text|}
: azt jelzi, hogy a(z) diagnosztika IdExpectedDiagnosticId
a következőhöztext
tartozik: .
Cserélje le a sablonteszteket a MakeConstUnitTest
osztályban a következő vizsgálati 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 Tesztböngészőt aWindows>Test Explorer tesztelése> lehetőség kiválasztásával. 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épesség kulcsfontosságú követelmény. A kódnak számos olyan tesztesete van, amelyek nem emelik a diagnosztikát. Az elemző már kezeli az egyik ilyen tesztet, az esetet, amikor egy változót az inicializálás után rendelnek hozzá. Adja hozzá a következő tesztmetódust az eset ábrázolásához:
[TestMethod]
public async Task VariableIsAssigned_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i = 0;
Console.WriteLine(i++);
}
}
");
}
Ez a teszt is megfelel. Ezután adja hozzá a még nem kezelt feltételek tesztelési módszereit:
Deklarációk, amelyek már
const
, mert már konstansok:[TestMethod] public async Task VariableIsAlreadyConst_NoDiagnostic() { await VerifyCS.VerifyAnalyzerAsync(@" using System; class Program { static void Main() { const int i = 0; Console.WriteLine(i); } } "); }
Inicializáló nélküli deklarációk, mert nincs használható érték:
[TestMethod] public async Task NoInitializer_NoDiagnostic() { await VerifyCS.VerifyAnalyzerAsync(@" using System; class Program { static void Main() { int i; i = 0; Console.WriteLine(i); } } "); }
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ő teszteset-sztring-á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 a nyilatkozat nem tehető const deklarációvá.
Futtassa újra a teszteket, és látni fogja, hogy ezek az új tesztesetek sikertelenek.
Az elemző frissítése a helyes deklarációk figyelmen kívül hagyásához
Az elemző AnalyzeNode
metódusának néhány továbbfejlesztésére van szüksége az ilyen feltételeknek megfelelő kód szűréséhez. Ezek mind kapcsolódó feltételek, így a hasonló változások minden feltételt kijavítanak. Végezze el a következő módosításokat:AnalyzeNode
- A szemantikai elemzés egyetlen változódeklarációt vizsgált. Ennek a kódnak egy
foreach
hurokban kell lennie, amely megvizsgálja az ugyanabban az utasításban deklarált összes változót. - 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 metódusban 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 az egyes változódeklarációkat szintaktikai elemzéssel vizsgálja. 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 az összes sikeresnek kell lennie.
Az utolsó polírozás hozzáadása
Már majdnem kész. Az elemzőnek még néhány feltételt kell kezelnie. A Visual Studio meghívja az elemzőket, miközben a felhasználó kódot ír. Gyakran előfordul, hogy a rendszer meghívja az elemzőt olyan kódhoz, amely nem fordítható le. A diagnosztikai elemző metódusa AnalyzeNode
nem ellenőrzi, hogy az állandó érték átváltható-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""|};
}
}
");
}
Emellett a referenciatípusok kezelése nem megfelelő. A referenciatípushoz null
csak állandó érték adható meg, kivéve az esetet System.String, amely lehetővé teszi a sztringkonstansokat. 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"";
}
}
");
}
Ahhoz, hogy alapos legyen, hozzá kell adnia egy másik tesztet, hogy meggyőződjön arról, hogy konstans deklarációt hozhat létre egy sztringhez. A következő kódrészlet határozza meg a diagnosztika é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 egy változó deklarálva van a var
kulcsszóval, a kódjavítás helytelenül cselekszik, és létrehoz egy deklarációt const var
, amelyet a C# nyelv nem támogat. A hiba kijavításához a kódjavításnak a var
kulcsszót a kikö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ákat az imént megtanult technikákkal lehet elhárítani.
Az első hiba kijavításához először nyissa meg a MakeConstAnalyzer.cs fájlt, és keresse meg a foreach hurkot, ahol a helyi deklaráció inicializálóinak ellenőrzésekor győződjön meg arról, hogy állandó értékekkel vannak hozzárendelve. Közvetlenül az első foreach hurok előtt hívja meg a helyi context.SemanticModel.GetTypeInfo()
deklarált típus részletes információinak lekéréséhez:
TypeSyntax variableTypeName = localDeclaration.Declaration.Type;
ITypeSymbol variableType = context.SemanticModel.GetTypeInfo(variableTypeName, context.CancellationToken).ConvertedType;
Ezután a foreach
hurokon belül ellenőrizze az egyes inicializálókat, hogy az átváltható-e a változó típusára. 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 hurok 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éhez, ha az állandó sztring vagy null.
// 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;
}
Egy kicsit több kódot kell írnia a kódjavítási szolgáltatóba, hogy a kulcsszót a var
megfelelő típusnévre cserélje. Térjen vissza a MakeConstCodeFixProvider.cs fájlhoz. A hozzáadni kívánt kód a következő lépéseket hajtja végre:
- Ellenőrizze, hogy a deklaráció
var
deklaráció-e, és hogy a következő-e: - Hozzon létre egy új típust a kikövetkesztett típushoz.
- Győződjön meg arról, hogy a típusdeklaráció nem alias. Ha igen, a deklarálása
const var
törvényes. - Győződjön meg arról, hogy
var
ez nem egy típusnév ebben a programban. (Ha igen,const var
jogi). - A teljes típusnév egyszerűsítése
Ez sok kódnak hangzik. Nem az. Cserélje le a deklarált és inicializálható newLocal
sort a következő kódra. A következő inicializálása newModifiers
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);
A típus használatához egy using
direktívát kell hozzáadnia Simplifier :
using Microsoft.CodeAnalysis.Simplification;
Futtassa a teszteket, és mindegyiknek át kell mennie. Gratuláljon saját magának a kész elemző futtatásával. Nyomja le a CtrlF5billentyűkombinációt+ az elemzőprojekt a Visual Studio egy második példányában való futtatásához a Roslyn Preview bővítmény betöltésével.
- 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 fordítóhiba történt a várt módon). - Ezután adja hozzá
object s = "abc";
a Main metódust. 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
var
használja. Megjelenik egy figyelmeztetés, és megjelenik egy javaslat a bal oldalon. - Vigye a szerkesztőt a hullámos aláhúzás fölé, és nyomja le 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
var
kulcsszó 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ámos váltógombokat. Adja hozzá const
a és j
a elemet isi
, és új figyelmeztetést k
kap, mert az most már lehet const
.
Gratulálunk! 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 számos olyan kód API-t tanult meg, amelyek a .NET Fordítóplatform SDK (Roslyn API-k) részét képezik. A GitHub-adattárban található mintamintákban ellenőrizheti a munkáját.
Egyéb erőforrások
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: