3 declare(strict_types=1);
5 namespace CuyZ\Valinor\QA\PHPStan\Extension;
7 use CuyZ\Valinor\Mapper\TreeMapper;
8 use PhpParser\Node\Expr\MethodCall;
9 use PHPStan\Analyser\Scope;
10 use PHPStan\PhpDoc\TypeStringResolver;
11 use PHPStan\Reflection\MethodReflection;
12 use PHPStan\Type\ClassStringType;
13 use PHPStan\Type\Constant\ConstantStringType;
14 use PHPStan\Type\DynamicMethodReturnTypeExtension;
15 use PHPStan\Type\Generic\GenericClassStringType;
16 use PHPStan\Type\MixedType;
17 use PHPStan\Type\ObjectWithoutClassType;
18 use PHPStan\Type\Type;
19 use PHPStan\Type\UnionType;
21 final class TreeMapperPHPStanExtension implements DynamicMethodReturnTypeExtension
23 public function __construct(private TypeStringResolver $resolver) {}
25 public function getClass(): string
27 return TreeMapper::class;
30 public function isMethodSupported(MethodReflection $methodReflection): bool
32 return $methodReflection->getName() === 'map';
35 public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
37 $arguments = $methodCall->getArgs();
39 if (count($arguments) === 0) {
40 return new MixedType();
43 $type = $scope->getType($arguments[0]->value);
45 if ($type instanceof UnionType) {
46 return $type->traverse(fn (Type $type) => $this->type($type));
49 return $this->type($type);
52 private function type(Type $type): Type
54 if ($type instanceof GenericClassStringType) {
55 return $type->getGenericType();
58 if ($type instanceof ConstantStringType) {
59 return $this->resolver->resolve($type->getValue());
62 if ($type instanceof ClassStringType) {
63 return new ObjectWithoutClassType();
66 return new MixedType();