Skip to content

Commit

Permalink
Fix first-class callable internal error
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Apr 27, 2022
1 parent 3a3c69e commit 3f75d8a
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 12 deletions.
4 changes: 2 additions & 2 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -1665,13 +1665,13 @@ private function processExprNode(Expr $expr, MutatingScope $scope, callable $nod
{
if ($expr instanceof Expr\CallLike && $expr->isFirstClassCallable()) {
if ($expr instanceof FuncCall) {
$newExpr = new FunctionCallableNode($expr->name, $expr->getAttributes());
$newExpr = new FunctionCallableNode($expr->name, $expr);
} elseif ($expr instanceof MethodCall) {
$newExpr = new MethodCallableNode($expr->var, $expr->name, $expr);
} elseif ($expr instanceof StaticCall) {
$newExpr = new StaticMethodCallableNode($expr->class, $expr->name, $expr);
} elseif ($expr instanceof New_ && !$expr->class instanceof Class_) {
$newExpr = new InstantiationCallableNode($expr->class, $expr->getAttributes());
$newExpr = new InstantiationCallableNode($expr->class, $expr);
} else {
throw new ShouldNotHappenException();
}
Expand Down
5 changes: 5 additions & 0 deletions src/Node/ClassStatementsGatherer.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ private function gatherNodes(Node $node, Scope $scope): void
if ($node instanceof Node\Scalar\EncapsedStringPart) {
return;
}
if ($node instanceof FunctionCallableNode) {
$node = $node->getOriginalNode();
} elseif ($node instanceof InstantiationCallableNode) {
$node = $node->getOriginalNode();
}

$inAssign = $scope->isInExpressionAssign($node);
if ($inAssign) {
Expand Down
12 changes: 7 additions & 5 deletions src/Node/FunctionCallableNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@
class FunctionCallableNode extends Expr implements VirtualNode
{

/**
* @param mixed[] $attributes
*/
public function __construct(private Name|Expr $name, array $attributes = [])
public function __construct(private Name|Expr $name, private Expr\FuncCall $originalNode)
{
parent::__construct($attributes);
parent::__construct($this->originalNode->getAttributes());
}

/**
Expand All @@ -25,6 +22,11 @@ public function getName()
return $this->name;
}

public function getOriginalNode(): Expr\FuncCall
{
return $this->originalNode;
}

public function getType(): string
{
return 'PHPStan_Node_FunctionCallableNode';
Expand Down
12 changes: 7 additions & 5 deletions src/Node/InstantiationCallableNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@
class InstantiationCallableNode extends Expr implements VirtualNode
{

/**
* @param mixed[] $attributes
*/
public function __construct(private Name|Expr $class, array $attributes = [])
public function __construct(private Name|Expr $class, private Expr\New_ $originalNode)
{
parent::__construct($attributes);
parent::__construct($this->originalNode->getAttributes());
}

/**
Expand All @@ -25,6 +22,11 @@ public function getClass()
return $this->class;
}

public function getOriginalNode(): Expr\New_
{
return $this->originalNode;
}

public function getType(): string
{
return 'PHPStan_Node_InstantiationCallableNode';
Expand Down
11 changes: 11 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,17 @@ public function testBug3853(): void
$this->assertNoErrors($errors);
}

public function testBug7135(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$errors = $this->runAnalyse(__DIR__ . '/data/bug-7135.php');
$this->assertCount(1, $errors);
$this->assertSame('Cannot create callable from the new operator.', $errors[0]->getMessage());
}

/**
* @param string[]|null $allAnalysedFiles
* @return Error[]
Expand Down
38 changes: 38 additions & 0 deletions tests/PHPStan/Analyser/data/bug-7135.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php // lint >= 8.1

namespace Bug7135;

class HelloWorld
{
private \Closure $closure;
public function sayHello(callable $callable): void
{
$this->closure = $callable(...);
}
public function sayHello2(callable $callable): void
{
$this->closure = $this->sayHello(...);
}
public function sayHello3(callable $callable): void
{
$this->closure = strlen(...);
}
public function sayHello4(callable $callable): void
{
$this->closure = new HelloWorld(...);
}
public function sayHello5(callable $callable): void
{
$this->closure = self::doFoo(...);
}

public static function doFoo(): void
{

}

public function getClosure(): \Closure
{
return $this->closure;
}
}

0 comments on commit 3f75d8a

Please sign in to comment.