Skip to content

Commit

Permalink
Recognize [1 => 'method', 0 => $obj] as callable
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed Jan 31, 2024
1 parent 778b569 commit b04bb5a
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 18 deletions.
52 changes: 34 additions & 18 deletions src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -503,23 +503,46 @@ public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope)
return $acceptors;
}

/** @deprecated Use findTypeAndMethodNames() instead */
public function findTypeAndMethodName(): ?ConstantArrayTypeAndMethod
/**
* @return array{Type, Type}|array{}
*/
private function getClassOrObjectAndMethods(): array
{
if (count($this->keyTypes) !== 2) {
return null;
return [];
}

if ($this->keyTypes[0]->isSuperTypeOf(new ConstantIntegerType(0))->no()) {
return null;
$classOrObject = null;
$method = null;
foreach ($this->keyTypes as $i => $keyType) {
if ($keyType->isSuperTypeOf(new ConstantIntegerType(0))->yes()) {
$classOrObject = $this->valueTypes[$i];
continue;
}

if (!$keyType->isSuperTypeOf(new ConstantIntegerType(1))->yes()) {
continue;
}

$method = $this->valueTypes[$i];
}

if ($this->keyTypes[1]->isSuperTypeOf(new ConstantIntegerType(1))->no()) {
return null;
if ($classOrObject === null || $method === null) {
return [];
}

[$classOrObject, $method] = $this->valueTypes;
return [$classOrObject, $method];
}

/** @deprecated Use findTypeAndMethodNames() instead */
public function findTypeAndMethodName(): ?ConstantArrayTypeAndMethod
{
$callableArray = $this->getClassOrObjectAndMethods();
if ($callableArray === []) {
return null;
}

[$classOrObject, $method] = $callableArray;
if (!$method instanceof ConstantStringType) {
return ConstantArrayTypeAndMethod::createUnknown();
}
Expand All @@ -544,19 +567,12 @@ public function findTypeAndMethodName(): ?ConstantArrayTypeAndMethod
/** @return ConstantArrayTypeAndMethod[] */
public function findTypeAndMethodNames(): array
{
if (count($this->keyTypes) !== 2) {
return [];
}

if ($this->keyTypes[0]->isSuperTypeOf(new ConstantIntegerType(0))->no()) {
return [];
}

if ($this->keyTypes[1]->isSuperTypeOf(new ConstantIntegerType(1))->no()) {
$callableArray = $this->getClassOrObjectAndMethods();
if ($callableArray === []) {
return [];
}

[$classOrObject, $methods] = $this->valueTypes;
[$classOrObject, $methods] = $callableArray;
if (count($methods->getConstantStrings()) === 0) {
return [ConstantArrayTypeAndMethod::createUnknown()];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1036,4 +1036,23 @@ public function testLooseComparisonAgainstEnumsNoPhpdoc(): void
$this->analyse([__DIR__ . '/data/loose-comparison-against-enums.php'], $issues);
}

public function testBug10502(): void
{
$tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.';

$this->checkAlwaysTrueCheckTypeFunctionCall = true;
$this->treatPhpDocTypesAsCertain = true;
$this->analyse([__DIR__ . '/data/bug-10502.php'], [
[
"Call to function is_callable() with array{ArrayObject<int, int>, 'count'} will always evaluate to true.",
23,
],
[
"Call to function is_callable() with array{1: 'count', 0: ArrayObject<int, int>} will always evaluate to true.",
24,
$tipText,
],
]);
}

}
26 changes: 26 additions & 0 deletions tests/PHPStan/Rules/Comparison/data/bug-10502.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php declare(strict_types=1);

namespace Bug10502;

use ArrayObject;

/** @param ?ArrayObject<int, int> $x */
function doFoo(?ArrayObject $x):void {
$callable1 = [$x, 'count'];
$callable2 = array_reverse($callable1, true);

var_dump(
is_callable($callable1),
is_callable($callable2)
);
}

function doBar():void {
$callable1 = [new ArrayObject([0]), 'count'];
$callable2 = array_reverse($callable1, true);

var_dump(
is_callable($callable1),
is_callable($callable2)
);
}

0 comments on commit b04bb5a

Please sign in to comment.