CodeGuard

Hello-World-DSL

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

Hello-World-DSL

Die einfachste sinnvolle CodeGuard-Regel hat sechs 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

Speichere das als .codeguard/rules/no-manager-suffix.cgr und führe in deinem Repo aus:

codeguard analyze .

Wenn ihr eine Klasse mit Manager-Suffix habt, taucht sie als Finding auf.

Was passiert hier

Sechs Zeilen Metadaten und vier Zeilen Query. Lass uns das durchgehen.

Metadaten

Jede Regel braucht mindestens @name und @severity. Die anderen sind optional aber empfohlen, sie tauchen im Editor-Hover, im CI-Log und in den Output-Formaten wieder auf.

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

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

select t ist der Pflicht-Abschluss. Was du selectest wird zum Finding - der t ist ein TypeModel, also wird das Finding auf die Datei und Zeile zeigen wo t deklariert ist.

Variante: Suffix dynamisch verbieten

@name "No vague class suffixes"
@severity warn

from t in Types
where t.Kind == "Class"
where new[] { "Manager", "Helper", "Util", "Utility" }
    .Any(suffix => t.Name.EndsWith(suffix))
select t

LINQ-Expressions sind möglich. Du kannst inline-Arrays bauen, sortieren, filtern.

Variante: Mit Sub-Query

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

Variante: Methoden statt Typen

@name "Public async method without CancellationToken"
@severity warn
@category "Async"

from m in Methods
where m.IsAsync
where m.AccessModifier == "Public"
where !m.IsOverride
where !m.Parameters.Any(p => p.TypeShortName == "CancellationToken")
select m

Dasselbe Schema, nur mit Methods statt Types als Startpunkt.

Wo geht's weiter