Skip to content

Commit 3f75d8a

Browse files
committed
Fix first-class callable internal error
1 parent 3a3c69e commit 3f75d8a

File tree

6 files changed

+70
-12
lines changed

6 files changed

+70
-12
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,13 +1665,13 @@ private function processExprNode(Expr $expr, MutatingScope $scope, callable $nod
16651665
{
16661666
if ($expr instanceof Expr\CallLike && $expr->isFirstClassCallable()) {
16671667
if ($expr instanceof FuncCall) {
1668-
$newExpr = new FunctionCallableNode($expr->name, $expr->getAttributes());
1668+
$newExpr = new FunctionCallableNode($expr->name, $expr);
16691669
} elseif ($expr instanceof MethodCall) {
16701670
$newExpr = new MethodCallableNode($expr->var, $expr->name, $expr);
16711671
} elseif ($expr instanceof StaticCall) {
16721672
$newExpr = new StaticMethodCallableNode($expr->class, $expr->name, $expr);
16731673
} elseif ($expr instanceof New_ && !$expr->class instanceof Class_) {
1674-
$newExpr = new InstantiationCallableNode($expr->class, $expr->getAttributes());
1674+
$newExpr = new InstantiationCallableNode($expr->class, $expr);
16751675
} else {
16761676
throw new ShouldNotHappenException();
16771677
}

src/Node/ClassStatementsGatherer.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ private function gatherNodes(Node $node, Scope $scope): void
175175
if ($node instanceof Node\Scalar\EncapsedStringPart) {
176176
return;
177177
}
178+
if ($node instanceof FunctionCallableNode) {
179+
$node = $node->getOriginalNode();
180+
} elseif ($node instanceof InstantiationCallableNode) {
181+
$node = $node->getOriginalNode();
182+
}
178183

179184
$inAssign = $scope->isInExpressionAssign($node);
180185
if ($inAssign) {

src/Node/FunctionCallableNode.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99
class FunctionCallableNode extends Expr implements VirtualNode
1010
{
1111

12-
/**
13-
* @param mixed[] $attributes
14-
*/
15-
public function __construct(private Name|Expr $name, array $attributes = [])
12+
public function __construct(private Name|Expr $name, private Expr\FuncCall $originalNode)
1613
{
17-
parent::__construct($attributes);
14+
parent::__construct($this->originalNode->getAttributes());
1815
}
1916

2017
/**
@@ -25,6 +22,11 @@ public function getName()
2522
return $this->name;
2623
}
2724

25+
public function getOriginalNode(): Expr\FuncCall
26+
{
27+
return $this->originalNode;
28+
}
29+
2830
public function getType(): string
2931
{
3032
return 'PHPStan_Node_FunctionCallableNode';

src/Node/InstantiationCallableNode.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99
class InstantiationCallableNode extends Expr implements VirtualNode
1010
{
1111

12-
/**
13-
* @param mixed[] $attributes
14-
*/
15-
public function __construct(private Name|Expr $class, array $attributes = [])
12+
public function __construct(private Name|Expr $class, private Expr\New_ $originalNode)
1613
{
17-
parent::__construct($attributes);
14+
parent::__construct($this->originalNode->getAttributes());
1815
}
1916

2017
/**
@@ -25,6 +22,11 @@ public function getClass()
2522
return $this->class;
2623
}
2724

25+
public function getOriginalNode(): Expr\New_
26+
{
27+
return $this->originalNode;
28+
}
29+
2830
public function getType(): string
2931
{
3032
return 'PHPStan_Node_InstantiationCallableNode';

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,17 @@ public function testBug3853(): void
757757
$this->assertNoErrors($errors);
758758
}
759759

760+
public function testBug7135(): void
761+
{
762+
if (PHP_VERSION_ID < 80100) {
763+
$this->markTestSkipped('Test requires PHP 8.1.');
764+
}
765+
766+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-7135.php');
767+
$this->assertCount(1, $errors);
768+
$this->assertSame('Cannot create callable from the new operator.', $errors[0]->getMessage());
769+
}
770+
760771
/**
761772
* @param string[]|null $allAnalysedFiles
762773
* @return Error[]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php // lint >= 8.1
2+
3+
namespace Bug7135;
4+
5+
class HelloWorld
6+
{
7+
private \Closure $closure;
8+
public function sayHello(callable $callable): void
9+
{
10+
$this->closure = $callable(...);
11+
}
12+
public function sayHello2(callable $callable): void
13+
{
14+
$this->closure = $this->sayHello(...);
15+
}
16+
public function sayHello3(callable $callable): void
17+
{
18+
$this->closure = strlen(...);
19+
}
20+
public function sayHello4(callable $callable): void
21+
{
22+
$this->closure = new HelloWorld(...);
23+
}
24+
public function sayHello5(callable $callable): void
25+
{
26+
$this->closure = self::doFoo(...);
27+
}
28+
29+
public static function doFoo(): void
30+
{
31+
32+
}
33+
34+
public function getClosure(): \Closure
35+
{
36+
return $this->closure;
37+
}
38+
}

0 commit comments

Comments
 (0)