Skip to content

Commit

Permalink
Merge branch refs/heads/1.10.x into 1.11.x
Browse files Browse the repository at this point in the history
  • Loading branch information
phpstan-bot committed Jul 22, 2023
2 parents 86292b0 + 08e4464 commit abac6e1
Show file tree
Hide file tree
Showing 41 changed files with 445 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/Rules/Classes/MixinRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public function processNode(Node $node, Scope $scope): array
'Generic type %s in PHPDoc tag @mixin does not specify all template types of %s %s: %s',
'Generic type %s in PHPDoc tag @mixin specifies %d template types, but %s %s supports only %d: %s',
'Type %s in generic type %s in PHPDoc tag @mixin is not subtype of template type %s of %s %s.',
'Call-site variance of %s in generic type %s in PHPDoc tag @mixin is in conflict with %s template type %s of %s %s.',
'Call-site variance of %s in generic type %s in PHPDoc tag @mixin is redundant, template type %s of %s %s has the same variance.',
));

foreach ($this->missingTypehintCheck->getNonGenericObjectTypesWithGenericClass($type) as [$innerName, $genericTypeNames]) {
Expand Down
2 changes: 2 additions & 0 deletions src/Rules/Generics/ClassAncestorsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public function processNode(Node $node, Scope $scope): array
'Generic type %s in PHPDoc tag @extends does not specify all template types of %s %s: %s',
'Generic type %s in PHPDoc tag @extends specifies %d template types, but %s %s supports only %d: %s',
'Type %s in generic type %s in PHPDoc tag @extends is not subtype of template type %s of %s %s.',
'Call-site variance annotation of %s in generic type %s in PHPDoc tag @extends is not allowed.',
'PHPDoc tag @extends has invalid type %s.',
sprintf('Class %s extends generic class %%s but does not specify its types: %%s', $escapedClassName),
sprintf('in extended type %%s of class %s', $escapedClassName),
Expand All @@ -70,6 +71,7 @@ public function processNode(Node $node, Scope $scope): array
'Generic type %s in PHPDoc tag @implements does not specify all template types of %s %s: %s',
'Generic type %s in PHPDoc tag @implements specifies %d template types, but %s %s supports only %d: %s',
'Type %s in generic type %s in PHPDoc tag @implements is not subtype of template type %s of %s %s.',
'Call-site variance annotation of %s in generic type %s in PHPDoc tag @implements is not allowed.',
'PHPDoc tag @implements has invalid type %s.',
sprintf('Class %s implements generic interface %%s but does not specify its types: %%s', $escapedClassName),
sprintf('in implemented type %%s of class %s', $escapedClassName),
Expand Down
2 changes: 2 additions & 0 deletions src/Rules/Generics/EnumAncestorsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public function processNode(Node $node, Scope $scope): array
'',
'',
'',
'',
);

$implementsErrors = $this->genericAncestorsCheck->check(
Expand All @@ -68,6 +69,7 @@ public function processNode(Node $node, Scope $scope): array
'Generic type %s in PHPDoc tag @implements does not specify all template types of %s %s: %s',
'Generic type %s in PHPDoc tag @implements specifies %d template types, but %s %s supports only %d: %s',
'Type %s in generic type %s in PHPDoc tag @implements is not subtype of template type %s of %s %s.',
'Call-site variance annotation of %s in generic type %s in PHPDoc tag @implements is not allowed.',
'PHPDoc tag @implements has invalid type %s.',
sprintf('Enum %s implements generic interface %%s but does not specify its types: %%s', $escapedEnumName),
sprintf('in implemented type %%s of enum %s', $escapedEnumName),
Expand Down
16 changes: 16 additions & 0 deletions src/Rules/Generics/GenericAncestorsCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Generic\TemplateTypeVariance;
use PHPStan\Type\Generic\TypeProjectionHelper;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
use function array_fill_keys;
Expand Down Expand Up @@ -52,6 +53,7 @@ public function check(
string $notEnoughTypesMessage,
string $extraTypesMessage,
string $typeIsNotSubtypeMessage,
string $typeProjectionIsNotAllowedMessage,
string $invalidTypeMessage,
string $genericClassInNonGenericObjectType,
string $invalidVarianceMessage,
Expand Down Expand Up @@ -93,6 +95,8 @@ public function check(
$notEnoughTypesMessage,
$extraTypesMessage,
$typeIsNotSubtypeMessage,
'',
'',
);
$messages = array_merge($messages, $genericObjectTypeCheckMessages);

Expand All @@ -114,6 +118,18 @@ public function check(
foreach ($this->varianceCheck->check($variance, $ancestorType, $messageContext) as $message) {
$messages[] = $message;
}

foreach ($ancestorType->getVariances() as $index => $typeVariance) {
if ($typeVariance->invariant()) {
continue;
}

$messages[] = RuleErrorBuilder::message(sprintf(
$typeProjectionIsNotAllowedMessage,
TypeProjectionHelper::describe($ancestorType->getTypes()[$index], $typeVariance, VerbosityLevel::typeOnly()),
$ancestorType->describe(VerbosityLevel::typeOnly()),
))->identifier('generics.callSiteVarianceNotAllowed')->build();
}
}

if ($this->checkGenericClassInNonGenericObjectType) {
Expand Down
35 changes: 34 additions & 1 deletion src/Rules/Generics/GenericObjectTypeCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeVariance;
use PHPStan\Type\Generic\TypeProjectionHelper;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\VerbosityLevel;
Expand All @@ -30,6 +32,8 @@ public function check(
string $notEnoughTypesMessage,
string $extraTypesMessage,
string $typeIsNotSubtypeMessage,
string $typeProjectionHasConflictingVarianceMessage,
string $typeProjectionIsRedundantMessage,
): array
{
$genericTypes = $this->getGenericTypes($phpDocType);
Expand All @@ -51,6 +55,7 @@ public function check(
$templateTypes = array_values($classReflection->getTemplateTypeMap()->getTypes());

$genericTypeTypes = $genericType->getTypes();
$genericTypeVariances = $genericType->getVariances();
$templateTypesCount = count($templateTypes);
$genericTypeTypesCount = count($genericTypeTypes);
if ($templateTypesCount > $genericTypeTypesCount) {
Expand Down Expand Up @@ -80,8 +85,36 @@ public function check(
}

$templateType = $templateTypes[$i];
$boundType = TemplateTypeHelper::resolveToBounds($templateType);
$genericTypeType = $genericTypeTypes[$i];

$genericTypeVariance = $genericTypeVariances[$i] ?? TemplateTypeVariance::createInvariant();
if ($templateType instanceof TemplateType && !$genericTypeVariance->invariant()) {
if ($genericTypeVariance->equals($templateType->getVariance())) {
$messages[] = RuleErrorBuilder::message(sprintf(
$typeProjectionIsRedundantMessage,
TypeProjectionHelper::describe($genericTypeType, $genericTypeVariance, VerbosityLevel::typeOnly()),
$genericType->describe(VerbosityLevel::typeOnly()),
$templateType->describe(VerbosityLevel::typeOnly()),
$classLikeDescription,
$classReflection->getDisplayName(false),
))
->identifier('generics.callSiteVarianceRedundant')
->tip('You can safely remove the call-site variance annotation.')
->build();
} elseif (!$genericTypeVariance->validPosition($templateType->getVariance())) {
$messages[] = RuleErrorBuilder::message(sprintf(
$typeProjectionHasConflictingVarianceMessage,
TypeProjectionHelper::describe($genericTypeType, $genericTypeVariance, VerbosityLevel::typeOnly()),
$genericType->describe(VerbosityLevel::typeOnly()),
$templateType->getVariance()->describe(),
$templateType->describe(VerbosityLevel::typeOnly()),
$classLikeDescription,
$classReflection->getDisplayName(false),
))->identifier('generics.callSiteVarianceConflict')->build();
}
}

$boundType = TemplateTypeHelper::resolveToBounds($templateType);
if ($boundType->isSuperTypeOf($genericTypeType)->yes()) {
if (!$templateType instanceof TemplateType) {
continue;
Expand Down
2 changes: 2 additions & 0 deletions src/Rules/Generics/InterfaceAncestorsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function processNode(Node $node, Scope $scope): array
'Generic type %s in PHPDoc tag @extends does not specify all template types of %s %s: %s',
'Generic type %s in PHPDoc tag @extends specifies %d template types, but %s %s supports only %d: %s',
'Type %s in generic type %s in PHPDoc tag @extends is not subtype of template type %s of %s %s.',
'Call-site variance annotation of %s in generic type %s in PHPDoc tag @extends is not allowed.',
'PHPDoc tag @extends has invalid type %s.',
sprintf('Interface %s extends generic interface %%s but does not specify its types: %%s', $escapedInterfaceName),
sprintf('in extended type %%s of interface %s', $escapedInterfaceName),
Expand All @@ -71,6 +72,7 @@ public function processNode(Node $node, Scope $scope): array
'',
'',
'',
'',
);

foreach ($this->crossCheckInterfacesHelper->check($classReflection) as $error) {
Expand Down
2 changes: 2 additions & 0 deletions src/Rules/Generics/TemplateTypeCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ public function check(
sprintf('PHPDoc tag @template %s bound has type %%s which does not specify all template types of %%s %%s: %%s', $escapedTemplateTagName),
sprintf('PHPDoc tag @template %s bound has type %%s which specifies %%d template types, but %%s %%s supports only %%d: %%s', $escapedTemplateTagName),
sprintf('Type %%s in generic type %%s in PHPDoc tag @template %s is not subtype of template type %%s of %%s %%s.', $escapedTemplateTagName),
sprintf('Call-site variance of %%s in generic type %%s in PHPDoc tag @template %s is in conflict with %%s template type %%s of %%s %%s.', $escapedTemplateTagName),
sprintf('Call-site variance of %%s in generic type %%s in PHPDoc tag @template %s is redundant, template type %%s of %%s %%s has the same variance.', $escapedTemplateTagName),
);
foreach ($genericObjectErrors as $genericObjectError) {
$messages[] = $genericObjectError;
Expand Down
1 change: 1 addition & 0 deletions src/Rules/Generics/UsedTraitsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public function processNode(Node $node, Scope $scope): array
'Generic type %s in PHPDoc tag @use does not specify all template types of %s %s: %s',
'Generic type %s in PHPDoc tag @use specifies %d template types, but %s %s supports only %d: %s',
'Type %s in generic type %s in PHPDoc tag @use is not subtype of template type %s of %s %s.',
'Call-site variance annotation of %s in generic type %s in PHPDoc tag @use is not allowed.',
'PHPDoc tag @use has invalid type %s.',
sprintf('%s uses generic trait %%s but does not specify its types: %%s', ucfirst($description)),
sprintf('in used type %%s of %s', $description),
Expand Down
10 changes: 10 additions & 0 deletions src/Rules/PhpDoc/IncompatibleClassConstantPhpDocTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ private function processSingleConstant(ClassReflection $classReflection, string
$className,
$escapedConstantName,
),
sprintf(
'Call-site variance of %%s in generic type %%s in PHPDoc tag @var for constant %s::%s is in conflict with %%s template type %%s of %%s %%s.',
$className,
$escapedConstantName,
),
sprintf(
'Call-site variance of %%s in generic type %%s in PHPDoc tag @var for constant %s::%s is redundant, template type %%s of %%s %%s has the same variance.',
$className,
$escapedConstantName,
),
));
}

Expand Down
12 changes: 12 additions & 0 deletions src/Rules/PhpDoc/IncompatiblePhpDocTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ public function processNode(Node $node, Scope $scope): array
$escapedTagName,
$escapedParameterName,
),
sprintf(
'Call-site variance of %%s in generic type %%s in PHPDoc tag %s for parameter $%s is in conflict with %%s template type %%s of %%s %%s.',
$escapedTagName,
$escapedParameterName,
),
sprintf(
'Call-site variance of %%s in generic type %%s in PHPDoc tag %s for parameter $%s is redundant, template type %%s of %%s %%s has the same variance.',
$escapedTagName,
$escapedParameterName,
),
));

if ($phpDocParamTag instanceof ParamOutTag) {
Expand Down Expand Up @@ -183,6 +193,8 @@ public function processNode(Node $node, Scope $scope): array
'Generic type %s in PHPDoc tag @return does not specify all template types of %s %s: %s',
'Generic type %s in PHPDoc tag @return specifies %d template types, but %s %s supports only %d: %s',
'Type %s in generic type %s in PHPDoc tag @return is not subtype of template type %s of %s %s.',
'Call-site variance of %s in generic type %s in PHPDoc tag @return is in conflict with %s template type %s of %s %s.',
'Call-site variance of %s in generic type %s in PHPDoc tag @return is redundant, template type %s of %s %s has the same variance.',
));
if ($isReturnSuperType->no()) {
$errors[] = RuleErrorBuilder::message(sprintf(
Expand Down
12 changes: 12 additions & 0 deletions src/Rules/PhpDoc/IncompatiblePropertyPhpDocTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ public function processNode(Node $node, Scope $scope): array
$className,
$escapedPropertyName,
),
sprintf(
'Call-site variance of %%s in generic type %%s in %s for property %s::$%s is in conflict with %%s template type %%s of %%s %%s.',
$description,
$className,
$escapedPropertyName,
),
sprintf(
'Call-site variance of %%s in generic type %%s in %s for property %s::$%s is redundant, template type %%s of %%s %%s has the same variance.',
$description,
$className,
$escapedPropertyName,
),
));

return $messages;
Expand Down
2 changes: 2 additions & 0 deletions src/Rules/PhpDoc/InvalidPhpDocVarTagTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ public function processNode(Node $node, Scope $scope): array
sprintf('Generic type %%s in %s does not specify all template types of %%s %%s: %%s', $escapedIdentifier),
sprintf('Generic type %%s in %s specifies %%d template types, but %%s %%s supports only %%d: %%s', $escapedIdentifier),
sprintf('Type %%s in generic type %%s in %s is not subtype of template type %%s of %%s %%s.', $escapedIdentifier),
sprintf('Call-site variance of %%s in generic type %%s in %s is in conflict with %%s template type %%s of %%s %%s.', $escapedIdentifier),
sprintf('Call-site variance of %%s in generic type %%s in %s is redundant, template type %%s of %%s %%s has the same variance.', $escapedIdentifier),
));

foreach ($this->missingTypehintCheck->getNonGenericObjectTypesWithGenericClass($varTagType) as [$innerName, $genericTypeNames]) {
Expand Down
9 changes: 9 additions & 0 deletions tests/PHPStan/Rules/Classes/MixinRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ public function testRule(): void
'PHPDoc tag @mixin contains non-object type int.',
92,
],
[
'Call-site variance of contravariant MixinRule\Foo in generic type MixinRule\Adipiscing<contravariant MixinRule\Foo> in PHPDoc tag @mixin is in conflict with covariant template type T of class MixinRule\Adipiscing.',
108,
],
[
'Call-site variance of covariant MixinRule\Foo in generic type MixinRule\Adipiscing<covariant MixinRule\Foo> in PHPDoc tag @mixin is redundant, template type T of class MixinRule\Adipiscing has the same variance.',
116,
'You can safely remove the call-site variance annotation.',
],
]);
}

Expand Down
24 changes: 24 additions & 0 deletions tests/PHPStan/Rules/Classes/data/mixin.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,27 @@ interface InterfaceWithMixin
{

}

/**
* @template-covariant T
*/
class Adipiscing
{

}

/**
* @mixin Adipiscing<contravariant Foo>
*/
class Elit
{

}

/**
* @mixin Adipiscing<covariant Foo>
*/
class Elit2
{

}
8 changes: 8 additions & 0 deletions tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ public function testRuleExtends(): void
215,
'You can turn this off by setting <fg=cyan>checkGenericClassInNonGenericObjectType: false</> in your <fg=cyan>%configurationFile%</>.',
],
[
'Call-site variance annotation of covariant Throwable in generic type ClassAncestorsExtends\FooGeneric<covariant Throwable, InvalidArgumentException> in PHPDoc tag @extends is not allowed.',
228,
],
]);
}

Expand Down Expand Up @@ -194,6 +198,10 @@ public function testRuleImplements(): void
'Template type T is declared as covariant, but occurs in invariant position in implemented type ClassAncestorsImplements\FooGeneric9<T, T> of class ClassAncestorsImplements\FooGeneric10.',
216,
],
[
'Call-site variance annotation of covariant Throwable in generic type ClassAncestorsImplements\FooGeneric<covariant Throwable, InvalidArgumentException> in PHPDoc tag @implements is not allowed.',
224,
],
]);
}

Expand Down
9 changes: 9 additions & 0 deletions tests/PHPStan/Rules/Generics/ClassTemplateTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ public function testRule(): void
'PHPDoc tag @template for anonymous class cannot have existing type alias TypeAlias as its name.',
78,
],
[
'Call-site variance of covariant int in generic type ClassTemplateType\Consecteur<covariant int> in PHPDoc tag @template U is redundant, template type T of class ClassTemplateType\Consecteur has the same variance.',
113,
'You can safely remove the call-site variance annotation.',
],
[
'Call-site variance of contravariant int in generic type ClassTemplateType\Consecteur<contravariant int> in PHPDoc tag @template W is in conflict with covariant template type T of class ClassTemplateType\Consecteur.',
113,
],
]);
}

Expand Down
4 changes: 4 additions & 0 deletions tests/PHPStan/Rules/Generics/EnumAncestorsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public function testRule(): void
'Enum EnumGenericAncestors\Foo7 has @extends tag, but cannot extend anything.',
64,
],
[
'Call-site variance annotation of covariant EnumGenericAncestors\NonGeneric in generic type EnumGenericAncestors\Generic<covariant EnumGenericAncestors\NonGeneric, int> in PHPDoc tag @implements is not allowed.',
93,
],
]);
}

Expand Down
9 changes: 9 additions & 0 deletions tests/PHPStan/Rules/Generics/FunctionTemplateTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ public function testRule(): void
'PHPDoc tag @template T for function FunctionTemplateType\nullNotSupported() with bound type null is not supported.',
68,
],
[
'Call-site variance of covariant int in generic type FunctionTemplateType\GenericCovariant<covariant int> in PHPDoc tag @template U is redundant, template type T of class FunctionTemplateType\GenericCovariant has the same variance.',
94,
'You can safely remove the call-site variance annotation.',
],
[
'Call-site variance of contravariant int in generic type FunctionTemplateType\GenericCovariant<contravariant int> in PHPDoc tag @template W is in conflict with covariant template type T of class FunctionTemplateType\GenericCovariant.',
94,
],
]);
}

Expand Down
8 changes: 8 additions & 0 deletions tests/PHPStan/Rules/Generics/InterfaceAncestorsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ public function testRuleImplements(): void
'Interface InterfaceAncestorsImplements\FooGenericGeneric8 has @implements tag, but can not implement any interface, must extend from it.',
182,
],
[
'Interface InterfaceAncestorsImplements\FooTypeProjection has @implements tag, but can not implement any interface, must extend from it.',
190,
],
]);
}

Expand Down Expand Up @@ -194,6 +198,10 @@ public function testRuleExtends(): void
'Template type T is declared as covariant, but occurs in invariant position in extended type InterfaceAncestorsExtends\FooGeneric9<T, T> of interface InterfaceAncestorsExtends\FooGeneric10.',
215,
],
[
'Call-site variance annotation of covariant LogicException in generic type InterfaceAncestorsExtends\FooGeneric<int, covariant LogicException> in PHPDoc tag @extends is not allowed.',
223,
],
]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ public function testRule(): void
'PHPDoc tag @template for interface InterfaceTemplateType\Ipsum cannot have existing type alias ImportedAlias as its name.',
45,
],
[
'Call-site variance of covariant int in generic type InterfaceTemplateType\Covariant<covariant int> in PHPDoc tag @template U is redundant, template type T of interface InterfaceTemplateType\Covariant has the same variance.',
74,
'You can safely remove the call-site variance annotation.',
],
[
'Call-site variance of contravariant int in generic type InterfaceTemplateType\Covariant<contravariant int> in PHPDoc tag @template W is in conflict with covariant template type T of interface InterfaceTemplateType\Covariant.',
74,
],
]);
}

Expand Down
Loading

0 comments on commit abac6e1

Please sign in to comment.