Skip to content

Commit 3a0a285

Browse files
authored
Merge pull request #94 from orklah/psalm-coverage
Psalm: Enable totallyTyped=true and bump type coverage to 100%
2 parents 05619ca + c0441d1 commit 3a0a285

File tree

5 files changed

+61
-20
lines changed

5 files changed

+61
-20
lines changed

phpstan.neon

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
parameters:
22
level: max
3-
checkGenericClassInNonGenericObjectType: false
4-
checkMissingIterableValueType: false
3+
4+
ignoreErrors:
5+
# Bug in PHPStan? The error is "expects A, A given"
6+
- '#Parameter \#1 \$tokens of method phpDocumentor\\Reflection\\Types\\ContextFactory::parse#'
7+
# ArrayIterator::current can return null if iterated even if ArrayIterator::valid isn't checked before
8+
- '#Strict comparison using === between array\(int, string, int\)\|string and false will always evaluate to false#'
9+
- '#Strict comparison using === between string and null will always evaluate to false#'
10+
- '#Unreachable statement - code above always terminates#'

psalm.xml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0"?>
22
<psalm
3-
totallyTyped="false"
3+
totallyTyped="true"
44
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
55
xmlns="https://getpsalm.org/schema/config"
66
xsi:schemaLocation="https://getpsalm.org/schema/config file:///composer/vendor/vimeo/psalm/config.xsd"
@@ -13,6 +13,20 @@
1313
</projectFiles>
1414

1515
<issueHandlers>
16-
<LessSpecificReturnType errorLevel="info" />
16+
<DocblockTypeContradiction>
17+
<errorLevel type="info">
18+
<!-- ArrayIterator::current can return null if iterated even if ArrayIterator::valid isn't checked before -->
19+
<file name="src/TypeResolver.php"/>
20+
<!-- Not sure what's going on. I don't think it's possible to have false here -->
21+
<file name="src/Types/ContextFactory.php"/>
22+
</errorLevel>
23+
</DocblockTypeContradiction>
24+
25+
<RedundantConditionGivenDocblockType>
26+
<errorLevel type="info">
27+
<!-- ArrayIterator::current can return null if iterated even if ArrayIterator::valid isn't checked before -->
28+
<file name="src/TypeResolver.php"/>
29+
</errorLevel>
30+
</RedundantConditionGivenDocblockType>
1731
</issueHandlers>
1832
</psalm>

src/TypeResolver.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ public function resolve(string $type, ?Context $context = null) : Type
151151
/**
152152
* Analyse each tokens and creates types
153153
*
154-
* @param ArrayIterator $tokens the iterator on tokens
155-
* @param int $parserContext on of self::PARSER_* constants, indicating
154+
* @param ArrayIterator<int, string> $tokens the iterator on tokens
155+
* @param int $parserContext on of self::PARSER_* constants, indicating
156156
* the context where we are in the parsing
157157
*/
158158
private function parseTypes(ArrayIterator $tokens, Context $context, int $parserContext) : Type
@@ -396,6 +396,8 @@ private function resolveTypedObject(string $type, ?Context $context = null) : Ob
396396

397397
/**
398398
* Resolves class string
399+
*
400+
* @param ArrayIterator<int, string> $tokens
399401
*/
400402
private function resolveClassString(ArrayIterator $tokens, Context $context) : Type
401403
{
@@ -427,6 +429,8 @@ private function resolveClassString(ArrayIterator $tokens, Context $context) : T
427429
/**
428430
* Resolves the collection values and keys
429431
*
432+
* @param ArrayIterator<int, string> $tokens
433+
*
430434
* @return Array_|Iterable_|Collection
431435
*/
432436
private function resolveCollection(ArrayIterator $tokens, Type $classType, Context $context) : Type

src/Types/Compound.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,20 @@
2424
* A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated
2525
* using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type
2626
* may contain a value with any of the given types.
27+
*
28+
* @template-implements IteratorAggregate<int, Type>
2729
*/
2830
final class Compound implements Type, IteratorAggregate
2931
{
30-
/** @var Type[] */
32+
/** @var array<int, Type> */
3133
private $types = [];
3234

3335
/**
3436
* Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface.
3537
*
3638
* @param Type[] $types
39+
*
40+
* @phpstan-param list<Type> $types
3741
*/
3842
public function __construct(array $types)
3943
{
@@ -86,9 +90,9 @@ public function __toString() : string
8690
}
8791

8892
/**
89-
* {@inheritdoc}
93+
* @return ArrayIterator<int, Type>
9094
*/
91-
public function getIterator()
95+
public function getIterator() : ArrayIterator
9296
{
9397
return new ArrayIterator($this->types);
9498
}

src/Types/ContextFactory.php

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ final class ContextFactory
6464
public function createFromReflector(Reflector $reflector) : Context
6565
{
6666
if ($reflector instanceof ReflectionClass) {
67+
/** @var ReflectionClass<object> $reflector */
6768
return $this->createFromReflectionClass($reflector);
6869
}
6970

@@ -90,6 +91,7 @@ private function createFromReflectionParameter(ReflectionParameter $parameter) :
9091
{
9192
$class = $parameter->getDeclaringClass();
9293
if ($class) {
94+
/** @var ReflectionClass<object> $class */
9395
return $this->createFromReflectionClass($class);
9496
}
9597

@@ -111,6 +113,9 @@ private function createFromReflectionClassConstant(ReflectionClassConstant $cons
111113
return $this->createFromReflectionClass($constant->getDeclaringClass());
112114
}
113115

116+
/**
117+
* @param ReflectionClass<object> $class
118+
*/
114119
private function createFromReflectionClass(ReflectionClass $class) : Context
115120
{
116121
$fileName = $class->getFileName();
@@ -190,6 +195,8 @@ public function createForNamespace(string $namespace, string $fileContents) : Co
190195

191196
/**
192197
* Deduce the name from tokens when we are at the T_NAMESPACE token.
198+
*
199+
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
193200
*/
194201
private function parseNamespace(ArrayIterator $tokens) : string
195202
{
@@ -209,6 +216,8 @@ private function parseNamespace(ArrayIterator $tokens) : string
209216
/**
210217
* Deduce the names of all imports when we are at the T_USE token.
211218
*
219+
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
220+
*
212221
* @return string[]
213222
*/
214223
private function parseUseStatement(ArrayIterator $tokens) : array
@@ -233,6 +242,8 @@ private function parseUseStatement(ArrayIterator $tokens) : array
233242

234243
/**
235244
* Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token.
245+
*
246+
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
236247
*/
237248
private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens) : void
238249
{
@@ -245,6 +256,8 @@ private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens) : v
245256
* Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of
246257
* a USE statement yet. This will return a key/value array of the alias => namespace.
247258
*
259+
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
260+
*
248261
* @return string[]
249262
*
250263
* @psalm-suppress TypeDoesNotContainType
@@ -266,7 +279,7 @@ private function extractUseStatements(ArrayIterator $tokens) : array
266279
switch ($tokenId) {
267280
case T_STRING:
268281
case T_NS_SEPARATOR:
269-
$currentNs .= $tokenValue;
282+
$currentNs .= (string) $tokenValue;
270283
$currentAlias = $tokenValue;
271284
break;
272285
case T_CURLY_OPEN:
@@ -304,17 +317,17 @@ private function extractUseStatements(ArrayIterator $tokens) : array
304317
switch ($tokenId) {
305318
case T_STRING:
306319
case T_NS_SEPARATOR:
307-
$currentNs .= $tokenValue;
320+
$currentNs .= (string) $tokenValue;
308321
$currentAlias = $tokenValue;
309322
break;
310323
case T_AS:
311324
$state = 'grouped-alias';
312325
break;
313326
case self::T_LITERAL_USE_SEPARATOR:
314-
$state = 'grouped';
315-
$extractedUseStatements[$currentAlias] = $currentNs;
316-
$currentNs = $groupedNs;
317-
$currentAlias = '';
327+
$state = 'grouped';
328+
$extractedUseStatements[(string) $currentAlias] = $currentNs;
329+
$currentNs = $groupedNs;
330+
$currentAlias = '';
318331
break;
319332
case self::T_LITERAL_END_OF_USE:
320333
$state = 'end';
@@ -330,10 +343,10 @@ private function extractUseStatements(ArrayIterator $tokens) : array
330343
$currentAlias = $tokenValue;
331344
break;
332345
case self::T_LITERAL_USE_SEPARATOR:
333-
$state = 'grouped';
334-
$extractedUseStatements[$currentAlias] = $currentNs;
335-
$currentNs = $groupedNs;
336-
$currentAlias = '';
346+
$state = 'grouped';
347+
$extractedUseStatements[(string) $currentAlias] = $currentNs;
348+
$currentNs = $groupedNs;
349+
$currentAlias = '';
337350
break;
338351
case self::T_LITERAL_END_OF_USE:
339352
$state = 'end';
@@ -351,7 +364,7 @@ private function extractUseStatements(ArrayIterator $tokens) : array
351364
}
352365

353366
if ($groupedNs !== $currentNs) {
354-
$extractedUseStatements[$currentAlias] = $currentNs;
367+
$extractedUseStatements[(string) $currentAlias] = $currentNs;
355368
}
356369

357370
return $extractedUseStatements;

0 commit comments

Comments
 (0)