Skip to content

Commit

Permalink
Bleeding edge - check missing types in LocalTypeAliasesCheck
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 24, 2024
1 parent 892b319 commit ce7ffaf
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 2 deletions.
2 changes: 2 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@ services:
class: PHPStan\Rules\Classes\LocalTypeAliasesCheck
arguments:
globalTypeAliases: %typeAliases%
checkMissingTypehints: %checkMissingTypehints%
absentTypeChecks: %featureToggles.absentTypeChecks%

-
class: PHPStan\Rules\Comparison\ConstantConditionRuleHelper
Expand Down
46 changes: 46 additions & 0 deletions src/Rules/Classes/LocalTypeAliasesCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\MissingTypehintCheck;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\CircularTypeAliasErrorType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\VerbosityLevel;
use function array_key_exists;
use function in_array;
use function sprintf;
Expand All @@ -28,6 +30,9 @@ public function __construct(
private array $globalTypeAliases,
private ReflectionProvider $reflectionProvider,
private TypeNodeResolver $typeNodeResolver,
private MissingTypehintCheck $missingTypehintCheck,
private bool $checkMissingTypehints,
private bool $absentTypeChecks,
)
{
}
Expand Down Expand Up @@ -169,6 +174,47 @@ public function check(ClassReflection $reflection): array

return $traverse($type);
});

if ($this->absentTypeChecks && !$foundError) {
if ($this->checkMissingTypehints) {
foreach ($this->missingTypehintCheck->getIterableTypesWithMissingValueTypehint($resolvedType) as $iterableType) {
$iterableTypeDescription = $iterableType->describe(VerbosityLevel::typeOnly());
$errors[] = RuleErrorBuilder::message(sprintf(
'%s %s has type alias %s with no value type specified in iterable type %s.',
$reflection->getClassTypeDescription(),
$reflection->getDisplayName(),
$aliasName,
$iterableTypeDescription,
))
->tip(MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP)
->identifier('missingType.iterableValue')
->build();
}

foreach ($this->missingTypehintCheck->getNonGenericObjectTypesWithGenericClass($resolvedType) as [$name, $genericTypeNames]) {
$errors[] = RuleErrorBuilder::message(sprintf(
'%s %s has type alias %s with generic %s but does not specify its types: %s',
$reflection->getClassTypeDescription(),
$reflection->getDisplayName(),
$aliasName,
$name,
implode(', ', $genericTypeNames),
))
->identifier('missingType.generics')
->build();
}

foreach ($this->missingTypehintCheck->getCallablesWithMissingSignature($resolvedType) as $callableType) {
$errors[] = RuleErrorBuilder::message(sprintf(
'%s %s has type alias %s with no signature specified for %s.',
$reflection->getClassTypeDescription(),
$reflection->getDisplayName(),
$aliasName,
$callableType->describe(VerbosityLevel::typeOnly()),
))->identifier('missingType.callable')->build();
}
}
}
}

return $errors;
Expand Down
17 changes: 17 additions & 0 deletions tests/PHPStan/Rules/Classes/LocalTypeAliasesRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Rules\Classes;

use PHPStan\PhpDoc\TypeNodeResolver;
use PHPStan\Rules\MissingTypehintCheck;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use const PHP_VERSION_ID;
Expand All @@ -20,6 +21,9 @@ protected function getRule(): Rule
['GlobalTypeAlias' => 'int|string'],
$this->createReflectionProvider(),
self::getContainer()->getByType(TypeNodeResolver::class),
new MissingTypehintCheck(true, true, true, true, []),
true,
true,
),
);
}
Expand Down Expand Up @@ -91,6 +95,19 @@ public function testRule(): void
'Invalid type definition detected in type alias InvalidTypeAlias.',
62,
],
[
'Class LocalTypeAliases\MissingTypehints has type alias NoIterableValue with no value type specified in iterable type array.',
77,
'See: https://phpstan.org/blog/solving-phpstan-no-value-type-specified-in-iterable-type',
],
[
'Class LocalTypeAliases\MissingTypehints has type alias NoGenerics with generic class LocalTypeAliases\Generic but does not specify its types: T',
77,
],
[
'Class LocalTypeAliases\MissingTypehints has type alias NoCallable with no signature specified for callable.',
77,
],
]);
}

Expand Down
5 changes: 5 additions & 0 deletions tests/PHPStan/Rules/Classes/LocalTypeTraitAliasesRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public function testRule(): void
'Invalid type definition detected in type alias InvalidTypeAlias.',
62,
],
[
'Trait LocalTypeTraitAliases\MissingType has type alias NoIterablueValue with no value type specified in iterable type array.',
69,
'See: https://phpstan.org/blog/solving-phpstan-no-value-type-specified-in-iterable-type',
],
]);
}

Expand Down
12 changes: 11 additions & 1 deletion tests/PHPStan/Rules/Classes/data/local-type-aliases.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class ExistingClassAlias {}

/**
* @phpstan-type ExportedTypeAlias \Countable&\Traversable
* @phpstan-type ExportedTypeAlias \Countable&\Traversable<int>
*/
class Foo
{
Expand Down Expand Up @@ -68,3 +68,13 @@ class InvalidTypeDefinitionToIgnoreBecauseItsAParseErrorAlreadyReportedInInvalid
{

}

/**
* @phpstan-type NoIterableValue = array
* @phpstan-type NoGenerics = Generic
* @phpstan-type NoCallable = array<callable>
*/
class MissingTypehints
{

}
10 changes: 9 additions & 1 deletion tests/PHPStan/Rules/Classes/data/local-type-trait-aliases.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class ExistingClassAlias {}

/**
* @phpstan-type ExportedTypeAlias \Countable&\Traversable
* @phpstan-type ExportedTypeAlias \Countable&\Traversable<int>
*/
trait Foo
{
Expand Down Expand Up @@ -62,3 +62,11 @@ trait Generic
trait Invalid
{
}

/**
* @phpstan-type NoIterablueValue = array
*/
trait MissingType
{

}

0 comments on commit ce7ffaf

Please sign in to comment.