HowTo: Eigene Regeln für Microsoft StyleCop erstellen
Im ersten Teil der Serie habe ich erklärt, wie man StyleCop grundsätzlich in Verbindung mit MSBuild und Visual Studio verwenden kann. Heute möchte ich kurz erklären, wie man eigene Regeln für StyleCop definiert und diese mit dem Setup vom ersten Teil verwenden kann. Da ich selber ein verfechter davon bin, private Member (Properties, Klassen, Methoden, etc.) mit einem _ (Unterstrich) als Präfix zu versehen, möchte ich heute eine StyleCop Regel implementieren, die genau diesen Standard überprüft.
Eigene Regeln implementieren!
Um eigene Regeln für StyleCop zu implementieren, müssen wir zuerst einmal StyleCop herunterladen bzw. installieren, weil wir dabei 2 wichtige Assemblies bekommen, die wir für die Entwicklung benötigen. Als nächstes erstellen wir uns in Visual Studio eine leere Klassenbibliothek und referenzieren die 2 Assemblies
- Microsoft.StyleCop.dll
- Microsoft.StyleCop.CSharp.dll
Haben wir das, erstellen wir uns eine neue Klasse und leiten diese von Microsoft.StyleCop.SourceAnalyzer ab. Das ist die Basisklasse für alle Regeln, die in StyleCop verfügbar sein sollen. Zusätzlich definieren wir über der Klasse noch ein Attribut, das angibt, für welche Sprache die Regel verwendet werden kann: [SourceAnalyzer(typeof(CsParser))].
Die Basisklasse implementiert eine Methode namens AnalyzeDocument(…), die wir in unserer eigenen Klasse nun überschreiben sollten, um auch wirklich auf den Quellcode der einzelnen Dateien, die später von unserer Regel evaluiert werden sollen, Zugriff zu haben:
[sourcecode language="csharp"]
public override void AnalyzeDocument(CodeDocument document)
{
CsDocument csdocument = document as CsDocument;
if (csdocument == null) return;
csdocument.WalkDocument(new CodeWalkerElementVisitor<object>(this._VisitElement));
}
[/sourcecode]
Die Methode hat einen Parameter vom Type CodeDocument. Dieses Dokument können wir anschließend auf ein CsDocument casten, da wir ja wissen, dass unsere Regel nur für CSharp Dokumente verwendet werden kann. Um jetzt das Dokument durchlaufen zu können, bietet das SDK hierfür verschiedene Klassen an, mit den denen unterschiedliche Typen evaluiert werden können. Wir verwenden hier die Klasse CodeWalkerElementVisitor, weil wir nur an Elementen (Klassen, Properites, usw.) interessiert sind. Diese Klassen verwenden das Visitor Design Pattern. Das heißt, dass der Visitor das Quelldokument durchparst und sobald er auf ein (in unserem Falle) Element trifft, die Methode _VisitElement aufruft:
[sourcecode language="csharp"]
private bool _VisitElement(CsElement element, CsElement parentElement, object context)
{
if (element.Generated) return true;
if (element.AccessModifier == AccessModifierType.Public ||
element.AccessModifier == AccessModifierType.Internal) return true;
if (element.ElementType == ElementType.Field ||
element.ElementType == ElementType.Accessor ||
element.ElementType == ElementType.Constructor ||
element.ElementType == ElementType.ConstructorInitializer ||
element.ElementType == ElementType.Destructor ||
element.ElementType == ElementType.EmptyElement ||
element.ElementType == ElementType.EnumItem ||
element.ElementType == ElementType.ExternAliasDirective ||
element.ElementType == ElementType.File ||
element.ElementType == ElementType.Indexer ||
element.ElementType == ElementType.Namespace ||
element.ElementType == ElementType.Root ||
element.ElementType == ElementType.UsingDirective) return true;
if (element.Declaration.Name.Trim().StartsWith("_")) return true;
this.AddViolation(element, element.LineNumber, "NonPublicMemberUnderscorePrefix");
return true;
}
[/sourcecode]
Die Methode überprüft, ob es sich bei dem Element um einen Typ handelt, der für uns auch von Interesse ist. Falls ja, muss der Name des Elements mit einem Unterstrich beginnen. Tut es das nicht, verwenden wir die Methode AddViolation der Basisklasse, um StyleCop mitzuteilen, dass wir auf eine Regelverletzung gestossen sind. Der 3. Parameter “NonPublicMemberUnderscorePrefix” ist wichtig: Um diese Regel nun tatsächlich verwenden zu können, müssen wir sie noch mit einigen Metainformationen ausstatten:
MetaInfos für StyleCop Regeln
StyleCop verwendet für jede Regel ein XML File, das die notwendigen Metainformationen für jede Regel beinhaltet. Wir müssen daher für unsere eigene Regel noch ein XML File erstellen, dass genau diese Metainformationen beinhaltet. Zuerst fügen wir zu unserem VS Projekt ein neues XML File hinzu und ändern in den Properties die Eigenschaft Build Action auf Embedded Resource. Anschließend können wir im XML File die Metainformationen befüllen:
[sourcecode language="xml"]
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="My Rules">
<Description>
My custom StyleCop rules.
</Description>
<Rules>
<RuleGroup Name="Naming Rules">
<Rule Name="NonPublicMemberUnderscorePrefix" CheckId="MY1001">
<Context>
NonPublic members must start with an underscore
followed by an upper-case letter.
</Context>
<Description>
NonPublic members must start with an
underscore followed by an upper-case letter.
</Description>
</Rule>
</RuleGroup>
</Rules>
</SourceAnalyzer>
[/sourcecode]
Der Aufbau dieses XMLs ist relativ einfach. Zuerst definieren wir einen neuen SourceAnalyser und geben ihm einen Namen und eine Beschreibung. Dann definieren wir eine neue Gruppe für unsere Regel (RuleGroup, wir könnten auf diese Gruppe auch verzichten). Und jetzt kommt der wirklich interessante Teil. Wir definieren unsere eigene Regel und geben ihr einen Namen: NonPublicMemberUnderscorePrefix. Dieser Name muss exakt mit dem übereinstimmen, den wir vorher bei der Methode AddViolation(…) im Sourcecode als 3. Parameter angegeben haben. Sind diese Namen unterschiedlich, kann StyleCop die Metainformationen nicht eindeutig zuordnen. WICHTIG: Der Name der XML Datei muss exakt der gleiche wie der Name der Sourcecode Datei sein. Also heißt die *.cs Datei MyRules.cs muss die XML auch MyRules.xml heißen.
Um diese Regel jetzt auch tatsächlich in StyleCop verwenden zu können, kopieren wir sie in das Installationsverzeichnis von StyleCop. Starten wir jetzt den StyleCopSettingsEditor, sollten wir unsere neue Regel bereits in der Liste sehen können:
Integration mit MSBuild
Um solche eigenen Regeln auch mit dem Setup aus dem ersten Teil verwenden zu können, müssen wir am *.targets File von StyleCop etwas verändern. Bei diesem Setup haben wir alle Dateien (*.dll und *.targets), die für StyleCop notwendig sind, in einem relativen Verzeichnispfad abgelegt. In das selbe Verzeichnis kopieren wir nun unsere soeben erstellte StyleCop-Assembly. Im selben Verzeichnis sollte eigentlich eine Datei namens Microsoft.StyleCop.Targets liegen. Die öffnen wir uns suchen nach der Zeile AdditionalAddinPaths=”…”. Der Pfad, der dort eingetragen ist, gibt an, wo zusätzliche Assemblies gespeichert sind, in denen eigene StyleCop Regeln definiert sind. Nun, da wir gerade unsere Assembly in das selbe Verzeichnis kopiert haben, können wir mit den VS Macros direkt relativ auf dieses Verzeichnis verweisen:
[sourcecode language="xml"]
<StyleCopTask
ProjectFullPath="$(MSBuildProjectFile)"
SourceFiles="@(StyleCopFiles)"
AdditionalAddinPaths="$(ProjectDir)\Integration\"
ForceFullAnalysis="$(StyleCopForceFullAnalysis)"
DefineConstants="$(DefineConstants)"
TreatErrorsAsWarnings="$(StyleCopTreatErrorsAsWarnings)"
CacheResults="$(StyleCopCacheResults)"
OverrideSettingsFile="$(StyleCopOverrideSettingsFile)"
OutputFile="$(StyleCopOutputFile)"
MaxViolationCount="$(StyleCopMaxViolationCount)" />
[/sourcecode]
Das wars. Der Sourcecode (den ich etwas erweitert habe) kann wie immer unter http://downloads.juergenoberngruber.at/blog/CustomStyleCopRules.zip herunter geladen werden.
In diesem Sinne.









