Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -519,19 +519,28 @@ public function specifyTypesInCondition(
$methodReflection = $scope->getMethodReflection($methodCalledOnType, $expr->name->name);
if ($methodReflection !== null) {
$referencedClasses = $methodCalledOnType->getObjectClassNames();
if (
count($referencedClasses) === 1
&& $this->reflectionProvider->hasClass($referencedClasses[0])
) {
$methodClassReflection = $this->reflectionProvider->getClass($referencedClasses[0]);
$specifiedTypes = null;
foreach ($referencedClasses as $referencedClass) {
if (!$this->reflectionProvider->hasClass($referencedClass)) {
continue;
}

$methodClassReflection = $this->reflectionProvider->getClass($referencedClass);
foreach ($this->getMethodTypeSpecifyingExtensionsForClass($methodClassReflection->getName()) as $extension) {
if (!$extension->isMethodSupported($methodReflection, $expr, $context)) {
continue;
}

return $extension->specifyTypes($methodReflection, $expr, $scope, $context);
if ($specifiedTypes !== null) {
$specifiedTypes = $specifiedTypes->unionWith($extension->specifyTypes($methodReflection, $expr, $scope, $context));
} else {
$specifiedTypes = $extension->specifyTypes($methodReflection, $expr, $scope, $context);
}
}
}
if ($specifiedTypes !== null) {
return $specifiedTypes;
}

// lazy create parametersAcceptor, as creation can be expensive
$parametersAcceptor = null;
Expand Down Expand Up @@ -572,19 +581,28 @@ public function specifyTypesInCondition(
$staticMethodReflection = $scope->getMethodReflection($calleeType, $expr->name->name);
if ($staticMethodReflection !== null) {
$referencedClasses = $calleeType->getObjectClassNames();
if (
count($referencedClasses) === 1
&& $this->reflectionProvider->hasClass($referencedClasses[0])
) {
$staticMethodClassReflection = $this->reflectionProvider->getClass($referencedClasses[0]);
$specifiedTypes = null;
foreach ($referencedClasses as $referencedClass) {
if (!$this->reflectionProvider->hasClass($referencedClass)) {
continue;
}

$staticMethodClassReflection = $this->reflectionProvider->getClass($referencedClass);
foreach ($this->getStaticMethodTypeSpecifyingExtensionsForClass($staticMethodClassReflection->getName()) as $extension) {
if (!$extension->isStaticMethodSupported($staticMethodReflection, $expr, $context)) {
continue;
}

return $extension->specifyTypes($staticMethodReflection, $expr, $scope, $context);
if ($specifiedTypes !== null) {
$specifiedTypes = $specifiedTypes->unionWith($extension->specifyTypes($staticMethodReflection, $expr, $scope, $context));
} else {
$specifiedTypes = $extension->specifyTypes($staticMethodReflection, $expr, $scope, $context);
}
}
}
if ($specifiedTypes !== null) {
return $specifiedTypes;
}

// lazy create parametersAcceptor, as creation can be expensive
$parametersAcceptor = null;
Expand Down
21 changes: 21 additions & 0 deletions tests/PHPStan/Analyser/MultipleTypeSpecifyingExtension.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
-
class: PHPStan\Tests\AssertionClassMethodTypeSpecifyingExtension
arguments:
nullContext: true
tags:
- phpstan.typeSpecifier.methodTypeSpecifyingExtension
-
class: PHPStan\Tests\AssertionClassStaticMethodTypeSpecifyingExtension
arguments:
nullContext: true
tags:
- phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension
-
class: PHPStan\Tests\AssertionClassMethodMultipleTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.methodTypeSpecifyingExtension
-
class: PHPStan\Tests\AssertionClassStaticMethodMultipleTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser;

use PHPStan\Testing\TypeInferenceTestCase;

class MultipleTypeSpecifyingExtensionTypeInferenceTest extends TypeInferenceTestCase
{

public function dataTypeSpecifyingExtensionsTrue(): iterable
{
yield from $this->gatherAssertTypes(__DIR__ . '/data/multiple-type-specifying-extensions-1.php');
}

/**
* @dataProvider dataTypeSpecifyingExtensionsTrue
* @param mixed ...$args
*/
public function testTypeSpecifyingExtensionsTrue(

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan with result cache (8.2)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan with result cache (8.4)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan with result cache (8.3)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan with result cache (8.1)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.2, windows-latest)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.3, windows-latest)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.1, windows-latest)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.3, ubuntu-latest)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.2, ubuntu-latest)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.4, ubuntu-latest)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.4, windows-latest)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.

Check failure on line 19 in tests/PHPStan/Analyser/MultipleTypeSpecifyingExtensionTypeInferenceTest.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.1, ubuntu-latest)

@dataProvider dataTypeSpecifyingExtensionsTrue related method must be static in PHPUnit 10 and newer.
string $assertType,
string $file,
...$args,
): void
{
$this->assertFileAsserts($assertType, $file, ...$args);
}

public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/MultipleTypeSpecifyingExtension.neon',
];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

use function PHPStan\Testing\assertType;

/** @var string|null $foo */
$foo = null;

/** @var int|null $bar */
$bar = null;

(new \PHPStan\Tests\AssertionClass())->assertString($foo);
\PHPStan\Tests\AssertionClass::assertInt($bar);

assertType('non-empty-string', $foo);
assertType('int<1, max>', $bar);

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php declare(strict_types = 1);

namespace PHPStan\Tests;

use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\MethodTypeSpecifyingExtension;

class AssertionClassMethodMultipleTypeSpecifyingExtension implements MethodTypeSpecifyingExtension
{

public function getClass(): string
{
return AssertionClass::class;
}

public function isMethodSupported(
MethodReflection $methodReflection,
MethodCall $node,
TypeSpecifierContext $context,
): bool
{
return $methodReflection->getName() === 'assertString';
}

public function specifyTypes(
MethodReflection $methodReflection,
MethodCall $node,
Scope $scope,
TypeSpecifierContext $context,
): SpecifiedTypes
{
return new SpecifiedTypes(['$foo' => [$node->getArgs()[0]->value, new AccessoryNonEmptyStringType()]]);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php declare(strict_types = 1);

namespace PHPStan\Tests;

use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\StaticMethodTypeSpecifyingExtension;

class AssertionClassStaticMethodMultipleTypeSpecifyingExtension implements StaticMethodTypeSpecifyingExtension
{

public function getClass(): string
{
return AssertionClass::class;
}

public function isStaticMethodSupported(
MethodReflection $staticMethodReflection,
StaticCall $node,
TypeSpecifierContext $context,
): bool
{
return $staticMethodReflection->getName() === 'assertInt';
}

public function specifyTypes(
MethodReflection $staticMethodReflection,
StaticCall $node,
Scope $scope,
TypeSpecifierContext $context,
): SpecifiedTypes
{
return new SpecifiedTypes(['$foo' => [$node->getArgs()[0]->value, IntegerRangeType::createAllGreaterThan(0)]]);
}

}
Loading