Skip to content

Commit ce7ffaf

Browse files
committed
Bleeding edge - check missing types in LocalTypeAliasesCheck
1 parent 892b319 commit ce7ffaf

6 files changed

+90
-2
lines changed

conf/config.neon

+2
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,8 @@ services:
913913
class: PHPStan\Rules\Classes\LocalTypeAliasesCheck
914914
arguments:
915915
globalTypeAliases: %typeAliases%
916+
checkMissingTypehints: %checkMissingTypehints%
917+
absentTypeChecks: %featureToggles.absentTypeChecks%
916918

917919
-
918920
class: PHPStan\Rules\Comparison\ConstantConditionRuleHelper

src/Rules/Classes/LocalTypeAliasesCheck.php

+46
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
use PHPStan\Reflection\ClassReflection;
99
use PHPStan\Reflection\ReflectionProvider;
1010
use PHPStan\Rules\IdentifierRuleError;
11+
use PHPStan\Rules\MissingTypehintCheck;
1112
use PHPStan\Rules\RuleErrorBuilder;
1213
use PHPStan\Type\CircularTypeAliasErrorType;
1314
use PHPStan\Type\ErrorType;
1415
use PHPStan\Type\Generic\TemplateType;
1516
use PHPStan\Type\Type;
1617
use PHPStan\Type\TypeTraverser;
18+
use PHPStan\Type\VerbosityLevel;
1719
use function array_key_exists;
1820
use function in_array;
1921
use function sprintf;
@@ -28,6 +30,9 @@ public function __construct(
2830
private array $globalTypeAliases,
2931
private ReflectionProvider $reflectionProvider,
3032
private TypeNodeResolver $typeNodeResolver,
33+
private MissingTypehintCheck $missingTypehintCheck,
34+
private bool $checkMissingTypehints,
35+
private bool $absentTypeChecks,
3136
)
3237
{
3338
}
@@ -169,6 +174,47 @@ public function check(ClassReflection $reflection): array
169174

170175
return $traverse($type);
171176
});
177+
178+
if ($this->absentTypeChecks && !$foundError) {
179+
if ($this->checkMissingTypehints) {
180+
foreach ($this->missingTypehintCheck->getIterableTypesWithMissingValueTypehint($resolvedType) as $iterableType) {
181+
$iterableTypeDescription = $iterableType->describe(VerbosityLevel::typeOnly());
182+
$errors[] = RuleErrorBuilder::message(sprintf(
183+
'%s %s has type alias %s with no value type specified in iterable type %s.',
184+
$reflection->getClassTypeDescription(),
185+
$reflection->getDisplayName(),
186+
$aliasName,
187+
$iterableTypeDescription,
188+
))
189+
->tip(MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP)
190+
->identifier('missingType.iterableValue')
191+
->build();
192+
}
193+
194+
foreach ($this->missingTypehintCheck->getNonGenericObjectTypesWithGenericClass($resolvedType) as [$name, $genericTypeNames]) {
195+
$errors[] = RuleErrorBuilder::message(sprintf(
196+
'%s %s has type alias %s with generic %s but does not specify its types: %s',
197+
$reflection->getClassTypeDescription(),
198+
$reflection->getDisplayName(),
199+
$aliasName,
200+
$name,
201+
implode(', ', $genericTypeNames),
202+
))
203+
->identifier('missingType.generics')
204+
->build();
205+
}
206+
207+
foreach ($this->missingTypehintCheck->getCallablesWithMissingSignature($resolvedType) as $callableType) {
208+
$errors[] = RuleErrorBuilder::message(sprintf(
209+
'%s %s has type alias %s with no signature specified for %s.',
210+
$reflection->getClassTypeDescription(),
211+
$reflection->getDisplayName(),
212+
$aliasName,
213+
$callableType->describe(VerbosityLevel::typeOnly()),
214+
))->identifier('missingType.callable')->build();
215+
}
216+
}
217+
}
172218
}
173219

174220
return $errors;

tests/PHPStan/Rules/Classes/LocalTypeAliasesRuleTest.php

+17
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Rules\Classes;
44

55
use PHPStan\PhpDoc\TypeNodeResolver;
6+
use PHPStan\Rules\MissingTypehintCheck;
67
use PHPStan\Rules\Rule;
78
use PHPStan\Testing\RuleTestCase;
89
use const PHP_VERSION_ID;
@@ -20,6 +21,9 @@ protected function getRule(): Rule
2021
['GlobalTypeAlias' => 'int|string'],
2122
$this->createReflectionProvider(),
2223
self::getContainer()->getByType(TypeNodeResolver::class),
24+
new MissingTypehintCheck(true, true, true, true, []),
25+
true,
26+
true,
2327
),
2428
);
2529
}
@@ -91,6 +95,19 @@ public function testRule(): void
9195
'Invalid type definition detected in type alias InvalidTypeAlias.',
9296
62,
9397
],
98+
[
99+
'Class LocalTypeAliases\MissingTypehints has type alias NoIterableValue with no value type specified in iterable type array.',
100+
77,
101+
'See: https://phpstan.org/blog/solving-phpstan-no-value-type-specified-in-iterable-type',
102+
],
103+
[
104+
'Class LocalTypeAliases\MissingTypehints has type alias NoGenerics with generic class LocalTypeAliases\Generic but does not specify its types: T',
105+
77,
106+
],
107+
[
108+
'Class LocalTypeAliases\MissingTypehints has type alias NoCallable with no signature specified for callable.',
109+
77,
110+
],
94111
]);
95112
}
96113

tests/PHPStan/Rules/Classes/LocalTypeTraitAliasesRuleTest.php

+5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ public function testRule(): void
9191
'Invalid type definition detected in type alias InvalidTypeAlias.',
9292
62,
9393
],
94+
[
95+
'Trait LocalTypeTraitAliases\MissingType has type alias NoIterablueValue with no value type specified in iterable type array.',
96+
69,
97+
'See: https://phpstan.org/blog/solving-phpstan-no-value-type-specified-in-iterable-type',
98+
],
9499
]);
95100
}
96101

tests/PHPStan/Rules/Classes/data/local-type-aliases.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
class ExistingClassAlias {}
66

77
/**
8-
* @phpstan-type ExportedTypeAlias \Countable&\Traversable
8+
* @phpstan-type ExportedTypeAlias \Countable&\Traversable<int>
99
*/
1010
class Foo
1111
{
@@ -68,3 +68,13 @@ class InvalidTypeDefinitionToIgnoreBecauseItsAParseErrorAlreadyReportedInInvalid
6868
{
6969

7070
}
71+
72+
/**
73+
* @phpstan-type NoIterableValue = array
74+
* @phpstan-type NoGenerics = Generic
75+
* @phpstan-type NoCallable = array<callable>
76+
*/
77+
class MissingTypehints
78+
{
79+
80+
}

tests/PHPStan/Rules/Classes/data/local-type-trait-aliases.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
class ExistingClassAlias {}
66

77
/**
8-
* @phpstan-type ExportedTypeAlias \Countable&\Traversable
8+
* @phpstan-type ExportedTypeAlias \Countable&\Traversable<int>
99
*/
1010
trait Foo
1111
{
@@ -62,3 +62,11 @@ trait Generic
6262
trait Invalid
6363
{
6464
}
65+
66+
/**
67+
* @phpstan-type NoIterablueValue = array
68+
*/
69+
trait MissingType
70+
{
71+
72+
}

0 commit comments

Comments
 (0)