Zum Inhalt springen

Rule-Criteria

Das rule:-Kriterium eines Requirements wird gegen den Graphen eurer Lösung geprüft — holds, violated mit dem konkreten Pfad, oder unknown mit dem genauen ungelösten Punkt.

Das rule:-Kriterium eines Requirements trägt einen CodeGuard-Rule-DSL-Ausdruck, der gegen euren Code halten muss. Anders als test:, manual:, integration: und external:, die einen Pin gegen etwas an anderer Stelle Geprüftes festhalten, wertet CodeCharter ein rule:-Kriterium selbst aus, gegen denselben Graphen, den auch codecharter analyze aufbaut. codecharter req check kompiliert den Ausdruck, führt ihn gegen das analysierte Modell aus und meldet eines von drei Verdicts.

Die drei Verdicts

Holds. Die Regel wurde kompiliert, ausgeführt und hat nichts zu beanstanden gefunden. Das ist kein Beweis, dass das Requirement lückenlos ist, sondern genau das, was die geschriebene Regel tatsächlich prüft, nicht mehr und nicht weniger.

Violated. Die Regel hat mindestens eine Stelle im Code gefunden, die sie verletzt. Der Bericht nennt die konkrete verletzende Entität und, wo das Kriterium typbezogen ist (ein Reaches/ReachedBy-Predicate über Typen), einen Witness: einen realen Pfad von einem passenden Quelltyp zu einem passenden Zieltyp, gerendert als Type -[kind]-> Type. Das Verdict wird allein durch die Auswertung der Regel entschieden; der Witness ist Dekoration darauf. Ein Kriterium bleibt deshalb auch dann Violated, wenn im seltenen Fall kein Witness gerendert werden konnte (der Bericht zeigt dann path-unavailable: <Grund> statt einer Kette).

Unknown. Etwas am Kriterium oder am Code konnte nicht eindeutig entschieden werden. Das ist der wichtige Fall, siehe unten.

Was Unknown in 1.6.0 bedeutet

Unknown heißt, dass CodeCharter euch sagt, dass es keine Antwort geben kann, nicht dass es alles gesehen und nichts gefunden hat. Konkret:

  • Eine Regel, die eine Konstruktion abfragt, durch die die statische Analyse von CodeCharter grundsätzlich nicht hindurchsehen kann, etwa ein nicht erkannter Reflection-Aufruf, ein Delegate-Aufruf oder ein Dispatch-Pfad, der sich erst zur Laufzeit auflöst, kommt als Unknown zurück statt still als Holds durchzugehen. Vor 1.6.0 (und für alles, was diese Version noch nicht erkennt) liest sich dieselbe Konstruktion weiterhin als Holds: eine statische Analyse, die einen Aufruf nicht sieht, sieht ihn einfach nicht, was ohne explizite Kennzeichnung nicht von „da ist nichts" zu unterscheiden ist. Rule-Criteria kennzeichnen das für die unten gelisteten Konstruktionen explizit, für alles andere gilt weiterhin das ältere, ehrlich begrenzte Verhalten.
  • Dataflow-Analyse, Taint-Tracking und Timing-/Nebenläufigkeitseigenschaften werden gar nicht erst versucht. Ein rule:-Kriterium, das so etwas braucht, bleibt Aufgabe eines echten Tests (test:), hier bewusst abwesend statt mit einer Prüfung vorgetäuscht, die gründlich aussieht, es aber nicht ist.
  • Ein verschriebenes Typnamen-Glob (Reaches(t => t.Name.Matches("Sevrice"))), das auf null Typen in eurer Lösung passt, ist Unknown, nicht ein vakuos-wahres Holds: ein Kriterium, das still nichts prüft, ist schlimmer als eines, das das sagt.

Unknown-Token

Jedes Unknown-Verdict nennt eine konkrete Edge-Id, damit der Bericht umsetzbar ist statt ein bloßes „irgendetwas stimmt nicht". Jedes Token ist entweder Tier A, ein Defekt im Kriterium oder im Lauf selbst, niemals acknowledgeable, weil es an eurem Code nichts zu akzeptieren gibt, oder Tier B, eine echte Lücke in dem, was diese Analyse sehen kann, die ihr über acknowledge: bewusst akzeptieren könnt (siehe unten).

Edge-Id Tier Bedeutung
analysis-unavailable A Für diesen Lauf wurde kein Codemodell analysiert.
criterion-not-compilable A Die DSL des Kriteriums konnte nicht geparst werden.
criterion-schema-mismatch A Das Kriterium fragt eine Root-Collection ab, die diese Engine-Version nicht kennt.
criterion-param-unbound A Das Kriterium deklariert einen @param ohne Default, für den kein Override gesetzt wurde.
criterion-evaluation-error A Die Auswertung des Kriteriums hat einen Fehler geworfen (z. B. ein unbekanntes Member auf einer Entität).
pattern-matches-no-type B Ein Reaches/ReachedBy-Typnamen-Glob-Literal passt auf null Typen in der Lösung.
dispatch-divergence B Das Kriterium hält unter deklarierten Aufrufen, ist aber unter virtuellem Dispatch verletzt, oder umgekehrt.
opaque-site B Ein Aufruf innerhalb eines vom Kriterium ausgewählten Typs zielt auf eine erkannte Reflection-/Opaque-Dispatch-Konstruktion (Activator.CreateInstance, Type.GetType, MethodInfo.Invoke, der Aufruf eines Delegate-typisierten Parameters oder Felds, …), die die statische Analyse nicht auf ein konkretes Ziel auflösen kann.

Ein rule:-Kriterium, dessen analysiertes Modell Call-Edge-Informationen verloren hat (eine mehrdeutige Überladung, ein dynamic-Aufruf), bekommt trotzdem ein Holds- oder Violated-Verdict; diese Lücke blockiert das Kriterium nicht, aber der Bericht ergänzt einen Hinweissatz, der nennt, wie viele Aufrufe betroffen sind, damit die Lücke sichtbar bleibt statt still zu verschwinden.

Ein Unknown acknowledgen

Ein Tier-B-Unknown blockiert das Requirement, bis ihr entweder den Code korrigiert (die Konstruktion analysierbar macht, den Glob-Tippfehler behebt) oder es über einen acknowledge:-Eintrag am Kriterium bewusst akzeptiert:

criteria:
  - id: C1-1
    rule: Types.Where(t => t.Name.EndsWith("Repository")).All(t => t.ImplementedInterfaces.Any(i => i.Name.EndsWith("Repository")))
    acknowledge:
      - edge: "opaque-site:M:MyApp.Data.RepositoryFactory.Create#Activator.CreateInstance"
        reason: "Factory ist eine geschlossene Menge von drei bekannten Repository-Typen, geprüft am 02.09.2026."

edge und reason sind beide Pflicht: ein Eintrag ohne reason wird als req-acknowledge-missing-reason gemeldet und ausgewertet, als wäre er nie geschrieben worden. edge muss exakt auf die Id eines Unknown-Edge passen; die Liste ist ordinal nach Id sortiert, sodass dieselbe Fixture bei jedem Lauf dieselbe Reihenfolge liefert.

Ein Tier-A-Unknown lässt sich nie acknowledgen: seine Id in acknowledge: zu nennen hat keine Wirkung, weil es an eurem Code nichts zu akzeptieren gibt, nur einen Defekt im Kriterium oder im Lauf. Behebt stattdessen das Kriterium.

Edge-Ids und Umbenennen

Eine Edge-Id trägt keine Zeilennummern, ein Reformat der Datei macht ein Acknowledgment also nicht ungültig. Sie trägt aber die Deklaration oder das Member, an dem das Unknown hängt (im Beispiel oben M:MyApp.Data.RepositoryFactory.Create). Wird diese Deklaration umbenannt, ändert sich ihre Id und damit die Edge-Id, absichtlich: das Acknowledgment wurde gegen ein konkret benanntes Ding geprüft, und eine Umbenennung bedeutet, dass ein Mensch noch einmal hinschauen sollte, nicht dass das alte Sign-off still für das gilt, wie die Deklaration jetzt heißt.

Ein acknowledge:-Eintrag, der auf kein aktuelles Unknown-Edge mehr passt (die Lücke wurde behoben, oder das benannte Ding wurde umbenannt), wird als veraltetes Acknowledgment gemeldet, sichtbar zum Aufräumen, kippt aber nie von selbst ein Verdict.

Exit-Codes und Ausgabeformate stehen bei codecharter req check.