Skip to content

Commit 52980d0

Browse files
committed
Implement InterpolatedStringHandler
1 parent 6cf0ab2 commit 52980d0

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Generator\ExprHandler;
4+
5+
use Generator;
6+
use PhpParser\Node\Expr;
7+
use PhpParser\Node\InterpolatedStringPart;
8+
use PhpParser\Node\Scalar\InterpolatedString;
9+
use PhpParser\Node\Stmt;
10+
use PHPStan\Analyser\ExpressionContext;
11+
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
12+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
13+
use PHPStan\Analyser\Generator\ExprHandler;
14+
use PHPStan\Analyser\Generator\GeneratorScope;
15+
use PHPStan\Analyser\Generator\NoopNodeCallback;
16+
use PHPStan\Analyser\SpecifiedTypes;
17+
use PHPStan\DependencyInjection\AutowiredService;
18+
use PHPStan\Reflection\InitializerExprTypeResolver;
19+
use PHPStan\Type\Constant\ConstantStringType;
20+
use function array_merge;
21+
22+
/**
23+
* @implements ExprHandler<InterpolatedString>
24+
*/
25+
#[AutowiredService]
26+
final class InterpolatedStringHandler implements ExprHandler
27+
{
28+
29+
public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver)
30+
{
31+
}
32+
33+
public function supports(Expr $expr): bool
34+
{
35+
return $expr instanceof InterpolatedString;
36+
}
37+
38+
public function analyseExpr(
39+
Stmt $stmt,
40+
Expr $expr,
41+
GeneratorScope $scope,
42+
ExpressionContext $context,
43+
?callable $alternativeNodeCallback,
44+
): Generator
45+
{
46+
yield from [];
47+
48+
$resultType = null;
49+
$hasYield = false;
50+
$throwPoints = [];
51+
$impurePoints = [];
52+
$isAlwaysTerminating = false;
53+
foreach ($expr->parts as $part) {
54+
if ($part instanceof InterpolatedStringPart) {
55+
$partType = new ConstantStringType($part->value);
56+
} else {
57+
$result = yield new ExprAnalysisRequest($stmt, $part, $scope, $context->enterDeep(), new NoopNodeCallback());
58+
$partType = $result->type->toString();
59+
60+
$hasYield = $hasYield || $result->hasYield;
61+
$throwPoints = array_merge($throwPoints, $result->throwPoints);
62+
$impurePoints = array_merge($impurePoints, $result->impurePoints);
63+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating;
64+
$scope = $result->scope;
65+
}
66+
67+
if ($resultType === null) {
68+
$resultType = $partType;
69+
continue;
70+
}
71+
72+
$resultType = $this->initializerExprTypeResolver->resolveConcatType($resultType, $partType);
73+
}
74+
75+
$type = $resultType ?? new ConstantStringType('');
76+
return new ExprAnalysisResult(
77+
$type,
78+
$type,
79+
$scope,
80+
hasYield: $hasYield,
81+
isAlwaysTerminating: $isAlwaysTerminating,
82+
throwPoints: $throwPoints,
83+
impurePoints: $impurePoints,
84+
specifiedTruthyTypes: new SpecifiedTypes(),
85+
specifiedFalseyTypes: new SpecifiedTypes(),
86+
specifiedNullTypes: new SpecifiedTypes(),
87+
);
88+
}
89+
90+
}

tests/PHPStan/Analyser/Generator/data/gnsr.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,14 @@ function doCast() {
247247
assertType("'1'", (string) $a);
248248
}
249249

250+
function doInterpolatedString() {
251+
$a = '1';
252+
253+
assertType("'1'", "$a");
254+
assertNativeType("'1'", "$a");
255+
}
256+
257+
250258
}
251259

252260
function (): void {

0 commit comments

Comments
 (0)