|
12 | 12 | use PHPStan\Rules\Rule;
|
13 | 13 | use PHPStan\Rules\RuleErrorBuilder;
|
14 | 14 | use PHPStan\ShouldNotHappenException;
|
| 15 | +use PHPStan\Type\ClosureType; |
15 | 16 | use PHPStan\Type\FileTypeMapper;
|
16 | 17 | use PHPStan\Type\Generic\TemplateType;
|
17 | 18 | use PHPStan\Type\Type;
|
18 | 19 | use PHPStan\Type\VerbosityLevel;
|
19 | 20 | use function array_merge;
|
| 21 | +use function in_array; |
20 | 22 | use function is_string;
|
21 | 23 | use function sprintf;
|
22 | 24 | use function trim;
|
@@ -68,10 +70,9 @@ public function processNode(Node $node, Scope $scope): array
|
68 | 70 |
|
69 | 71 | $errors = [];
|
70 | 72 |
|
71 |
| - foreach ([$resolvedPhpDoc->getParamTags(), $resolvedPhpDoc->getParamOutTags()] as $parameters) { |
| 73 | + foreach (['@param' => $resolvedPhpDoc->getParamTags(), '@param-out' => $resolvedPhpDoc->getParamOutTags(), '@param-closure-this' => $resolvedPhpDoc->getParamClosureThisTags()] as $tagName => $parameters) { |
72 | 74 | foreach ($parameters as $parameterName => $phpDocParamTag) {
|
73 | 75 | $phpDocParamType = $phpDocParamTag->getType();
|
74 |
| - $tagName = $phpDocParamTag instanceof ParamTag ? '@param' : '@param-out'; |
75 | 76 |
|
76 | 77 | if (!isset($nativeParameterTypes[$parameterName])) {
|
77 | 78 | $errors[] = RuleErrorBuilder::message(sprintf(
|
@@ -99,7 +100,6 @@ public function processNode(Node $node, Scope $scope): array
|
99 | 100 | ) {
|
100 | 101 | $phpDocParamType = $phpDocParamType->getIterableValueType();
|
101 | 102 | }
|
102 |
| - $isParamSuperType = $nativeParamType->isSuperTypeOf($phpDocParamType); |
103 | 103 |
|
104 | 104 | $escapedParameterName = SprintfHelper::escapeFormatString($parameterName);
|
105 | 105 | $escapedTagName = SprintfHelper::escapeFormatString($tagName);
|
@@ -160,28 +160,43 @@ public function processNode(Node $node, Scope $scope): array
|
160 | 160 | continue;
|
161 | 161 | }
|
162 | 162 |
|
163 |
| - if ($isParamSuperType->no()) { |
164 |
| - $errors[] = RuleErrorBuilder::message(sprintf( |
165 |
| - 'PHPDoc tag %s for parameter $%s with type %s is incompatible with native type %s.', |
166 |
| - $tagName, |
167 |
| - $parameterName, |
168 |
| - $phpDocParamType->describe(VerbosityLevel::typeOnly()), |
169 |
| - $nativeParamType->describe(VerbosityLevel::typeOnly()), |
170 |
| - ))->identifier('parameter.phpDocType')->build(); |
171 |
| - |
172 |
| - } elseif ($isParamSuperType->maybe()) { |
173 |
| - $errorBuilder = RuleErrorBuilder::message(sprintf( |
174 |
| - 'PHPDoc tag %s for parameter $%s with type %s is not subtype of native type %s.', |
175 |
| - $tagName, |
176 |
| - $parameterName, |
177 |
| - $phpDocParamType->describe(VerbosityLevel::typeOnly()), |
178 |
| - $nativeParamType->describe(VerbosityLevel::typeOnly()), |
179 |
| - ))->identifier('parameter.phpDocType'); |
180 |
| - if ($phpDocParamType instanceof TemplateType) { |
181 |
| - $errorBuilder->tip(sprintf('Write @template %s of %s to fix this.', $phpDocParamType->getName(), $nativeParamType->describe(VerbosityLevel::typeOnly()))); |
| 163 | + if (in_array($tagName, ['@param', '@param-out'], true)) { |
| 164 | + $isParamSuperType = $nativeParamType->isSuperTypeOf($phpDocParamType); |
| 165 | + if ($isParamSuperType->no()) { |
| 166 | + $errors[] = RuleErrorBuilder::message(sprintf( |
| 167 | + 'PHPDoc tag %s for parameter $%s with type %s is incompatible with native type %s.', |
| 168 | + $tagName, |
| 169 | + $parameterName, |
| 170 | + $phpDocParamType->describe(VerbosityLevel::typeOnly()), |
| 171 | + $nativeParamType->describe(VerbosityLevel::typeOnly()), |
| 172 | + ))->identifier('parameter.phpDocType')->build(); |
| 173 | + |
| 174 | + } elseif ($isParamSuperType->maybe()) { |
| 175 | + $errorBuilder = RuleErrorBuilder::message(sprintf( |
| 176 | + 'PHPDoc tag %s for parameter $%s with type %s is not subtype of native type %s.', |
| 177 | + $tagName, |
| 178 | + $parameterName, |
| 179 | + $phpDocParamType->describe(VerbosityLevel::typeOnly()), |
| 180 | + $nativeParamType->describe(VerbosityLevel::typeOnly()), |
| 181 | + ))->identifier('parameter.phpDocType'); |
| 182 | + if ($phpDocParamType instanceof TemplateType) { |
| 183 | + $errorBuilder->tip(sprintf('Write @template %s of %s to fix this.', $phpDocParamType->getName(), $nativeParamType->describe(VerbosityLevel::typeOnly()))); |
| 184 | + } |
| 185 | + |
| 186 | + $errors[] = $errorBuilder->build(); |
182 | 187 | }
|
| 188 | + } |
183 | 189 |
|
184 |
| - $errors[] = $errorBuilder->build(); |
| 190 | + if ($tagName === '@param-closure-this') { |
| 191 | + $isNonClosure = (new ClosureType())->isSuperTypeOf($nativeParamType)->no(); |
| 192 | + if ($isNonClosure) { |
| 193 | + $errors[] = RuleErrorBuilder::message(sprintf( |
| 194 | + 'PHPDoc tag %s is for parameter $%s with non-Closure type %s.', |
| 195 | + $tagName, |
| 196 | + $parameterName, |
| 197 | + $nativeParamType->describe(VerbosityLevel::typeOnly()), |
| 198 | + ))->identifier('paramClosureThis.nonClosure')->build(); |
| 199 | + } |
185 | 200 | }
|
186 | 201 | }
|
187 | 202 | }
|
|
0 commit comments