diff --git a/lib/Internal/Placeholder.php b/lib/Internal/Placeholder.php index b84aa70..e0d2f0d 100644 --- a/lib/Internal/Placeholder.php +++ b/lib/Internal/Placeholder.php @@ -145,7 +145,10 @@ trait Placeholder } try { - /** @var mixed $result */ + /** + * @var mixed $result + * @psalm-suppress PossiblyInvalidMethodCall https://github.com/vimeo/psalm/issues/3033 + */ $result = $onResolved(null, $this->result); $onResolved = null; // allow garbage collection of $onResolved, to catch any exceptions from destructors diff --git a/lib/LazyPromise.php b/lib/LazyPromise.php index f87a4d1..96be33f 100644 --- a/lib/LazyPromise.php +++ b/lib/LazyPromise.php @@ -12,7 +12,7 @@ final class LazyPromise implements Promise /** @var callable|null */ private $promisor; - /** @var \Amp\Promise|null */ + /** @var Promise|null */ private $promise; /** @@ -30,11 +30,15 @@ final class LazyPromise implements Promise public function onResolve(callable $onResolved) { if ($this->promise === null) { + \assert($this->promisor !== null); + $provider = $this->promisor; $this->promisor = null; $this->promise = call($provider); } + \assert($this->promise !== null); + $this->promise->onResolve($onResolved); } } diff --git a/lib/Loop/Driver.php b/lib/Loop/Driver.php index 57d6862..2d2ca19 100644 --- a/lib/Loop/Driver.php +++ b/lib/Loop/Driver.php @@ -182,6 +182,7 @@ abstract class Driver */ public function defer(callable $callback, $data = null): string { + /** @psalm-var Watcher $watcher */ $watcher = new Watcher; $watcher->type = Watcher::DEFER; $watcher->id = $this->nextId++; @@ -216,10 +217,12 @@ abstract class Driver throw new \Error("Delay must be greater than or equal to zero"); } + /** @psalm-var Watcher $watcher */ $watcher = new Watcher; $watcher->type = Watcher::DELAY; $watcher->id = $this->nextId++; $watcher->callback = $callback; + /** @psalm-suppress InvalidPropertyAssignmentValue https://github.com/vimeo/psalm/issues/3035 */ $watcher->value = $delay; $watcher->data = $data; @@ -251,10 +254,12 @@ abstract class Driver throw new \Error("Interval must be greater than or equal to zero"); } + /** @psalm-var Watcher $watcher */ $watcher = new Watcher; $watcher->type = Watcher::REPEAT; $watcher->id = $this->nextId++; $watcher->callback = $callback; + /** @psalm-suppress InvalidPropertyAssignmentValue https://github.com/vimeo/psalm/issues/3035 */ $watcher->value = $interval; $watcher->data = $data; @@ -285,10 +290,12 @@ abstract class Driver */ public function onReadable($stream, callable $callback, $data = null): string { + /** @psalm-var Watcher $watcher */ $watcher = new Watcher; $watcher->type = Watcher::READABLE; $watcher->id = $this->nextId++; $watcher->callback = $callback; + /** @psalm-suppress InvalidPropertyAssignmentValue https://github.com/vimeo/psalm/issues/3035 */ $watcher->value = $stream; $watcher->data = $data; @@ -319,10 +326,12 @@ abstract class Driver */ public function onWritable($stream, callable $callback, $data = null): string { + /** @psalm-var Watcher $watcher */ $watcher = new Watcher; $watcher->type = Watcher::WRITABLE; $watcher->id = $this->nextId++; $watcher->callback = $callback; + /** @psalm-suppress InvalidPropertyAssignmentValue https://github.com/vimeo/psalm/issues/3035 */ $watcher->value = $stream; $watcher->data = $data; @@ -354,10 +363,12 @@ abstract class Driver */ public function onSignal(int $signo, callable $callback, $data = null): string { + /** @psalm-var Watcher $watcher */ $watcher = new Watcher; $watcher->type = Watcher::SIGNAL; $watcher->id = $this->nextId++; $watcher->callback = $callback; + /** @psalm-suppress InvalidPropertyAssignmentValue https://github.com/vimeo/psalm/issues/3035 */ $watcher->value = $signo; $watcher->data = $data; diff --git a/lib/Loop/EvDriver.php b/lib/Loop/EvDriver.php index 0e1dc89..557294c 100644 --- a/lib/Loop/EvDriver.php +++ b/lib/Loop/EvDriver.php @@ -157,6 +157,8 @@ class EvDriver extends Driver { $active = self::$activeSignals; + \assert($active !== null); + foreach ($active as $event) { $event->stop(); } @@ -230,10 +232,14 @@ class EvDriver extends Driver if (!isset($this->events[$id = $watcher->id])) { switch ($watcher->type) { case Watcher::READABLE: + \assert(\is_resource($watcher->value)); + $this->events[$id] = $this->handle->io($watcher->value, \Ev::READ, $this->ioCallback, $watcher); break; case Watcher::WRITABLE: + \assert(\is_resource($watcher->value)); + $this->events[$id] = $this->handle->io( $watcher->value, \Ev::WRITE, @@ -244,6 +250,8 @@ class EvDriver extends Driver case Watcher::DELAY: case Watcher::REPEAT: + \assert(\is_int($watcher->value)); + $interval = $watcher->value / self::MILLISEC_PER_SEC; $this->events[$id] = $this->handle->timer( $interval, @@ -254,6 +262,8 @@ class EvDriver extends Driver break; case Watcher::SIGNAL: + \assert(\is_int($watcher->value)); + $this->events[$id] = $this->handle->signal($watcher->value, $this->signalCallback, $watcher); break; @@ -267,6 +277,7 @@ class EvDriver extends Driver } if ($watcher->type === Watcher::SIGNAL) { + /** @psalm-suppress PropertyTypeCoercion */ $this->signals[$id] = $this->events[$id]; } } diff --git a/lib/Loop/EventDriver.php b/lib/Loop/EventDriver.php index 5171725..54adfff 100644 --- a/lib/Loop/EventDriver.php +++ b/lib/Loop/EventDriver.php @@ -53,6 +53,8 @@ class EventDriver extends Driver } $this->ioCallback = function ($resource, $what, Watcher $watcher) { + \assert(\is_resource($watcher->value)); + try { $result = ($watcher->callback)($watcher->id, $watcher->value, $watcher->data); @@ -73,6 +75,8 @@ class EventDriver extends Driver }; $this->timerCallback = function ($resource, $what, Watcher $watcher) { + \assert(\is_int($watcher->value)); + if ($watcher->type & Watcher::DELAY) { $this->cancel($watcher->id); } else { @@ -167,6 +171,8 @@ class EventDriver extends Driver { $active = self::$activeSignals; + \assert($active !== null); + foreach ($active as $event) { $event->del(); } @@ -244,6 +250,8 @@ class EventDriver extends Driver if (!isset($this->events[$id = $watcher->id])) { switch ($watcher->type) { case Watcher::READABLE: + \assert(\is_resource($watcher->value)); + $this->events[$id] = new \Event( $this->handle, $watcher->value, @@ -254,6 +262,8 @@ class EventDriver extends Driver break; case Watcher::WRITABLE: + \assert(\is_resource($watcher->value)); + $this->events[$id] = new \Event( $this->handle, $watcher->value, @@ -265,6 +275,8 @@ class EventDriver extends Driver case Watcher::DELAY: case Watcher::REPEAT: + \assert(\is_int($watcher->value)); + $this->events[$id] = new \Event( $this->handle, -1, @@ -275,6 +287,8 @@ class EventDriver extends Driver break; case Watcher::SIGNAL: + \assert(\is_int($watcher->value)); + $this->events[$id] = new \Event( $this->handle, $watcher->value, @@ -294,6 +308,8 @@ class EventDriver extends Driver switch ($watcher->type) { case Watcher::DELAY: case Watcher::REPEAT: + \assert(\is_int($watcher->value)); + $interval = $watcher->value - ($now - $this->now()); $this->events[$id]->add($interval > 0 ? $interval / self::MILLISEC_PER_SEC : 0); break; diff --git a/lib/Loop/Internal/TimerQueue.php b/lib/Loop/Internal/TimerQueue.php index cabc455..6ec63d0 100644 --- a/lib/Loop/Internal/TimerQueue.php +++ b/lib/Loop/Internal/TimerQueue.php @@ -21,6 +21,8 @@ final class TimerQueue * @param Watcher $watcher * @param int $expiration * + * @psalm-param Watcher $watcher + * * @return void */ public function insert(Watcher $watcher, int $expiration) @@ -32,6 +34,8 @@ final class TimerQueue $this->pointers[$watcher->id] = $node; while ($node !== 0 && $entry->expiration < $this->data[$parent = ($node - 1) >> 1]->expiration) { + \assert(isset($parent)); // see https://github.com/vimeo/psalm/issues/3034 + $temp = $this->data[$parent]; $this->data[$node] = $temp; $this->pointers[$temp->watcher->id] = $node; @@ -48,6 +52,8 @@ final class TimerQueue * * @param Watcher $watcher * + * @psalm-param Watcher $watcher + * * @return void */ public function remove(Watcher $watcher) @@ -68,6 +74,8 @@ final class TimerQueue * @param int $now Current loop time. * * @return Watcher|null Expired watcher at the top of the heap or null if the watcher has not expired. + * + * @psalm-return Watcher|null */ public function extract(int $now) { diff --git a/lib/Loop/NativeDriver.php b/lib/Loop/NativeDriver.php index 5be332b..c066019 100644 --- a/lib/Loop/NativeDriver.php +++ b/lib/Loop/NativeDriver.php @@ -217,6 +217,8 @@ class NativeDriver extends Driver } } + \assert(\is_array($write)); // See https://github.com/vimeo/psalm/issues/3036 + foreach ($write as $stream) { $streamId = (int) $stream; if (!isset($this->writeWatchers[$streamId])) { diff --git a/lib/Loop/Watcher.php b/lib/Loop/Watcher.php index 7a9002b..4bace62 100644 --- a/lib/Loop/Watcher.php +++ b/lib/Loop/Watcher.php @@ -4,6 +4,9 @@ namespace Amp\Loop; use Amp\Struct; +/** + * @template TValue as (int|resource|null) + */ class Watcher { use Struct; @@ -42,7 +45,8 @@ class Watcher /** * Watcher-dependent value storage. Stream for IO watchers, signal number for signal watchers, interval for timers. * - * @var resource|int + * @var resource|int|null + * @psalm-var TValue */ public $value; } diff --git a/psalm.xml b/psalm.xml index d6aa085..97b60ea 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,7 +1,7 @@