Commit 62535bb
authored
* GH-75: Document the defensive guards the issue called dead, and cover four
The issue lists five "dead defensive code paths" to remove. Measured, none
is dead - each is a guard that is unreachable through the value converter's
CHAIN but live for another entry, and removing them would drop real
protection:
- The two null guards (builtin, object trait) throw for a null on a
non-nullable target. Through the chain NullValueConversionStrategy claims
every null first, so they never run there - but the strategy classes are
public SPI, and a direct convert() call reaches them. Proven: a direct
builtin convert(null, int) throws TypeMismatchException, as it should.
- The convertObjectValue non-object branch hands the value back for a type
its own supports() would reject - reached only by a direct call skipping
supports(). Proven the same way.
- The ValueConverter LogicException is unreachable while the passthrough
strategy (supports() always true, last) is registered, and guards the
invariant if that ever changes - otherwise the method would fall off the
end returning null.
- The "collection class must be provided" throw is not a runtime guard at
all but a load-bearing assertion: it narrows ?string to string for the
calls below, and PHPStan max fails without it. Removing it, verified,
breaks static analysis.
So the resolution is the acceptance criterion's "documented reason", not
removal: each branch now says why it cannot occur on the normal path and
why it is kept. The four SPI/invariant guards also gain tests that drive
them directly, turning "documented dead branch" into "live, covered
branch" - the criterion's preferred outcome for the null guards.
The architecture audit on the issue asked that the null guards not be
deleted before the two-entry-points fix (#87, still open), since a single
entry point may make them live through the chain too. Documenting and
covering rather than deleting respects that: nothing here has to be
revisited when #87 lands, and if #87 makes a guard live on the chain, its
test simply gains a second caller.
* GH-75: Cover the object null guard, drop the misuse-contract test, pin the message
Review found my commit message claimed to cover four guards but only three
tests existed - and the missing one was exactly the object-trait null
guard. Removing its throw made convertObjectValue return null for a
non-nullable object target with the whole suite still green, the precise
regression this was meant to prevent. Added theObjectGuardRefusesANull...,
verified by mutation: neutralising the throw now fails it.
Dropped theObjectGuardHandsBackAValueForANonObjectType. It asserted the
exact silent-passthrough the object trait does for a type it does not
support - but the SPI interface promises nothing about convert() on an
unsupported type, so the test froze a misuse-only implementation detail as
contract, and a future fail-fast refactor would break it for no real
reason. The branch keeps its documented reason; it is defensive against a
protocol violation, which the acceptance criterion covers by documentation
rather than a test.
And the builtin guard test asserted only the exception type, which several
sites in that strategy throw. It now matches the message, so it pins this
guard rather than any TypeMismatchException.
1 parent 976e49a commit 62535bb
5 files changed
Lines changed: 113 additions & 0 deletions
File tree
- src
- JsonMapper/Value
- Strategy
- tests/JsonMapper/Value
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
483 | 483 | | |
484 | 484 | | |
485 | 485 | | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
486 | 491 | | |
487 | 492 | | |
488 | 493 | | |
| |||
Lines changed: 5 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
245 | 245 | | |
246 | 246 | | |
247 | 247 | | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
248 | 253 | | |
249 | 254 | | |
250 | 255 | | |
| |||
Lines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
63 | 67 | | |
64 | 68 | | |
65 | 69 | | |
| |||
77 | 81 | | |
78 | 82 | | |
79 | 83 | | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
80 | 87 | | |
81 | 88 | | |
82 | 89 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
60 | 64 | | |
61 | 65 | | |
62 | 66 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
0 commit comments