Skip to content

Commit 013e021

Browse files
committed
GNSR - all info passed in requests and results
1 parent fe194c2 commit 013e021

33 files changed

+519
-94
lines changed

build/phpstan.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ parameters:
117117
count: 1
118118
path: ../src/Diagnose/PHPStanDiagnoseExtension.php
119119
- '#^Short ternary operator is not allowed#'
120+
-
121+
identifier: shipmonk.deadMethod
122+
path: ../src/Analyser/Generator
120123
reportStaticMethodSignatures: true
121124
tmpDir: %rootDir%/tmp
122125
stubFiles:

src/Analyser/Generator/ExprAnalysisRequest.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use PhpParser\Node;
66
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Stmt;
8+
use PHPStan\Analyser\ExpressionContext;
79
use PHPStan\Analyser\Scope;
810

911
final class ExprAnalysisRequest
@@ -13,9 +15,11 @@ final class ExprAnalysisRequest
1315
* @param (callable(Node, Scope, callable(Node, Scope): void): void)|null $alternativeNodeCallback
1416
*/
1517
public function __construct(
16-
public Expr $expr,
17-
public GeneratorScope $scope,
18-
public $alternativeNodeCallback = null,
18+
public readonly Stmt $stmt,
19+
public readonly Expr $expr,
20+
public readonly GeneratorScope $scope,
21+
public readonly ExpressionContext $context,
22+
public readonly mixed $alternativeNodeCallback = null,
1923
)
2024
{
2125
}

src/Analyser/Generator/ExprAnalysisResult.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@
22

33
namespace PHPStan\Analyser\Generator;
44

5+
use PHPStan\Analyser\ImpurePoint;
56
use PHPStan\Type\Type;
67

78
final class ExprAnalysisResult
89
{
910

11+
/**
12+
* @param InternalThrowPoint[] $throwPoints
13+
* @param ImpurePoint[] $impurePoints
14+
*/
1015
public function __construct(
11-
public Type $type,
12-
public GeneratorScope $scope,
16+
public readonly Type $type,
17+
public readonly GeneratorScope $scope,
18+
public readonly bool $hasYield,
19+
public readonly bool $isAlwaysTerminating,
20+
public readonly array $throwPoints,
21+
public readonly array $impurePoints,
1322
)
1423
{
1524
}

src/Analyser/Generator/ExprHandler.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use Generator;
66
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Stmt;
8+
use PHPStan\Analyser\ExpressionContext;
79

810
/**
911
* @template T of Expr
@@ -22,6 +24,6 @@ public function supports(Expr $expr): bool;
2224
* @param T $expr
2325
* @return Generator<int, ExprAnalysisRequest|NodeCallbackRequest, ExprAnalysisResult, ExprAnalysisResult>
2426
*/
25-
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator;
27+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context): Generator;
2628

2729
}

src/Analyser/Generator/ExprHandler/AssignHandler.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
use Generator;
66
use PhpParser\Node\Expr;
77
use PhpParser\Node\Expr\Assign;
8+
use PhpParser\Node\Stmt;
9+
use PHPStan\Analyser\ExpressionContext;
810
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
911
use PHPStan\Analyser\Generator\ExprAnalysisResult;
1012
use PHPStan\Analyser\Generator\ExprHandler;
1113
use PHPStan\Analyser\Generator\GeneratorScope;
1214
use PHPStan\DependencyInjection\AutowiredService;
1315
use PHPStan\ShouldNotHappenException;
16+
use function array_merge;
1417
use function is_string;
1518

1619
/**
@@ -25,16 +28,23 @@ public function supports(Expr $expr): bool
2528
return $expr instanceof Assign;
2629
}
2730

28-
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
31+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context): Generator
2932
{
3033
if (!$expr->var instanceof Expr\Variable || !is_string($expr->var->name)) {
3134
throw new ShouldNotHappenException('Not implemented');
3235
}
3336
$variableName = $expr->var->name;
34-
$exprResult = yield new ExprAnalysisRequest($expr->expr, $scope);
35-
$varResult = yield new ExprAnalysisRequest($expr->var, $scope);
37+
$exprResult = yield new ExprAnalysisRequest($stmt, $expr->expr, $scope, $context->enterDeep());
38+
$varResult = yield new ExprAnalysisRequest($stmt, $expr->var, $scope, $context->enterDeep());
3639

37-
return new ExprAnalysisResult($exprResult->type, $varResult->scope->assignVariable($variableName, $exprResult->type));
40+
return new ExprAnalysisResult(
41+
$exprResult->type,
42+
$varResult->scope->assignVariable($variableName, $exprResult->type),
43+
hasYield: $exprResult->hasYield || $varResult->hasYield,
44+
isAlwaysTerminating: $exprResult->isAlwaysTerminating || $varResult->isAlwaysTerminating,
45+
throwPoints: array_merge($exprResult->throwPoints, $varResult->throwPoints),
46+
impurePoints: array_merge($exprResult->impurePoints, $varResult->impurePoints),
47+
);
3848
}
3949

4050
}

src/Analyser/Generator/ExprHandler/CastIntHandler.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use Generator;
66
use PhpParser\Node\Expr;
77
use PhpParser\Node\Expr\Cast\Int_;
8+
use PhpParser\Node\Stmt;
9+
use PHPStan\Analyser\ExpressionContext;
810
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
911
use PHPStan\Analyser\Generator\ExprAnalysisResult;
1012
use PHPStan\Analyser\Generator\ExprHandler;
@@ -23,11 +25,18 @@ public function supports(Expr $expr): bool
2325
return $expr instanceof Int_;
2426
}
2527

26-
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
28+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context): Generator
2729
{
28-
$exprResult = yield new ExprAnalysisRequest($expr->expr, $scope);
30+
$exprResult = yield new ExprAnalysisRequest($stmt, $expr->expr, $scope, $context->enterDeep());
2931

30-
return new ExprAnalysisResult($exprResult->type->toInteger(), $scope);
32+
return new ExprAnalysisResult(
33+
$exprResult->type->toInteger(),
34+
$scope,
35+
hasYield: false,
36+
isAlwaysTerminating: false,
37+
throwPoints: [],
38+
impurePoints: [],
39+
);
3140
}
3241

3342
}

src/Analyser/Generator/ExprHandler/ClassConstFetchHandler.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use PhpParser\Node\Expr\ClassConstFetch;
88
use PhpParser\Node\Identifier;
99
use PhpParser\Node\Name;
10+
use PhpParser\Node\Stmt;
11+
use PHPStan\Analyser\ExpressionContext;
1012
use PHPStan\Analyser\Generator\ExprAnalysisResult;
1113
use PHPStan\Analyser\Generator\ExprHandler;
1214
use PHPStan\Analyser\Generator\GeneratorScope;
@@ -26,15 +28,22 @@ public function supports(Expr $expr): bool
2628
return $expr instanceof ClassConstFetch;
2729
}
2830

29-
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
31+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context): Generator
3032
{
3133
if (
3234
$expr->class instanceof Name
3335
&& $expr->name instanceof Identifier
3436
&& $expr->name->toLowerString() === 'class'
3537
) {
3638
yield from [];
37-
return new ExprAnalysisResult(new ConstantStringType($expr->class->toString()), $scope);
39+
return new ExprAnalysisResult(
40+
new ConstantStringType($expr->class->toString()),
41+
$scope,
42+
hasYield: false,
43+
isAlwaysTerminating: false,
44+
throwPoints: [],
45+
impurePoints: [],
46+
);
3847
}
3948

4049
throw new ShouldNotHappenException('Not implemented');

src/Analyser/Generator/ExprHandler/ClosureHandler.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
use Generator;
66
use PhpParser\Node\Expr;
77
use PhpParser\Node\Expr\Closure;
8+
use PhpParser\Node\Stmt;
9+
use PHPStan\Analyser\ExpressionContext;
810
use PHPStan\Analyser\Generator\ExprAnalysisResult;
911
use PHPStan\Analyser\Generator\ExprHandler;
1012
use PHPStan\Analyser\Generator\GeneratorScope;
1113
use PHPStan\Analyser\Generator\StmtsAnalysisRequest;
14+
use PHPStan\Analyser\StatementContext;
1215
use PHPStan\DependencyInjection\AutowiredService;
1316
use PHPStan\Type\ClosureType;
1417

@@ -24,12 +27,19 @@ public function supports(Expr $expr): bool
2427
return $expr instanceof Closure;
2528
}
2629

27-
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
30+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context): Generator
2831
{
29-
$result = yield new StmtsAnalysisRequest($expr->stmts, $scope); // @phpstan-ignore generator.valueType
32+
$result = yield new StmtsAnalysisRequest($expr->stmts, $scope, StatementContext::createTopLevel()); // @phpstan-ignore generator.valueType
3033
$scope = $result->scope;
3134

32-
return new ExprAnalysisResult(new ClosureType(), $scope);
35+
return new ExprAnalysisResult(
36+
new ClosureType(),
37+
$scope,
38+
hasYield: $result->hasYield,
39+
isAlwaysTerminating: $result->isAlwaysTerminating,
40+
throwPoints: $result->throwPoints,
41+
impurePoints: $result->impurePoints,
42+
);
3343
}
3444

3545
}

src/Analyser/Generator/ExprHandler/FuncCallHandler.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66
use PhpParser\Node\Expr;
77
use PhpParser\Node\Expr\FuncCall;
88
use PhpParser\Node\Name;
9+
use PhpParser\Node\Stmt;
10+
use PHPStan\Analyser\ExpressionContext;
911
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
1012
use PHPStan\Analyser\Generator\ExprAnalysisResult;
1113
use PHPStan\Analyser\Generator\ExprHandler;
1214
use PHPStan\Analyser\Generator\GeneratorScope;
1315
use PHPStan\DependencyInjection\AutowiredService;
1416
use PHPStan\Reflection\ReflectionProvider;
1517
use PHPStan\ShouldNotHappenException;
18+
use function array_merge;
1619

1720
/**
1821
* @implements ExprHandler<FuncCall>
@@ -30,25 +33,44 @@ public function supports(Expr $expr): bool
3033
return $expr instanceof FuncCall;
3134
}
3235

33-
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
36+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context): Generator
3437
{
38+
$throwPoints = [];
39+
$impurePoints = [];
40+
$isAlwaysTerminating = false;
41+
$hasYield = false;
3542
if ($expr->name instanceof Expr) {
36-
$nameResult = yield new ExprAnalysisRequest($expr->name, $scope);
43+
$nameResult = yield new ExprAnalysisRequest($stmt, $expr->name, $scope, $context->enterDeep());
3744
$scope = $nameResult->scope;
45+
$throwPoints = $nameResult->throwPoints;
46+
$impurePoints = $nameResult->impurePoints;
47+
$isAlwaysTerminating = $nameResult->isAlwaysTerminating;
48+
$hasYield = $nameResult->hasYield;
3849
}
3950

4051
$argTypes = [];
4152

4253
foreach ($expr->getArgs() as $arg) {
43-
$argResult = yield new ExprAnalysisRequest($arg->value, $scope);
54+
$argResult = yield new ExprAnalysisRequest($stmt, $arg->value, $scope, $context->enterDeep());
4455
$argTypes[] = $argResult->type;
4556
$scope = $argResult->scope;
57+
$throwPoints = array_merge($throwPoints, $argResult->throwPoints);
58+
$impurePoints = array_merge($impurePoints, $argResult->impurePoints);
59+
$isAlwaysTerminating = $isAlwaysTerminating || $argResult->isAlwaysTerminating;
60+
$hasYield = $hasYield || $argResult->hasYield;
4661
}
4762

4863
if ($expr->name instanceof Name) {
4964
if ($this->reflectionProvider->hasFunction($expr->name, $scope)) {
5065
$function = $this->reflectionProvider->getFunction($expr->name, $scope);
51-
return new ExprAnalysisResult($function->getOnlyVariant()->getReturnType(), $scope);
66+
return new ExprAnalysisResult(
67+
$function->getOnlyVariant()->getReturnType(),
68+
$scope,
69+
hasYield: $hasYield,
70+
isAlwaysTerminating: $isAlwaysTerminating,
71+
throwPoints: $throwPoints,
72+
impurePoints: $impurePoints,
73+
);
5274
}
5375
}
5476

src/Analyser/Generator/ExprHandler/MethodCallHandler.php

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
use PhpParser\Node\Expr;
77
use PhpParser\Node\Expr\MethodCall;
88
use PhpParser\Node\Identifier;
9+
use PhpParser\Node\Stmt;
10+
use PHPStan\Analyser\ExpressionContext;
911
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
1012
use PHPStan\Analyser\Generator\ExprAnalysisResult;
1113
use PHPStan\Analyser\Generator\ExprHandler;
1214
use PHPStan\Analyser\Generator\GeneratorScope;
1315
use PHPStan\DependencyInjection\AutowiredService;
1416
use PHPStan\ShouldNotHappenException;
17+
use function array_merge;
1518

1619
/**
1720
* @implements ExprHandler<MethodCall>
@@ -25,25 +28,40 @@ public function supports(Expr $expr): bool
2528
return $expr instanceof MethodCall;
2629
}
2730

28-
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
31+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context): Generator
2932
{
3033
if (!$expr->name instanceof Identifier) {
3134
throw new ShouldNotHappenException('Not implemented');
3235
}
3336

34-
$varResult = yield new ExprAnalysisRequest($expr->var, $scope);
35-
$currentScope = $varResult->scope;
37+
$varResult = yield new ExprAnalysisRequest($stmt, $expr->var, $scope, $context->enterDeep());
38+
$throwPoints = $varResult->throwPoints;
39+
$impurePoints = $varResult->impurePoints;
40+
$isAlwaysTerminating = $varResult->isAlwaysTerminating;
41+
$hasYield = $varResult->hasYield;
42+
$scope = $varResult->scope;
3643
$argTypes = [];
3744

3845
foreach ($expr->getArgs() as $arg) {
39-
$argResult = yield new ExprAnalysisRequest($arg->value, $currentScope);
46+
$argResult = yield new ExprAnalysisRequest($stmt, $arg->value, $scope, $context->enterDeep());
4047
$argTypes[] = $argResult->type;
41-
$currentScope = $argResult->scope;
48+
$scope = $argResult->scope;
49+
$throwPoints = array_merge($throwPoints, $argResult->throwPoints);
50+
$impurePoints = array_merge($impurePoints, $argResult->impurePoints);
51+
$isAlwaysTerminating = $isAlwaysTerminating || $argResult->isAlwaysTerminating;
52+
$hasYield = $hasYield || $argResult->hasYield;
4253
}
4354

4455
if ($varResult->type->hasMethod($expr->name->toString())->yes()) {
4556
$method = $varResult->type->getMethod($expr->name->toString(), $scope);
46-
return new ExprAnalysisResult($method->getOnlyVariant()->getReturnType(), $scope);
57+
return new ExprAnalysisResult(
58+
$method->getOnlyVariant()->getReturnType(),
59+
$scope,
60+
hasYield: $hasYield,
61+
isAlwaysTerminating: $isAlwaysTerminating,
62+
throwPoints: $throwPoints,
63+
impurePoints: $impurePoints,
64+
);
4765
}
4866

4967
throw new ShouldNotHappenException('Not implemented');

0 commit comments

Comments
 (0)