Skip to content

Commit 9b0c268

Browse files
committed
Updated ExpressionHandler
1 parent f6d969a commit 9b0c268

File tree

9 files changed

+441
-151
lines changed

9 files changed

+441
-151
lines changed

src/Analyser/Generator/ExprHandler/AssignHandler.php

Lines changed: 4 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use ArrayAccess;
66
use Closure;
77
use Generator;
8-
use PhpParser\Comment\Doc;
98
use PhpParser\Node;
109
use PhpParser\Node\Expr;
1110
use PhpParser\Node\Expr\ArrayDimFetch;
@@ -30,6 +29,7 @@
3029
use PHPStan\Analyser\Generator\GeneratorScope;
3130
use PHPStan\Analyser\Generator\InternalThrowPoint;
3231
use PHPStan\Analyser\Generator\NodeCallbackRequest;
32+
use PHPStan\Analyser\Generator\NodeHandler\VarAnnotationHelper;
3333
use PHPStan\Analyser\Generator\NoopNodeCallback;
3434
use PHPStan\Analyser\Generator\TypeExprRequest;
3535
use PHPStan\Analyser\ImpurePoint;
@@ -44,7 +44,6 @@
4444
use PHPStan\Node\Expr\SetOffsetValueTypeExpr;
4545
use PHPStan\Node\PropertyAssignNode;
4646
use PHPStan\Node\VariableAssignNode;
47-
use PHPStan\Node\VarTagChangedExpressionTypeNode;
4847
use PHPStan\Php\PhpVersion;
4948
use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection;
5049
use PHPStan\Reflection\Php\PhpPropertyReflection;
@@ -57,7 +56,6 @@
5756
use PHPStan\Type\Constant\ConstantArrayType;
5857
use PHPStan\Type\Constant\ConstantIntegerType;
5958
use PHPStan\Type\Constant\ConstantStringType;
60-
use PHPStan\Type\FileTypeMapper;
6159
use PHPStan\Type\MixedType;
6260
use PHPStan\Type\NullType;
6361
use PHPStan\Type\ObjectType;
@@ -74,7 +72,6 @@
7472
use function array_slice;
7573
use function count;
7674
use function in_array;
77-
use function is_int;
7875
use function is_string;
7976

8077
/**
@@ -88,8 +85,8 @@ final class AssignHandler implements ExprHandler
8885

8986
public function __construct(
9087
private PhpVersion $phpVersion,
91-
private FileTypeMapper $fileTypeMapper,
9288
private AssignHelper $assignHelper,
89+
private VarAnnotationHelper $varAnnotationHelper,
9390
#[AutowiredParameter(ref: '%exceptions.implicitThrows%')]
9491
private readonly bool $implicitThrows,
9592
)
@@ -172,11 +169,11 @@ static function (GeneratorScope $scope) use ($stmt, $expr, $context, $alternativ
172169
$vars = $this->getAssignedVariables($expr->var);
173170
if (count($vars) > 0) {
174171
$varChangedScope = false;
175-
$processVarGen = $this->processVarAnnotation($scope, $vars, $stmt, $varChangedScope);
172+
$processVarGen = $this->varAnnotationHelper->processVarAnnotation($scope, $vars, $stmt, $varChangedScope);
176173
yield from $processVarGen;
177174
$scope = $processVarGen->getReturn();
178175
if (!$varChangedScope) {
179-
$stmtVarGen = $this->processStmtVarAnnotation($scope, $stmt, null, $alternativeNodeCallback);
176+
$stmtVarGen = $this->varAnnotationHelper->processStmtVarAnnotation($scope, $stmt, null, $alternativeNodeCallback);
180177
yield from $stmtVarGen;
181178
$scope = $stmtVarGen->getReturn();
182179
}
@@ -228,146 +225,6 @@ private function getAssignedVariables(Expr $expr): array
228225
return [];
229226
}
230227

231-
/**
232-
* @param array<int, string> $variableNames
233-
* @return Generator<int, GeneratorTValueType, GeneratorTSendType, GeneratorScope>
234-
*/
235-
private function processVarAnnotation(GeneratorScope $scope, array $variableNames, Node\Stmt $node, bool &$changed = false): Generator
236-
{
237-
$function = $scope->getFunction();
238-
$varTags = [];
239-
foreach ($node->getComments() as $comment) {
240-
if (!$comment instanceof Doc) {
241-
continue;
242-
}
243-
244-
$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
245-
$scope->getFile(),
246-
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
247-
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
248-
$function !== null ? $function->getName() : null,
249-
$comment->getText(),
250-
);
251-
foreach ($resolvedPhpDoc->getVarTags() as $key => $varTag) {
252-
$varTags[$key] = $varTag;
253-
}
254-
}
255-
256-
if (count($varTags) === 0) {
257-
return $scope;
258-
}
259-
260-
foreach ($variableNames as $variableName) {
261-
if (!isset($varTags[$variableName])) {
262-
continue;
263-
}
264-
265-
$variableType = $varTags[$variableName]->getType();
266-
$changed = true;
267-
$assignVarGen = $scope->assignVariable($variableName, $variableType, new MixedType(), TrinaryLogic::createYes());
268-
yield from $assignVarGen;
269-
$scope = $assignVarGen->getReturn();
270-
}
271-
272-
if (count($variableNames) === 1 && count($varTags) === 1 && isset($varTags[0])) {
273-
$variableType = $varTags[0]->getType();
274-
$changed = true;
275-
$assignVarGen = $scope->assignVariable($variableNames[0], $variableType, new MixedType(), TrinaryLogic::createYes());
276-
yield from $assignVarGen;
277-
$scope = $assignVarGen->getReturn();
278-
}
279-
280-
return $scope;
281-
}
282-
283-
/**
284-
* @param (callable(Node, Scope, callable(Node, Scope): void): void)|null $alternativeNodeCallback
285-
* @return Generator<int, GeneratorTValueType, GeneratorTSendType, GeneratorScope>
286-
*/
287-
private function processStmtVarAnnotation(GeneratorScope $scope, Node\Stmt $stmt, ?Expr $defaultExpr, ?callable $alternativeNodeCallback): Generator
288-
{
289-
$function = $scope->getFunction();
290-
$variableLessTags = [];
291-
292-
foreach ($stmt->getComments() as $comment) {
293-
if (!$comment instanceof Doc) {
294-
continue;
295-
}
296-
297-
$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
298-
$scope->getFile(),
299-
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
300-
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
301-
$function !== null ? $function->getName() : null,
302-
$comment->getText(),
303-
);
304-
305-
$assignedVariable = null;
306-
if (
307-
$stmt instanceof Node\Stmt\Expression
308-
&& ($stmt->expr instanceof Assign || $stmt->expr instanceof AssignRef)
309-
&& $stmt->expr->var instanceof Variable
310-
&& is_string($stmt->expr->var->name)
311-
) {
312-
$assignedVariable = $stmt->expr->var->name;
313-
}
314-
315-
foreach ($resolvedPhpDoc->getVarTags() as $name => $varTag) {
316-
if (is_int($name)) {
317-
$variableLessTags[] = $varTag;
318-
continue;
319-
}
320-
321-
if ($name === $assignedVariable) {
322-
continue;
323-
}
324-
325-
$certainty = $scope->hasVariableType($name);
326-
if ($certainty->no()) {
327-
continue;
328-
}
329-
330-
if ($scope->isInClass() && $scope->getFunction() === null) {
331-
continue;
332-
}
333-
334-
if ($scope->canAnyVariableExist()) {
335-
$certainty = TrinaryLogic::createYes();
336-
}
337-
338-
$variableNode = new Variable($name, $stmt->getAttributes());
339-
$originalType = $scope->getVariableType($name);
340-
if (!$originalType->equals($varTag->getType())) {
341-
yield new NodeCallbackRequest(new VarTagChangedExpressionTypeNode($varTag, $variableNode), $scope, $alternativeNodeCallback);
342-
}
343-
344-
$variableNodeResult = yield new ExprAnalysisRequest($stmt, $variableNode, $scope, ExpressionContext::createDeep(), new NoopNodeCallback());
345-
346-
$assignVarGen = $scope->assignVariable(
347-
$name,
348-
$varTag->getType(),
349-
$variableNodeResult->nativeType,
350-
$certainty,
351-
);
352-
yield from $assignVarGen;
353-
$scope = $assignVarGen->getReturn();
354-
}
355-
}
356-
357-
if (count($variableLessTags) === 1 && $defaultExpr !== null) {
358-
//$originalType = $scope->getType($defaultExpr);
359-
$varTag = $variableLessTags[0];
360-
/*if (!$originalType->equals($varTag->getType())) {
361-
yield new NodeCallbackRequest(new VarTagChangedExpressionTypeNode($varTag, $defaultExpr), $scope);
362-
}*/
363-
$assignExprGen = $scope->assignExpression($defaultExpr, $varTag->getType(), new MixedType());
364-
yield from $assignExprGen;
365-
$scope = $assignExprGen->getReturn();
366-
}
367-
368-
return $scope;
369-
}
370-
371228
/**
372229
* @param (callable(Node, Scope, callable(Node, Scope): void): void)|null $alternativeNodeCallback
373230
* @param Closure(GeneratorScope $scope): Generator<int, GeneratorTValueType, GeneratorTSendType, ExprAnalysisResult> $processExprCallback

src/Analyser/Generator/GeneratorNodeScopeResolver.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
1212
use PhpParser\Node\Expr\StaticCall;
1313
use PhpParser\Node\Stmt;
1414
use PhpParser\Node\Stmt\Class_;
15+
use PhpParser\Node\Stmt\Foreach_;
16+
use PhpParser\Node\Stmt\Return_;
17+
use PhpParser\Node\Stmt\Static_;
1518
use PHPStan\Analyser\ExpressionContext;
1619
use PHPStan\Analyser\Generator\NodeHandler\AttrGroupsHandler;
1720
use PHPStan\Analyser\Generator\NodeHandler\StmtsHandler;
21+
use PHPStan\Analyser\Generator\NodeHandler\VarAnnotationHelper;
1822
use PHPStan\Analyser\Scope;
1923
use PHPStan\Analyser\StatementContext;
2024
use PHPStan\DependencyInjection\AutowiredService;
@@ -80,6 +84,7 @@ final class GeneratorNodeScopeResolver
8084

8185
public function __construct(
8286
private ExprPrinter $exprPrinter,
87+
private VarAnnotationHelper $varAnnotationHelper,
8388
private Container $container,
8489
)
8590
{
@@ -341,7 +346,40 @@ private function analyseStmts(Node $parentNode, array $stmts, GeneratorScope $sc
341346
*/
342347
private function analyseStmt(Stmt $stmt, GeneratorScope $scope, StatementContext $context, ?callable $alternativeNodeCallback): Generator
343348
{
344-
yield new NodeCallbackRequest($stmt, $scope, $alternativeNodeCallback);
349+
if (
350+
!$stmt instanceof Static_
351+
&& !$stmt instanceof Foreach_
352+
&& !$stmt instanceof Node\Stmt\Global_
353+
&& !$stmt instanceof Node\Stmt\Property
354+
&& !$stmt instanceof Node\Stmt\ClassConst
355+
&& !$stmt instanceof Node\Stmt\Const_
356+
) {
357+
$stmtVarGen = $this->varAnnotationHelper->processStmtVarAnnotation($scope, $stmt, null, $alternativeNodeCallback);
358+
yield from $stmtVarGen;
359+
$scope = $stmtVarGen->getReturn();
360+
}
361+
362+
$stmtScope = $scope;
363+
if ($stmt instanceof Node\Stmt\Expression && $stmt->expr instanceof Expr\Throw_) {
364+
$stmtScopeGen = $this->varAnnotationHelper->processStmtVarAnnotation($scope, $stmt, $stmt->expr->expr, $alternativeNodeCallback);
365+
yield from $stmtScopeGen;
366+
$stmtScope = $stmtScopeGen->getReturn();
367+
}
368+
if ($stmt instanceof Return_) {
369+
$stmtScopeGen = $this->varAnnotationHelper->processStmtVarAnnotation($scope, $stmt, $stmt->expr, $alternativeNodeCallback);
370+
yield from $stmtScopeGen;
371+
$stmtScope = $stmtScopeGen->getReturn();
372+
}
373+
374+
yield new NodeCallbackRequest($stmt, $stmtScope, $alternativeNodeCallback);
375+
376+
$overridingThrowPoints = $this->varAnnotationHelper->getOverridingThrowPoints($stmt, $scope);
377+
378+
if ($stmt instanceof Stmt\Expression) {
379+
if ($stmt->expr instanceof Expr\Throw_) {
380+
$scope = $stmtScope;
381+
}
382+
}
345383

346384
/**
347385
* @var StmtHandler<Stmt> $stmtHandler
@@ -354,7 +392,12 @@ private function analyseStmt(Stmt $stmt, GeneratorScope $scope, StatementContext
354392
$gen = $stmtHandler->analyseStmt($stmt, $scope, $context, $alternativeNodeCallback);
355393
yield from $gen;
356394

357-
return $gen->getReturn();
395+
$stmtResult = $gen->getReturn();
396+
if ($overridingThrowPoints !== null) {
397+
$stmtResult = $stmtResult->withThrowPoints($overridingThrowPoints);
398+
}
399+
400+
return $stmtResult;
358401
}
359402

360403
throw new ShouldNotHappenException('Unhandled stmt: ' . get_class($stmt));

0 commit comments

Comments
 (0)