Releases: rasuvaeff/understudy-phpstan
Release list
v0.5.2
- Requires
rasuvaeff/understudy^0.9 || ^0.10. The engine's 0.10 closes six
defects of the 1.0 review, the widest of them being that an optional
parameter no longer has to be spelled in a specification:when(fn () => $repository->find(1))now matches a contract whose remaining parameters
carry defaults, and an unspelled position is rendered as…in reports so
it is never mistaken for a writtenany(). Nothing this extension does changes
— the arity suppression it carries is aboutArg::rest(), which still
covers the contract's required parameters and only those. - Drops the
^0.8term, as 0.5.1 said it would in the release that
follows the engine's.
v0.5.1
- Allows
rasuvaeff/understudy^0.8 || ^0.9. A bridge, not a widening for
its own sake: the engine's 0.9 is the 1.0 candidate — every contract decision
of the 1.0 review lands there, and 1.0 follows once it has been driven by real
packages — and a project taking it must be able to keep this extension without a
window in whichcomposer requiresilently installs the 0.8 engine beside
it. The^0.8term is dropped in the release that follows the engine's. understudy.returnsis reported past the first link of the chain.
when(fn () => $gate->close())->times(2)->returns(null)and
->throws(…)->then()->returns(null)on avoidmethod were silent while
the direct form was reported: the rule read only the immediate receiver of
returns(), found aMethodCallthere and gave up — the blindness
FluentCardinalityRulehad fortimes()until 0.5.0. It walks the chain
down to the verb now. Fixes #31.- Rector is green again, which it had not been since 0.3.0:
rector.php
skips the fixture projects — they are the test input, and
SortCallLikeNamedArgsRectorwould have rewritten theMisusefixture
that pins named bounds in either order into the order that never showed the
bug — and the remaining suggestions are applied.composer release-check
is what gates a tag, and it had been red under green builds. infection/infectionmoves to^0.35, the monorepo's single-major form.- Documentation no longer dates the
Arg::rest()andArg::captor()idioms to
«understudy 0.4»: with a floor of^0.8every engine this package installs
beside has them.
v0.5.0
- Requires
rasuvaeff/understudy^0.8, and requires it as a single term. The
accumulating union it carried (^0.4 || ^0.5 || …) had to be widened by hand
on every core release, and a package that misses one becomes uninstallable
beside its own engine. ->returns(...)->times(5, 2)is reported as impossible again. The
cardinality rule read only the immediate receiver oftimes(), so it fired
onexpect(...)->times(5, 2)and stayed silent on every spelling with a link
in between — including the one the engine's own README recommends for a
repeated call.times(maximum: 5, minimum: 1)is no longer reported as impossible. The
bounds were read positionally, so a named call with the arguments in the
other order looked like(5, 1). Correct code turned red, with no way around
it but removing the extension.- A specification that reaches its double through a helper is silent.
when(fn () => $this->gate()->find(1)),$double->find($this->id())and
$this->passThrough($double->find(1))were all reported as "the closure
makes 2 calls". The engine throws on the first call that lands on a double
and never sees the rest, so a call that is the receiver of another, or one
made on$this, is not a second specified call. The total still answers "no
call at all", so a specification that reaches its double only through a
helper is not accused of specifying nothing either. understudy.matcherLeakno longer accuses a matcher that arrives
indirectly. The rule compares file offsets against the textual range of the
when()call, so a matcher hoisted into a variable, stored on a property or
written inside a closure handed over later fell outside every range and was
called a leak. A matcher inside any closure is now left alone — the closure
has not run — and a matcher that is assigned rather than passed is nobody's
argument.wire()builds a shape for a?Contractparameter. Null in the union
answered no class name, so no shape was built for the key and
$wired['doubles']['clock']stayedobject— "Call to an undefined method
object::now()" on working code, against a docblock promising that?Contract
is the same double.- Includes the matcher-location and returned-call fixes merged after 0.4.1
(#23), which had not been released.
v0.4.1
- Documentation review fixes. Requirements now lists the direct
nikic/php-parserdependency; llms.txt names the engine constraint. The
Usage section is titled «Usage» like the rest of the family. AGENTS.md's
require-checker rule states what the gate really catches: symbols referenced
in code, not annotation-only ones.
v0.4.0
A minor rather than a patch: verify($call, times: -1) is a report the
extension did not make before, so a consumer's own code can draw a diagnostic
it did not draw yesterday.
- Allow
rasuvaeff/understudy^0.7. Widened rather than raised. verify($call, times: -1)is reported.verifyProblem()fell through to
theminimum/maximumpair and droppedtimeson the way, so a negative
exact count reached no check at all. It has a unit test rather than a
fixture on purpose:verify()declaresint<0, max>|null, so wherever
PHPStan checks that annotation it reports the argument itself and this rule
never speaks — what the rule buys is the levels where it does not, and these
rules run at every level, including 0.RestAritysays why it differs from the Psalm sibling onArg::rest().
Both packages answer the same question and neither is a shortcut: there a
leaked matcher has no rule of its own, so the narrower scope is what keeps
the diagnostic alive; hereRule\MatcherLeakRulesays it louder.- The
VerbNamesdocblock carries the history its Psalm counterpart had —
thecalls()closure reported as a leak while dogfooding, andlastCall()
silently outside every rule. The two copies had drifted apart, and the
lesson is the same in both.
v0.3.0
A minor rather than a patch: the misuse rules now fire inside
expectSequence(), where they were silently absent, so a consumer's own code
can draw a diagnostic it did not draw before.
-
Allow
rasuvaeff/understudy^0.6. Widened rather than raised: the
extension works against 0.4, 0.5 and 0.6, and consumers on the older ones
should not be cut off from it. -
wire()'s shape disagreed with the core on a parameter naming more than
one contract. An intersection —BookRepository&Auditor— is ONE double
standing for both, which is whatWire::resolve()builds; the extension
dropped the parameter, so its key was reported as a missing offset on code
that wires and runs. A union of two object types makes the core refuse the
class outright (CannotWire), and reporting a missing key there named the
wrong mistake: no shape describes a call that never returns, so none is
produced. Fixes #16. -
The mutation gate rises from 95 to 97. Re-measured on PHP 8.4, the version
the coverage job pins: 188 of 191 mutants killed, 98.43%, with the same
three equivalent survivors the config already names. The gate is one mutant
below the measurement rather than a round number three points under it. -
expectSequence()is recognised as a specification verb. It was known to
neither spelling's list, soSpecificationRangeCollectornever recorded the
call andunderstudy.matcherLeakwas reported for every matcher inside an
armed protocol — a false report on code the engine accepts. The closure and
matcher-kind rules were silently absent on the same closures. Fixes #13. -
VerbNamesTestnow walks the core's own public surface and fails when a
closure-taking verb is missing from either list.expectSequence()was the
second verb to fall outside every rule afterlastCall(); a list nobody
checks is what let both happen. -
Both READMEs say what was only implied: the
understudy.*identifiers are
stable, because they are what a consumer writes intoignoreErrors, while
the wording of a message is not.
v0.2.1
- The Requirements section of both READMEs said
rasuvaeff/understudy^0.1
whilecomposer.jsonhas required^0.4since 0.2.0. - Allow
rasuvaeff/understudy^0.5. Widened rather than raised: the
extension works against both, and 0.4 consumers should not be cut off from
it.
v0.2.0
A minor rather than a patch: new behaviour toward the consumer's own code
(an ignored arity report, a newly typed matcher) and a raised dependency
floor are both boundaries Composer's caret already treats as breaking on 0.x.
- The
rasuvaeff/understudyfloor rises to^0.4: the fixtures that prove
the new behaviour are written in the 0.4 idioms, and a lowest-versions run
against 0.1 would be proving nothing. Consumers on an older understudy stay
on the 0.1.x line of this package. - understudy 0.4 idioms (#6).
Arg::rest():
thearguments.countreport is ignored (via anIgnoreErrorExtension)
wherever a call's last written argument isArg::rest(); in a real call
the line is reported once, asunderstudy.matcherLeak— the leak is the
actual mistake, and the engine answers the call withArgumentCountError.
Arg::captor():$captor->capture()is typedneverlike theArg::
factories when the receiver is aCaptor, no longer counts against
"exactly one call per closure", and is reported byunderstudy.matcherLeak
when it reaches a real call;Arg::captor()itself stopped being collected
as a matcher — it is the factory, legitimately outside any specification,
and was falsely reported as a leak.
v0.1.3
- Allow
rasuvaeff/understudy^0.4:Arg::rest(),Arg::captor(),
Understudy::delegate(),Understudy::lean()and rendered property hooks
are all additive — the adapter needs no code change.
v0.1.2
- Allow
rasuvaeff/understudy^0.3(the engine refuses colliding same-call
when()/expect()registrations withConflictingExpectationfrom 0.3.0;
nothing in this adapter changes behaviour).