Skip to content

Commit 479e0dc

Browse files
committed
Thin GNSR
1 parent 6528880 commit 479e0dc

23 files changed

+750
-175
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Generator;
4+
5+
use Generator;
6+
use PhpParser\Node\Expr;
7+
8+
/**
9+
* @template T of Expr
10+
*/
11+
interface ExprHandler
12+
{
13+
14+
public const HANDLER_TAG = 'phpstan.gnsr.exprHandler';
15+
16+
/**
17+
* @phpstan-assert-if-true T $expr
18+
*/
19+
public function supports(Expr $expr): bool;
20+
21+
/**
22+
* @param T $expr
23+
* @return Generator<int, ExprAnalysisRequest|NodeCallbackRequest, ExprAnalysisResult, ExprAnalysisResult>
24+
*/
25+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator;
26+
27+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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\Expr\Assign;
8+
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
9+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
10+
use PHPStan\Analyser\Generator\ExprHandler;
11+
use PHPStan\Analyser\Generator\GeneratorScope;
12+
use PHPStan\DependencyInjection\AutowiredService;
13+
use PHPStan\ShouldNotHappenException;
14+
use function is_string;
15+
16+
/**
17+
* @implements ExprHandler<Assign>
18+
*/
19+
#[AutowiredService]
20+
final class AssignHandler implements ExprHandler
21+
{
22+
23+
public function supports(Expr $expr): bool
24+
{
25+
return $expr instanceof Assign;
26+
}
27+
28+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
29+
{
30+
if (!$expr->var instanceof Expr\Variable || !is_string($expr->var->name)) {
31+
throw new ShouldNotHappenException('Not implemented');
32+
}
33+
$variableName = $expr->var->name;
34+
$exprResult = yield new ExprAnalysisRequest($expr->expr, $scope);
35+
$varResult = yield new ExprAnalysisRequest($expr->var, $scope);
36+
37+
return new ExprAnalysisResult($exprResult->type, $varResult->scope->assignVariable($variableName, $exprResult->type));
38+
}
39+
40+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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\Expr\Cast\Int_;
8+
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
9+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
10+
use PHPStan\Analyser\Generator\ExprHandler;
11+
use PHPStan\Analyser\Generator\GeneratorScope;
12+
use PHPStan\DependencyInjection\AutowiredService;
13+
14+
/**
15+
* @implements ExprHandler<Int_>
16+
*/
17+
#[AutowiredService]
18+
final class CastIntHandler implements ExprHandler
19+
{
20+
21+
public function supports(Expr $expr): bool
22+
{
23+
return $expr instanceof Int_;
24+
}
25+
26+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
27+
{
28+
$exprResult = yield new ExprAnalysisRequest($expr->expr, $scope);
29+
30+
return new ExprAnalysisResult($exprResult->type->toInteger(), $scope);
31+
}
32+
33+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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\Expr\ClassConstFetch;
8+
use PhpParser\Node\Identifier;
9+
use PhpParser\Node\Name;
10+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
11+
use PHPStan\Analyser\Generator\ExprHandler;
12+
use PHPStan\Analyser\Generator\GeneratorScope;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\ShouldNotHappenException;
15+
use PHPStan\Type\Constant\ConstantStringType;
16+
17+
/**
18+
* @implements ExprHandler<ClassConstFetch>
19+
*/
20+
#[AutowiredService]
21+
final class ClassConstFetchHandler implements ExprHandler
22+
{
23+
24+
public function supports(Expr $expr): bool
25+
{
26+
return $expr instanceof ClassConstFetch;
27+
}
28+
29+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
30+
{
31+
if (
32+
$expr->class instanceof Name
33+
&& $expr->name instanceof Identifier
34+
&& $expr->name->toLowerString() === 'class'
35+
) {
36+
yield from [];
37+
return new ExprAnalysisResult(new ConstantStringType($expr->class->toString()), $scope);
38+
}
39+
40+
throw new ShouldNotHappenException('Not implemented');
41+
}
42+
43+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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\Expr\Closure;
8+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
9+
use PHPStan\Analyser\Generator\ExprHandler;
10+
use PHPStan\Analyser\Generator\GeneratorScope;
11+
use PHPStan\Analyser\Generator\StmtAnalysisRequest;
12+
use PHPStan\DependencyInjection\AutowiredService;
13+
use PHPStan\Type\ClosureType;
14+
15+
/**
16+
* @implements ExprHandler<Closure>
17+
*/
18+
#[AutowiredService]
19+
final class ClosureHandler implements ExprHandler
20+
{
21+
22+
public function supports(Expr $expr): bool
23+
{
24+
return $expr instanceof Closure;
25+
}
26+
27+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
28+
{
29+
foreach ($expr->stmts as $stmt) {
30+
$result = yield new StmtAnalysisRequest($stmt, $scope); // @phpstan-ignore generator.valueType
31+
$scope = $result->scope;
32+
}
33+
34+
return new ExprAnalysisResult(new ClosureType(), $scope);
35+
}
36+
37+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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\Expr\FuncCall;
8+
use PhpParser\Node\Name;
9+
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
10+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
11+
use PHPStan\Analyser\Generator\ExprHandler;
12+
use PHPStan\Analyser\Generator\GeneratorScope;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Reflection\ReflectionProvider;
15+
use PHPStan\ShouldNotHappenException;
16+
17+
/**
18+
* @implements ExprHandler<FuncCall>
19+
*/
20+
#[AutowiredService]
21+
final class FuncCallHandler implements ExprHandler
22+
{
23+
24+
public function __construct(private ReflectionProvider $reflectionProvider)
25+
{
26+
}
27+
28+
public function supports(Expr $expr): bool
29+
{
30+
return $expr instanceof FuncCall;
31+
}
32+
33+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
34+
{
35+
if ($expr->name instanceof Expr) {
36+
$nameResult = yield new ExprAnalysisRequest($expr->name, $scope);
37+
$scope = $nameResult->scope;
38+
}
39+
40+
$argTypes = [];
41+
42+
foreach ($expr->getArgs() as $arg) {
43+
$argResult = yield new ExprAnalysisRequest($arg->value, $scope);
44+
$argTypes[] = $argResult->type;
45+
$scope = $argResult->scope;
46+
}
47+
48+
if ($expr->name instanceof Name) {
49+
if ($this->reflectionProvider->hasFunction($expr->name, $scope)) {
50+
$function = $this->reflectionProvider->getFunction($expr->name, $scope);
51+
return new ExprAnalysisResult($function->getOnlyVariant()->getReturnType(), $scope);
52+
}
53+
}
54+
55+
throw new ShouldNotHappenException('Not implemented');
56+
}
57+
58+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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\Expr\MethodCall;
8+
use PhpParser\Node\Identifier;
9+
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
10+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
11+
use PHPStan\Analyser\Generator\ExprHandler;
12+
use PHPStan\Analyser\Generator\GeneratorScope;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\ShouldNotHappenException;
15+
16+
/**
17+
* @implements ExprHandler<MethodCall>
18+
*/
19+
#[AutowiredService]
20+
final class MethodCallHandler implements ExprHandler
21+
{
22+
23+
public function supports(Expr $expr): bool
24+
{
25+
return $expr instanceof MethodCall;
26+
}
27+
28+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
29+
{
30+
if (!$expr->name instanceof Identifier) {
31+
throw new ShouldNotHappenException('Not implemented');
32+
}
33+
34+
$varResult = yield new ExprAnalysisRequest($expr->var, $scope);
35+
$currentScope = $varResult->scope;
36+
$argTypes = [];
37+
38+
foreach ($expr->getArgs() as $arg) {
39+
$argResult = yield new ExprAnalysisRequest($arg->value, $currentScope);
40+
$argTypes[] = $argResult->type;
41+
$currentScope = $argResult->scope;
42+
}
43+
44+
if ($varResult->type->hasMethod($expr->name->toString())->yes()) {
45+
$method = $varResult->type->getMethod($expr->name->toString(), $scope);
46+
return new ExprAnalysisResult($method->getOnlyVariant()->getReturnType(), $scope);
47+
}
48+
49+
throw new ShouldNotHappenException('Not implemented');
50+
}
51+
52+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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\Expr\New_;
8+
use PhpParser\Node\Name;
9+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
10+
use PHPStan\Analyser\Generator\ExprHandler;
11+
use PHPStan\Analyser\Generator\GeneratorScope;
12+
use PHPStan\DependencyInjection\AutowiredService;
13+
use PHPStan\ShouldNotHappenException;
14+
use PHPStan\Type\ObjectType;
15+
16+
/**
17+
* @implements ExprHandler<New_>
18+
*/
19+
#[AutowiredService]
20+
final class NewHandler implements ExprHandler
21+
{
22+
23+
public function supports(Expr $expr): bool
24+
{
25+
return $expr instanceof New_;
26+
}
27+
28+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
29+
{
30+
if (!$expr->class instanceof Name) {
31+
throw new ShouldNotHappenException('Not implemented');
32+
}
33+
34+
yield from [];
35+
return new ExprAnalysisResult(new ObjectType($expr->class->toString()), $scope);
36+
}
37+
38+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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\Scalar\Int_;
8+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
9+
use PHPStan\Analyser\Generator\ExprHandler;
10+
use PHPStan\Analyser\Generator\GeneratorScope;
11+
use PHPStan\DependencyInjection\AutowiredService;
12+
use PHPStan\Type\Constant\ConstantIntegerType;
13+
14+
/**
15+
* @implements ExprHandler<Int_>
16+
*/
17+
#[AutowiredService]
18+
final class ScalarIntHandler implements ExprHandler
19+
{
20+
21+
public function supports(Expr $expr): bool
22+
{
23+
return $expr instanceof Int_;
24+
}
25+
26+
public function analyseExpr(Expr $expr, GeneratorScope $scope): Generator
27+
{
28+
yield from [];
29+
return new ExprAnalysisResult(new ConstantIntegerType($expr->value), $scope);
30+
}
31+
32+
}

0 commit comments

Comments
 (0)