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.