|
3 | 3 | namespace PHPStan\Rules\PhpDoc; |
4 | 4 |
|
5 | 5 | use PhpParser\Node; |
6 | | -use PHPStan\Analyser\NodeScopeResolver; |
7 | 6 | use PHPStan\Analyser\Scope; |
| 7 | +use PHPStan\Node\InClassMethodNode; |
| 8 | +use PHPStan\Node\InFunctionNode; |
| 9 | +use PHPStan\Node\VirtualNode; |
8 | 10 | use PHPStan\Rules\RuleError; |
9 | 11 | use PHPStan\Rules\RuleErrorBuilder; |
10 | | -use PHPStan\Type\FileTypeMapper; |
11 | 12 | use PHPStan\Type\ObjectType; |
12 | 13 | use PHPStan\Type\Type; |
13 | 14 | use PHPStan\Type\VerbosityLevel; |
14 | 15 | use PHPStan\Type\VoidType; |
15 | 16 |
|
16 | 17 | /** |
17 | | - * @implements \PHPStan\Rules\Rule<\PhpParser\Node\FunctionLike> |
| 18 | + * @implements \PHPStan\Rules\Rule<VirtualNode> |
18 | 19 | */ |
19 | 20 | class InvalidThrowsPhpDocValueRule implements \PHPStan\Rules\Rule |
20 | 21 | { |
21 | 22 |
|
22 | | - /** @var FileTypeMapper */ |
23 | | - private $fileTypeMapper; |
24 | | - |
25 | | - /** @var NodeScopeResolver */ |
26 | | - private $nodeScopeResolver; |
27 | | - |
28 | | - public function __construct( |
29 | | - FileTypeMapper $fileTypeMapper, |
30 | | - NodeScopeResolver $nodeScopeResolver |
31 | | - ) |
32 | | - { |
33 | | - $this->fileTypeMapper = $fileTypeMapper; |
34 | | - $this->nodeScopeResolver = $nodeScopeResolver; |
35 | | - } |
36 | | - |
37 | 23 | public function getNodeType(): string |
38 | 24 | { |
39 | | - return \PhpParser\Node\FunctionLike::class; |
| 25 | + return VirtualNode::class; |
40 | 26 | } |
41 | 27 |
|
42 | 28 | public function processNode(Node $node, Scope $scope): array |
43 | 29 | { |
44 | | - $throwsType = $this->getThrowsType($node, $scope); |
45 | | - return $this->check($throwsType); |
46 | | - } |
47 | | - |
48 | | - private function getThrowsType(Node $node, Scope $scope): ?Type |
49 | | - { |
50 | | - return $node instanceof Node\Stmt\ClassMethod |
51 | | - ? $this->getMethodThrowsType($node, $scope) |
52 | | - : $this->getFunctionThrowsType($node, $scope); |
53 | | - } |
54 | | - |
55 | | - private function getFunctionThrowsType(Node $node, Scope $scope): ?Type |
56 | | - { |
57 | | - $docComment = $node->getDocComment(); |
58 | | - if ($docComment === null) { |
59 | | - return null; |
| 30 | + if (!$node instanceof InFunctionNode && !$node instanceof InClassMethodNode) { |
| 31 | + return []; |
60 | 32 | } |
61 | 33 |
|
62 | | - $functionName = null; |
63 | | - if ($node instanceof Node\Stmt\Function_) { |
64 | | - $functionName = trim($scope->getNamespace() . '\\' . $node->name->name, '\\'); |
| 34 | + if ($scope->getFunction() === null) { |
| 35 | + throw new \PHPStan\ShouldNotHappenException(); |
65 | 36 | } |
66 | 37 |
|
67 | | - $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc( |
68 | | - $scope->getFile(), |
69 | | - $scope->isInClass() ? $scope->getClassReflection()->getName() : null, |
70 | | - $scope->isInTrait() ? $scope->getTraitReflection()->getName() : null, |
71 | | - $functionName, |
72 | | - $docComment->getText() |
73 | | - ); |
| 38 | + $throwType = $scope->getFunction()->getThrowType(); |
74 | 39 |
|
75 | | - $throwsTag = $resolvedPhpDoc->getThrowsTag(); |
76 | | - return $throwsTag === null ? null : $throwsTag->getType(); |
77 | | - } |
78 | | - |
79 | | - private function getMethodThrowsType(Node\Stmt\ClassMethod $node, Scope $scope): ?Type |
80 | | - { |
81 | | - [, , , $phpDocThrowType] = $this->nodeScopeResolver->getPhpDocs($scope, $node); |
82 | | - return $phpDocThrowType; |
| 40 | + return $this->check($throwType); |
83 | 41 | } |
84 | 42 |
|
85 | 43 | /** |
86 | | - * @param Type|null $phpDocThrowsType |
| 44 | + * @param Type|null $phpDocThrowType |
87 | 45 | * @return array<int, RuleError> errors |
88 | 46 | */ |
89 | | - private function check(?Type $phpDocThrowsType): array |
| 47 | + private function check(?Type $phpDocThrowType): array |
90 | 48 | { |
91 | | - if ($phpDocThrowsType === null) { |
| 49 | + if ($phpDocThrowType === null) { |
92 | 50 | return []; |
93 | 51 | } |
94 | 52 |
|
95 | | - if ((new VoidType())->isSuperTypeOf($phpDocThrowsType)->yes()) { |
| 53 | + if ((new VoidType())->isSuperTypeOf($phpDocThrowType)->yes()) { |
96 | 54 | return []; |
97 | 55 | } |
98 | 56 |
|
99 | | - $isThrowsSuperType = (new ObjectType(\Throwable::class))->isSuperTypeOf($phpDocThrowsType); |
| 57 | + $isThrowsSuperType = (new ObjectType(\Throwable::class))->isSuperTypeOf($phpDocThrowType); |
100 | 58 | if ($isThrowsSuperType->yes()) { |
101 | 59 | return []; |
102 | 60 | } |
103 | 61 |
|
104 | 62 | return [ |
105 | 63 | RuleErrorBuilder::message(sprintf( |
106 | 64 | 'PHPDoc tag @throws with type %s is not subtype of Throwable', |
107 | | - $phpDocThrowsType->describe(VerbosityLevel::typeOnly()) |
| 65 | + $phpDocThrowType->describe(VerbosityLevel::typeOnly()) |
108 | 66 | ))->build(), |
109 | 67 | ]; |
110 | 68 | } |
|
0 commit comments