diff --git a/build/kint.phar b/build/kint.phar index 2ba6492aa..e9b21eb4e 100644 Binary files a/build/kint.phar and b/build/kint.phar differ diff --git a/src/CallFinder.php b/src/CallFinder.php index 8fbb802ae..111f45bd1 100644 --- a/src/CallFinder.php +++ b/src/CallFinder.php @@ -201,6 +201,7 @@ public static function getFunctionCalls(string $source, int $line, $function): a $class = null; /** * @psalm-suppress RedundantFunctionCallGivenDocblockType + * Psalm bug #11075 */ $function = \strtolower($function); } @@ -258,7 +259,10 @@ public static function getFunctionCalls(string $source, int $line, $function): a } // All self::$namespace tokens are T_ constants - /** @psalm-var PhpTokenArray $prev_tokens[0] */ + /** + * @psalm-var PhpTokenArray $prev_tokens[0] + * Psalm bug #746 + */ $ns = \explode('\\', \strtolower($prev_tokens[0][1])); if (\end($ns) !== $class) { @@ -375,8 +379,10 @@ public static function getFunctionCalls(string $source, int $line, $function): a continue; } + $formatted_parameters = []; + // Format the final output parameters - foreach ($params as &$param) { + foreach ($params as $param) { $name = self::tokensFormatted($param['short']); $expression = false; @@ -387,7 +393,7 @@ public static function getFunctionCalls(string $source, int $line, $function): a } } - $param = [ + $formatted_parameters[] = [ 'name' => self::tokensToString($name), 'path' => self::tokensToString(self::tokensTrim($param['full'])), 'expression' => $expression, @@ -395,8 +401,7 @@ public static function getFunctionCalls(string $source, int $line, $function): a } // Skip first-class callables - /** @psalm-var list $params */ - if (KINT_PHP81 && 1 === \count($params) && '...' === \reset($params)['path']) { + if (KINT_PHP81 && 1 === \count($formatted_parameters) && '...' === \reset($formatted_parameters)['path']) { continue; } @@ -429,7 +434,7 @@ public static function getFunctionCalls(string $source, int $line, $function): a } $function_calls[] = [ - 'parameters' => $params, + 'parameters' => $formatted_parameters, 'modifiers' => $mods, ]; } @@ -437,9 +442,6 @@ public static function getFunctionCalls(string $source, int $line, $function): a return $function_calls; } - /** - * @psalm-param PhpToken[] $tokens - */ private static function realTokenIndex(array $tokens, int $index): ?int { ++$index; @@ -467,9 +469,6 @@ private static function tokenIsOperator($token): bool return '...' !== $token && isset(self::$operator[$token[0]]); } - /** - * @psalm-param PhpToken[] $tokens - */ private static function tokensToString(array $tokens): string { $out = ''; @@ -485,9 +484,6 @@ private static function tokensToString(array $tokens): string return $out; } - /** - * @psalm-param PhpToken[] $tokens - */ private static function tokensTrim(array $tokens): array { foreach ($tokens as $index => $token) { @@ -511,11 +507,6 @@ private static function tokensTrim(array $tokens): array return \array_reverse($tokens); } - /** - * @psalm-param PhpToken[] $tokens - * - * @psalm-return PhpToken[] - */ private static function tokensFormatted(array $tokens): array { $tokens = self::tokensTrim($tokens); @@ -549,8 +540,10 @@ private static function tokensFormatted(array $tokens): array } $next = $tokens[$next]; - /** @psalm-var PhpToken $last */ - // Since we call tokensTrim we know we can't be here without a $last */ + /** + * @psalm-var PhpToken $last + * Since we call tokensTrim we know we can't be here without a $last + */ if ($attribute && ']' === $last[0]) { $attribute = false; } elseif (!$ignorestrip && isset(self::$strip[$last[0]]) && !self::tokenIsOperator($next)) { diff --git a/src/Kint.php b/src/Kint.php index bf4fbbd59..42da72173 100644 --- a/src/Kint.php +++ b/src/Kint.php @@ -37,6 +37,7 @@ /** * @psalm-consistent-constructor + * Psalm bug #8523 * * @psalm-type KintMode = Kint::MODE_*|bool */ @@ -141,7 +142,7 @@ class Kint implements FacadeInterface ]; /** - * @psalm-var class-string[] Array of modes to renderer class names + * @psalm-var class-string[] Array of modes to renderer class names */ public static array $renderers = [ self::MODE_RICH => Renderer\RichRenderer::class, @@ -151,7 +152,7 @@ class Kint implements FacadeInterface ]; /** - * @psalm-var class-string[] + * @psalm-var array> */ public static array $plugins = [ \Kint\Parser\ArrayLimitPlugin::class, @@ -339,7 +340,6 @@ public static function createFromStatics(array $statics): ?FacadeInterface return null; } - /** @psalm-var class-string[] $statics['renderers'] */ if (isset($statics['renderers'][$mode]) && \is_subclass_of($statics['renderers'][$mode], RendererInterface::class)) { $renderer = new $statics['renderers'][$mode](); } else { diff --git a/src/Parser/AbstractPlugin.php b/src/Parser/AbstractPlugin.php index f48617988..6ab031d0b 100644 --- a/src/Parser/AbstractPlugin.php +++ b/src/Parser/AbstractPlugin.php @@ -27,9 +27,6 @@ namespace Kint\Parser; -/** - * @psalm-consistent-constructor - */ abstract class AbstractPlugin implements ConstructablePluginInterface { private Parser $parser; diff --git a/src/Parser/ClassMethodsPlugin.php b/src/Parser/ClassMethodsPlugin.php index 2f6ffffe0..8d92e7609 100644 --- a/src/Parser/ClassMethodsPlugin.php +++ b/src/Parser/ClassMethodsPlugin.php @@ -35,7 +35,7 @@ use ReflectionClass; /** - * @psalm-type OwnedMethodValue MethodValue&object{owner_class: class-string} + * @psalm-type OwnedMethodValue = MethodValue&object{owner_class: class-string} */ class ClassMethodsPlugin extends AbstractPlugin { diff --git a/src/Parser/ClassStaticsPlugin.php b/src/Parser/ClassStaticsPlugin.php index ef04e1730..038f18ba7 100644 --- a/src/Parser/ClassStaticsPlugin.php +++ b/src/Parser/ClassStaticsPlugin.php @@ -36,7 +36,7 @@ use UnitEnum; /** - * @psalm-type OwnedValue Value&object{owner_class: class-string} + * @psalm-type OwnedValue = Value&object{owner_class: class-string} */ class ClassStaticsPlugin extends AbstractPlugin { diff --git a/src/Parser/IteratorPlugin.php b/src/Parser/IteratorPlugin.php index b0cb29674..d884ed9bd 100644 --- a/src/Parser/IteratorPlugin.php +++ b/src/Parser/IteratorPlugin.php @@ -45,7 +45,7 @@ class IteratorPlugin extends AbstractPlugin * when traversed. Others are just huge. Either way, put them in here * and you won't have to worry about them being parsed. * - * @psalm-var class-string[] + * @psalm-var class-string[] */ public static array $blacklist = [ DOMNamedNodeMap::class, @@ -72,6 +72,10 @@ public function parse(&$var, Value &$o, int $trigger): void } foreach (self::$blacklist as $class) { + /** + * @psalm-suppress RedundantCondition + * Psalm bug #11076 + */ if ($var instanceof $class) { $b = new Value($class.' Iterator Contents'); $b->depth = $o->depth + 1; @@ -103,7 +107,7 @@ public function parse(&$var, Value &$o, int $trigger): void /** * @psalm-var object{contents: array} $r->contents->value * Since we didn't get TRIGGER_DEPTH_LIMIT and set the iterator to the - * same depth we can guarantee at least 1 level deep will exist + * same depth we can assume at least 1 level deep will exist */ $r->contents = $this->getParser()->parse($data, $base_obj); $r->contents = $r->contents->value->contents; diff --git a/src/Parser/JsonPlugin.php b/src/Parser/JsonPlugin.php index 7d4752d55..67d32b9c8 100644 --- a/src/Parser/JsonPlugin.php +++ b/src/Parser/JsonPlugin.php @@ -65,10 +65,10 @@ public function parse(&$var, Value &$o, int $trigger): void } $r = new Representation('Json'); - /** @psalm-var object{contents: array} $r->contents->value */ $r->contents = $this->getParser()->parse($json, $base_obj); if (!\in_array('depth_limit', $r->contents->hints, true)) { + /** @psalm-var object{contents: array} $r->contents->value */ $r->contents = $r->contents->value->contents; } diff --git a/src/Parser/MysqliPlugin.php b/src/Parser/MysqliPlugin.php index a811951b4..581afa2d5 100644 --- a/src/Parser/MysqliPlugin.php +++ b/src/Parser/MysqliPlugin.php @@ -109,7 +109,6 @@ public function parse(&$var, Value &$o, int $trigger): void $parser = $this->getParser(); foreach ($o->value->contents as $key => $obj) { - /** @psalm-var string $obj->name */ if (isset(self::CONNECTED_READABLE[$obj->name])) { if (!$connected) { // No failed connections after PHP 8.1 diff --git a/src/Parser/ProxyPlugin.php b/src/Parser/ProxyPlugin.php index 682941cfa..a991ed8ec 100644 --- a/src/Parser/ProxyPlugin.php +++ b/src/Parser/ProxyPlugin.php @@ -37,7 +37,8 @@ class ProxyPlugin implements PluginInterface protected $callback; private ?Parser $parser = null; - public function __construct(array $types, int $triggers, callable $callback) + /** @psalm-param callable $callback */ + public function __construct(array $types, int $triggers, $callback) { $this->types = $types; $this->triggers = $triggers; diff --git a/src/Parser/SimpleXMLElementPlugin.php b/src/Parser/SimpleXMLElementPlugin.php index d3a2b774b..de9871680 100644 --- a/src/Parser/SimpleXMLElementPlugin.php +++ b/src/Parser/SimpleXMLElementPlugin.php @@ -97,8 +97,7 @@ public function parse(&$var, Value &$o, int $trigger): void $attribs = []; foreach ($namespaces as $nsAlias => $nsUrl) { - /** @psalm-suppress RiskyTruthyFalsyComparison */ - if ($nsAttribs = $var->attributes($nsUrl)) { + if ((bool) $nsAttribs = $var->attributes($nsUrl)) { $cleanAttribs = []; foreach ($nsAttribs as $name => $attrib) { $cleanAttribs[(string) $name] = $attrib; @@ -106,7 +105,7 @@ public function parse(&$var, Value &$o, int $trigger): void if (null === $nsUrl) { $obj = clone $base_obj; - if ($obj->access_path) { + if (null !== $obj->access_path) { $obj->access_path .= '->attributes()'; } @@ -114,7 +113,7 @@ public function parse(&$var, Value &$o, int $trigger): void $a->contents = \is_array($contents) ? $contents : []; } else { $obj = clone $base_obj; - if ($obj->access_path) { + if (null !== $obj->access_path) { $obj->access_path .= '->attributes('.\var_export($nsAlias, true).', true)'; } @@ -145,13 +144,12 @@ public function parse(&$var, Value &$o, int $trigger): void continue; } - /** @psalm-suppress RiskyTruthyFalsyComparison */ - if ($nsChildren = $var->children($nsUrl)) { + if ((bool) $nsChildren = $var->children($nsUrl)) { $nsap = []; foreach ($nsChildren as $name => $child) { $obj = new Value((string) $name); $obj->depth = $x->depth + 1; - if ($x->access_path) { + if (null !== $x->access_path) { if (null === $nsUrl) { $obj->access_path = $x->access_path.'->children()->'; } else { @@ -174,7 +172,7 @@ public function parse(&$var, Value &$o, int $trigger): void $value = $parser->parse($child, $obj); - if ($value->access_path && 'string' === $value->type) { + if (null !== $value->access_path && 'string' === $value->type) { $value->access_path = '(string) '.$value->access_path; } diff --git a/src/Parser/StreamPlugin.php b/src/Parser/StreamPlugin.php index 5c6af17d9..3cd444e5d 100644 --- a/src/Parser/StreamPlugin.php +++ b/src/Parser/StreamPlugin.php @@ -75,7 +75,7 @@ public function parse(&$var, Value &$o, int $trigger): void /** * @psalm-var object{contents: array} $rep->contents->value - * We check the depth and can guarantee at least 1 level deep will exist + * We checked the depth and can guarantee at least 1 level deep will exist */ $rep->contents = $parser->parse($meta, $base_obj); $rep->contents = $rep->contents->value->contents; diff --git a/src/Parser/TimestampPlugin.php b/src/Parser/TimestampPlugin.php index b5d9e23ba..8dbc86805 100644 --- a/src/Parser/TimestampPlugin.php +++ b/src/Parser/TimestampPlugin.php @@ -63,6 +63,10 @@ public function parse(&$var, Value &$o, int $trigger): void return; } + if (!$o->value instanceof Representation) { + return; + } + $len = \strlen((string) $var); // Guess for anything between March 1973 and November 2286 @@ -71,7 +75,6 @@ public function parse(&$var, Value &$o, int $trigger): void // Additionally it's highly unlikely the shortValue will be clipped for length // If you're writing a plugin that interferes with this, just put your // parser plugin further down the list so that it gets loaded afterwards. - /** @psalm-var Representation $o->value */ $o->value->label = 'Timestamp'; $o->value->hints[] = 'timestamp'; } diff --git a/src/Parser/TracePlugin.php b/src/Parser/TracePlugin.php index 3c5ed2343..8f00398d4 100644 --- a/src/Parser/TracePlugin.php +++ b/src/Parser/TracePlugin.php @@ -78,7 +78,6 @@ public function parse(&$var, Value &$o, int $trigger): void $rep->contents = []; foreach ($old_trace as $frame) { - /** @psalm-var int $index */ $index = $frame->name; if (!isset($trace[$index]['function'])) { @@ -86,7 +85,6 @@ public function parse(&$var, Value &$o, int $trigger): void continue; } - /** @psalm-var array $trace[$index] */ if (Utils::traceFrameIsListed($trace[$index], self::$blacklist)) { continue; } diff --git a/src/Parser/XmlPlugin.php b/src/Parser/XmlPlugin.php index f23eabab0..9e8a18254 100644 --- a/src/Parser/XmlPlugin.php +++ b/src/Parser/XmlPlugin.php @@ -114,8 +114,6 @@ protected static function xmlToSimpleXML(string $var, ?string $parent_path): ?ar * * If it errors loading then we wouldn't have gotten this far in the first place. * - * @psalm-assert non-empty-string $var - * * @param ?string $parent_path The path to the parent, in this case the XML string * * @return ?array The root element DOMNode, the access path, and the root element name diff --git a/src/Renderer/AbstractRenderer.php b/src/Renderer/AbstractRenderer.php index eb6c59be9..453bc4669 100644 --- a/src/Renderer/AbstractRenderer.php +++ b/src/Renderer/AbstractRenderer.php @@ -31,9 +31,10 @@ use Kint\Zval\Value; /** - * @psalm-type PluginMap array + * @psalm-type PluginMap = array * - * @psalm-consistent-constructor + * I'd like to have PluginMap but can't: + * Psalm bug #4308 */ abstract class AbstractRenderer implements RendererInterface { diff --git a/src/Renderer/Rich/AbstractPlugin.php b/src/Renderer/Rich/AbstractPlugin.php index 714cff7e9..c24855da1 100644 --- a/src/Renderer/Rich/AbstractPlugin.php +++ b/src/Renderer/Rich/AbstractPlugin.php @@ -31,9 +31,6 @@ use Kint\Zval\InstanceValue; use Kint\Zval\Value; -/** - * @psalm-consistent-constructor - */ abstract class AbstractPlugin implements PluginInterface { protected RichRenderer $renderer; diff --git a/src/Renderer/Rich/SimpleXMLElementPlugin.php b/src/Renderer/Rich/SimpleXMLElementPlugin.php index 91e2921f9..e1dbbee74 100644 --- a/src/Renderer/Rich/SimpleXMLElementPlugin.php +++ b/src/Renderer/Rich/SimpleXMLElementPlugin.php @@ -39,8 +39,7 @@ public function renderValue(Value $o): ?string return null; } - /** @psalm-suppress RiskyTruthyFalsyComparison */ - if (!$o->is_string_value || !empty($o->getRepresentation('attributes')->contents)) { + if (!$o->is_string_value || (bool) ($o->getRepresentation('attributes')->contents ?? false)) { return null; } diff --git a/src/Renderer/Rich/TablePlugin.php b/src/Renderer/Rich/TablePlugin.php index 49ea38885..e33aa858c 100644 --- a/src/Renderer/Rich/TablePlugin.php +++ b/src/Renderer/Rich/TablePlugin.php @@ -29,6 +29,7 @@ use Kint\Renderer\RichRenderer; use Kint\Utils; +use Kint\Zval\BlobValue; use Kint\Zval\InstanceValue; use Kint\Zval\Representation\Representation; use Kint\Zval\Value; @@ -51,8 +52,10 @@ public function renderTab(Representation $r): ?string $out = '
';
 
-        /** @psalm-suppress PossiblyNullIterator
-         * Psalm bug #11055 */
+        /**
+         * @psalm-suppress PossiblyNullIterator
+         * Psalm bug #11055
+         */
         foreach ($firstrow->value->contents as $field) {
             $out .= '';
         }
@@ -94,15 +97,25 @@ public function renderTab(Representation $r): ?string
                         break;
                     case 'integer':
                     case 'double':
-                        /** @psalm-var int|double|null $field->value->contents */
-                        $out .= (string) ($field->value->contents ?? '');
+                        if (\is_numeric($field->value->contents ?? null)) {
+                            /**
+                             * @psalm-var string|int|double|null $field->value->contents
+                             * Psalm bug #11055
+                             */
+                            $out .= (string) $field->value->contents;
+                        } else {
+                            $out .= ''.$type.'';
+                        }
                         break;
                     case 'null':
                         $out .= ''.$ref.'null';
                         break;
                     case 'string':
-                        /** @psalm-var object{value: object{contents: string}} $field */
-                        if ($field->encoding) {
+                        if ($field instanceof BlobValue && false !== $field->encoding && \is_string($field->value->contents ?? null)) {
+                            /**
+                             * @psalm-var string $val
+                             * Psalm bug #11055
+                             */
                             $val = $field->value->contents;
                             if (RichRenderer::$strlen_max && self::$respect_str_length) {
                                 $val = Utils::truncateString($val, RichRenderer::$strlen_max);
@@ -117,8 +130,11 @@ public function renderTab(Representation $r): ?string
                         $out .= ''.$ref.'array'.$size;
                         break;
                     case 'object':
-                        /** @psalm-var InstanceValue $field */
-                        $out .= ''.$ref.$this->renderer->escape($field->classname).''.$size;
+                        if ($field instanceof InstanceValue) {
+                            $out .= ''.$ref.$this->renderer->escape($field->classname).''.$size;
+                        } else {
+                            $out .= ''.$type.'';
+                        }
                         break;
                     case 'resource':
                         $out .= ''.$ref.'resource';
diff --git a/src/Renderer/RichRenderer.php b/src/Renderer/RichRenderer.php
index 26647adb0..310ad02fe 100644
--- a/src/Renderer/RichRenderer.php
+++ b/src/Renderer/RichRenderer.php
@@ -46,7 +46,7 @@ class RichRenderer extends AbstractRenderer
     /**
      * RichRenderer value plugins should implement ValuePluginInterface.
      *
-     * @psalm-var PluginMap
+     * @psalm-var class-string[]
      */
     public static array $value_plugins = [
         'array_limit' => Rich\ArrayLimitPlugin::class,
@@ -63,7 +63,7 @@ class RichRenderer extends AbstractRenderer
     /**
      * RichRenderer tab plugins should implement TabPluginInterface.
      *
-     * @psalm-var PluginMap
+     * @psalm-var class-string[]
      */
     public static array $tab_plugins = [
         'binary' => Rich\BinaryPlugin::class,
diff --git a/src/Renderer/Text/AbstractPlugin.php b/src/Renderer/Text/AbstractPlugin.php
index f31b0d27a..9308abbc5 100644
--- a/src/Renderer/Text/AbstractPlugin.php
+++ b/src/Renderer/Text/AbstractPlugin.php
@@ -30,9 +30,6 @@
 use Kint\Renderer\TextRenderer;
 use Kint\Zval\Value;
 
-/**
- * @psalm-consistent-constructor
- */
 abstract class AbstractPlugin implements PluginInterface
 {
     protected TextRenderer $renderer;
diff --git a/src/Renderer/Text/TracePlugin.php b/src/Renderer/Text/TracePlugin.php
index b9679e7af..20afb4d15 100644
--- a/src/Renderer/Text/TracePlugin.php
+++ b/src/Renderer/Text/TracePlugin.php
@@ -45,14 +45,17 @@ public function render(Value $o): string
 
         $out .= $this->renderer->renderHeader($o).':'.PHP_EOL;
 
-        if (!$o instanceof TraceValue || !isset($o->value->contents) || !\is_array($o->value->contents)) {
+        if (!$o instanceof TraceValue || !\is_array($o->value->contents ?? null)) {
             return $out;
         }
 
         $indent = \str_repeat(' ', ($o->depth + 1) * $this->renderer->indent_width);
 
         $i = 1;
-        /** @psalm-var TraceFrameValue[] $o->value->contents */
+        /**
+         * @psalm-var object{value: object{contents: TraceFrameValue[]}} $o
+         * Psalm bug #11055
+         */
         foreach ($o->value->contents as $frame) {
             $framedesc = $indent.\str_pad($i.': ', 4, ' ');
 
diff --git a/src/Renderer/TextRenderer.php b/src/Renderer/TextRenderer.php
index 941a29400..62b14d9c0 100644
--- a/src/Renderer/TextRenderer.php
+++ b/src/Renderer/TextRenderer.php
@@ -29,6 +29,7 @@
 
 use Kint\Kint;
 use Kint\Parser;
+use Kint\Parser\PluginInterface as ParserPluginInterface;
 use Kint\Renderer\Text\PluginInterface;
 use Kint\Utils;
 use Kint\Zval\InstanceValue;
@@ -43,7 +44,7 @@ class TextRenderer extends AbstractRenderer
     /**
      * TextRenderer plugins should implement PluginInterface.
      *
-     * @psalm-var PluginMap
+     * @psalm-var class-string[]
      */
     public static array $plugins = [
         'array_limit' => Text\ArrayLimitPlugin::class,
@@ -59,7 +60,7 @@ class TextRenderer extends AbstractRenderer
      * Parser plugins must be instanceof one of these or
      * it will be removed for performance reasons.
      *
-     * @psalm-var class-string[]
+     * @psalm-var class-string[]
      */
     public static array $parser_plugin_whitelist = [
         Parser\ArrayLimitPlugin::class,
@@ -232,8 +233,10 @@ public function renderChildren(Value $o): string
         $children = '';
 
         if ($o->value && \is_array($o->value->contents)) {
-            /** @psalm-suppress PossiblyNullReference
-             * Psalm bug #11052 */
+            /**
+             * @psalm-suppress PossiblyNullReference
+             * Psalm bug #11052
+             */
             if ($o instanceof InstanceValue && 'properties' === $o->value->getName()) {
                 foreach (self::sortProperties($o->value->contents, self::$sort) as $obj) {
                     $children .= $this->render($obj);
diff --git a/src/Utils.php b/src/Utils.php
index 2f73beb8c..96597fbd3 100644
--- a/src/Utils.php
+++ b/src/Utils.php
@@ -36,6 +36,16 @@
  * A collection of utility methods. Should all be static methods with no dependencies.
  *
  * @psalm-import-type Encoding from BlobValue
+ *
+ * @psalm-type TraceFrame = array{
+ *   function: string,
+ *   line?: int,
+ *   file?: string,
+ *   class?: string,
+ *   object?: object,
+ *   type?: string,
+ *   args?: array
+ * }
  */
 final class Utils
 {
@@ -167,6 +177,9 @@ public static function composerSkipFlags(): void
         }
     }
 
+    /**
+     * @psalm-assert-if-true list $trace
+     */
     public static function isTrace(array $trace): bool
     {
         if (!self::isSequential($trace)) {
@@ -208,6 +221,7 @@ public static function isTrace(array $trace): bool
         return $file_found;
     }
 
+    /** @psalm-param TraceFrame $frame */
     public static function traceFrameIsListed(array $frame, array $matches): bool
     {
         if (isset($frame['class'])) {
diff --git a/src/Zval/BlobValue.php b/src/Zval/BlobValue.php
index e9a563b36..f28893965 100644
--- a/src/Zval/BlobValue.php
+++ b/src/Zval/BlobValue.php
@@ -28,7 +28,7 @@
 namespace Kint\Zval;
 
 /**
- * @psalm-type Encoding string|false
+ * @psalm-type Encoding = string|false
  */
 class BlobValue extends Value
 {
diff --git a/src/Zval/ClosureValue.php b/src/Zval/ClosureValue.php
index c3b2b3afa..3d8805883 100644
--- a/src/Zval/ClosureValue.php
+++ b/src/Zval/ClosureValue.php
@@ -27,9 +27,6 @@
 
 namespace Kint\Zval;
 
-/**
- * @psalm-import-type ValueName from Value
- */
 class ClosureValue extends InstanceValue
 {
     use ParameterHoldingTrait;
diff --git a/src/Zval/MethodValue.php b/src/Zval/MethodValue.php
index d4a4b65aa..52440e6b1 100644
--- a/src/Zval/MethodValue.php
+++ b/src/Zval/MethodValue.php
@@ -196,7 +196,7 @@ public function getModifiers(): ?string
         }
 
         if (null !== $out) {
-            /** @psalm-var truthy-string ltrim($out) */
+            /** @psalm-var non-empty-string rtrim($out) */
             return \rtrim($out);
         }
 
diff --git a/src/Zval/Representation/ColorRepresentation.php b/src/Zval/Representation/ColorRepresentation.php
index 711dcfb77..7ebf525d9 100644
--- a/src/Zval/Representation/ColorRepresentation.php
+++ b/src/Zval/Representation/ColorRepresentation.php
@@ -431,7 +431,10 @@ protected function setValuesFromFunction(string $value): int
             }
         }
 
-        /** @psalm-var non-empty-array $params Psalm bug workaround */
+        /**
+         * @psalm-var non-empty-array $params
+         * Psalm bug #746
+         */
         switch ($variant) {
             case self::COLOR_RGBA:
             case self::COLOR_RGB:
diff --git a/src/Zval/TraceFrameValue.php b/src/Zval/TraceFrameValue.php
index cd65b78c4..758d8bdaa 100644
--- a/src/Zval/TraceFrameValue.php
+++ b/src/Zval/TraceFrameValue.php
@@ -67,10 +67,21 @@ public function __construct(Value $base, array $raw_frame)
 
         foreach ($this->value->contents as $frame_prop) {
             if ('object' === $frame_prop->name) {
+                if (!$frame_prop instanceof InstanceValue) {
+                    throw new InvalidArgumentException('object key of TraceFrameValue must be parsed to InstanceValue');
+                }
                 $this->trace['object'] = $frame_prop;
             }
+
             if ('args' === $frame_prop->name) {
-                /** @psalm-var ParameterValue[] $frame_prop->value->contents */
+                if ('array' !== $frame_prop->type || !\is_array($frame_prop->value->contents ?? null)) {
+                    throw new InvalidArgumentException('args key of TraceFrameValue must be parsed to array Value');
+                }
+
+                /**
+                 * @psalm-var array $frame_prop->value->contents
+                 * Psalm bug #11052
+                 */
                 $this->trace['args'] = $frame_prop->value->contents;
 
                 if ($this->trace['function'] instanceof MethodValue) {
@@ -96,7 +107,6 @@ public function __construct(Value $base, array $raw_frame)
         }
 
         if (null !== $this->trace['object']) {
-            /** @psalm-var InstanceValue $this->trace['object'] */
             $callee = new Representation('object');
             $callee->label = 'Callee object ['.$this->trace['object']->classname.']';
             $callee->contents = $this->trace['object'];
diff --git a/src/Zval/Value.php b/src/Zval/Value.php
index 49ab677fa..95b8886e8 100644
--- a/src/Zval/Value.php
+++ b/src/Zval/Value.php
@@ -63,7 +63,7 @@ class Value
     public array $hints = [];
     public ?Representation $value = null;
 
-    /** @var Representation[] */
+    /** @psalm-var Representation[] */
     protected array $representations = [];
 
     /** @psalm-param ValueName $name */
@@ -207,12 +207,10 @@ public function getValueShort(): ?string
     {
         if ($rep = $this->value) {
             if ('boolean' === $this->type) {
-                /** @psalm-var bool $rep->contents */
-                return $rep->contents ? 'true' : 'false';
+                return ((bool) $rep->contents) ? 'true' : 'false';
             }
 
-            if ('integer' === $this->type || 'double' === $this->type) {
-                /** @psalm-var int|double $rep->contents */
+            if (('integer' === $this->type || 'double' === $this->type) && \is_numeric($rep->contents)) {
                 return (string) $rep->contents;
             }
         }
'.$this->renderer->escape($field->getName()).'