JustDummies

Juste des dummies, mais redoutablement efficaces.

Sections de la documentation

JD032 : BoundDeclaredTwice

PropriétéValeur
CatégorieConstraints (JustDummies.Constraints)
Sévérité🟠 Avertissement
Activée par défautOui

Une chaîne déclare deux fois la même borne, et seule la plus serrée des deux survit.

Les bornes se replient silencieusement et de façon monotone : un minimum garde la plus grande des deux valeurs, un maximum la plus petite, et l’appel perdant renvoie le générateur inchangé. Rien ne lève, et aucun rapport d’exécution ne le mentionne. L’un des deux appels est donc toujours mort — le plus lâche, quel que soit l’ordre dans lequel vous les avez écrits.

Non conforme

string reference = Any.String().WithMinLength(8).WithMinLength(10).Generate();  // tire ≥ 10 ; le 8 est effacé
int    quantity  = Any.Int32().LessThanOrEqualTo(50).LessThanOrEqualTo(90).Generate();  // tire ≤ 50 ; le 90 est inerte

Conforme

string reference = Any.String().WithMinLength(10).Generate();
int    quantity  = Any.Int32().LessThanOrEqualTo(50).Generate();

Gardez la borne que vous vouliez et supprimez l’autre. Si vous vouliez un intervalle, nommez-le : WithLengthBetween(10, 20), Between(1, 50) — voir JD031.

Les quatre vocabulaires, les deux ordres

borne déclarée deux foisgénérateurs
WithMinLength, WithMaxLengthAnyString
WithMinCount, WithMaxCounttous les générateurs de collections
GreaterThanOrEqualTo, LessThanOrEqualTo, GreaterThan, LessThanles générateurs numériques et TimeSpan
AfterOrEqualTo, BeforeOrEqualTo, After, BeforeAnyDateOnly, AnyDateTime, AnyDateTimeOffset, AnyTimeOnly

Les bornes strictes sont signalées ici, là où JD031 les refuse. JD031 doit nommer un appel de remplacement et ne peut pas le faire pour une paire stricte sans réécrire vos nombres ; cette règle-ci ne nomme aucun remplacement, donc GreaterThan(1).GreaterThan(5) est aussi signalable que n’importe quel autre doublon.

Le diagnostic se pose sur la seconde déclaration — l’appel qui a rendu la chaîne ambiguë, et celui dont vous pesez la suppression. Il ne dit délibérément pas laquelle des deux survit : c’est la plus serrée, et y répondre supposerait d’évaluer des arguments que cette règle ne lit jamais, sur des types comme TimeSpan et DateTime qu’aucune constante de compilation ne peut représenter.

Pourquoi un avertissement, là où JD024 est une information

Le précédent, c’est JD025, pas JD024. Une valeur listée deux fois dans un pool s’effondre silencieusement, rien ne lève, le domaine reste bien défini — et c’est un avertissement, parce que la surface se lit plus grande qu’elle n’est et que l’écart apparaît loin de l’endroit où il a été écrit. Une borne déclarée deux fois a exactement la même forme.

JD024 est en information pour une raison qui ne se transporte pas : une contrainte inerte a une lecture défendable, puisqu’une équipe peut exclure une sentinelle avant que l’intervalle capable de la produire n’existe — la contrainte documente alors une intention qui survit à un élargissement futur. Une borne écrite deux fois dans une même expression n’a pas cette lecture. Les deux appels sont devant le même lecteur, la plus serrée efface simplement la plus lâche, et il n’existe aucun futur où l’appel effacé se mettrait à compter.

Ce qu’elle ne signale pas

Des bornes détenues sous un nom. C’est le point important, et c’est une question de justesse et non de portée :

AnyString atLeastEight = Any.String().WithMinLength(8);
AnyString atLeastTen   = atLeastEight.WithMinLength(10);

Un générateur est une recette immuable : atLeastEight existe toujours et tire toujours un minimum de huit. C’est un générateur légitime à part entière, rien dans WithMinLength(8) n’est mort, et le signaler serait un faux positif. Ce qui rend l’appel le plus lâche mort à l’intérieur d’une seule chaîne, c’est que le générateur intermédiaire est sans nom et inatteignable — dès qu’on lui donne un nom, ce n’est plus vrai. La règle ne suit donc jamais un générateur à travers une variable locale, un champ, un paramètre ou une seconde instruction.

  • Une borne atteinte par un alias. NonEmpty() est une longueur minimale de un et Positive() un minimum de un, donc Any.String().NonEmpty().WithMinLength(8) atteint bien deux fois la même borne — et reste muette. Choisir l’alias dit sur l’intention quelque chose que la borne explicite ne dit pas, et le choix entre deux écritures correctes est la question de l’ADR-0077 ouvre un nouvel onglet, pas celle de cette règle.
  • Deux bornes différentes. WithMinLength(8).WithMaxLength(20) est un intervalle, pas un doublon. JD031 a quelque chose à en dire ; pas cette règle.
  • Une forme intervalle. Between(1, 50).Between(2, 20) porte deux bornes par appel, donc aucun appel entier ne meurt. Hors périmètre ici, et toujours couvert par JD024 quand il ne rétrécit rien.
  • Une taille exacte. WithLength et WithCount se déclarent une seule fois par générateur et une seconde déclaration lève, donc l’exécution les rapporte déjà, bruyamment.
  • Une chaîne dont on assertit qu’elle lève, comme pour les autres règles de contraintes.

Lire la source, ou la corriger là-bas ouvre un nouvel onglet· Repris depuis lib-v1.0.0-preview.6