Zum Inhalt springen

Die erste Hello-World-Regel in der CodeCharter-DSL schreiben

Die kleinstmögliche eigene CodeCharter-Regel. Schritt für Schritt erklärt.

Verhältnis zu anderen Seiten: dieses Tutorial führt euch Schritt für Schritt durch das Schreiben eurer ersten Regel. Syntax-Übersicht ist die kompakte Alltagsreferenz; DSL-Grammatik ist der formale Anhang.

Die einfachste sinnvolle CodeCharter-Regel hat neun Zeilen. Hier ist sie:

@name "Class must not be named Manager"
@severity warn
@category "Naming"
@description "Classes called *Manager are vague, name them after what they actually do"
@recommendation "Pick a verb-based name: OrderProcessor, EmailDispatcher, PriceCalculator"

from t in Types
where t.Kind == "Class"
where t.Name.EndsWith("Manager")
select t

Speichert das als rules/no-manager-suffix.ccr in eurem Repo. Wenn ihr nicht bei null anfangen möchtet: codecharter init legt das rules/-Verzeichnis mit zwei Beispielregeln für euch an und deklariert es unter rules: in .codecharter/config.yml. Habt ihr das Verzeichnis selbst angelegt, deklariert es einmal mit codecharter config add rules rules. Startet die Analyse dann aus dem Repo-Root. analyze erwartet den Pfad zu einer .sln-, .slnx- oder .csproj-Datei:

codecharter analyze MySolution.sln

Der Lauf liest das deklarierte rules/-Verzeichnis, die Rules:-Zeile auf stderr bestätigt es. Wenn ihr eine Klasse mit Manager-Suffix habt, taucht sie als Finding auf.

Was passiert hier

Fünf Zeilen Metadaten und vier Zeilen Query. Gehen wir das durch.

Metadaten

Alle Direktiven sind optional und haben sinnvolle Defaults. Der Regel-Slug kommt aus dem Dateinamen (no-manager-suffix), nicht aus @name. @name ist der menschenlesbare Titel, der im CI-Log und den Output-Formaten erscheint; wenn er fehlt, wird der Dateiname verwendet. @severity hat den Default warn. Auch unbekannte @severity-Werte fallen auf warn zurück; achtet also auf Tippfehler.

@name "Class must not be named Manager"
@severity warn
@category "Naming"
@description "..."
@recommendation "..."
Direktive Pflicht Default Beschreibung
@name nein Dateiname Menschenlesbarer Titel
@severity nein warn info, warn oder error
@category nein General Frei wählbar, gruppiert verwandte Regeln
@description nein -- Was die Regel prüft
@recommendation nein -- Wie man es fixt

Alle sind optional, aber @name, @description und @recommendation auszufüllen macht Findings deutlich hilfreicher.

Query

Der Body ist eine LINQ-Query gegen das Code-Modell:

from t in Types                    // alle Typen
where t.Kind == "Class"            // nur Klassen
where t.Name.EndsWith("Manager")   // mit Manager-Suffix
select t                           // gibt alle Treffer als Findings zurück

Kommentare in .ccr-Dateien nutzen //. select t ist der Pflicht-Abschluss. Was ihr selektiert, wird zum Finding. t ist ein TypeModel, also zeigt das Finding auf die Datei und Zeile, wo t deklariert ist.

Variante: Mehrere Suffixe verbieten

@name "No vague class suffixes"
@severity warn

from t in Types
where t.Kind == "Class"
where t.Name.EndsWith("Manager")
    || t.Name.EndsWith("Helper")
    || t.Name.EndsWith("Util")
select t

Bedingungen lassen sich mit && und || kombinieren. Die Query-Sprache unterstützt eine feste Auswahl an LINQ-artigen Methoden wie Where, Any, OrderBy und Count; die Syntax-Übersicht listet, was verfügbar ist.

Variante: Mehrere Bedingungen kombinieren

Eine Regel, die nur Klassen findet, die zu groß sind und zu viele Felder haben:

@name "Large class with many fields, split candidate"
@severity warn
@category "Design"

from t in Types
where t.Kind == "Class"
where t.LinesOfCode > 400
where t.Fields.Count > 15
select t

Die Regel testen

codecharter test rules/no-manager-suffix.ccr --scaffold legt eine Starter-Spec-Datei rules/no-manager-suffix.spec.md neben der Regel an. Tragt dort Code-Schnipsel ein, die die Regel treffen bzw. nicht treffen soll, und prüft die Regel anschließend mit codecharter test rules/ dagegen.

Weitere Beispiele

Die Seite Regel-Beispiele enthält eine Galerie fertiger Regeln nach Kategorie, darunter das vollständige Manager-Suffix- und Async/CancellationToken-Beispiel. Dort weiterlesen, sobald die Grundlagen sitzen.

Wo geht's weiter