diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Property/MergePhpstanDocTagIntoNativeRector/Fixture/keep_native_description.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Property/MergePhpstanDocTagIntoNativeRector/Fixture/keep_native_description.php.inc new file mode 100644 index 00000000000..00a5fe9ea24 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Property/MergePhpstanDocTagIntoNativeRector/Fixture/keep_native_description.php.inc @@ -0,0 +1,60 @@ +|null $excludePattern + */ + public function layerPattern(string $name, string|array $pattern, string|array|null $excludePattern = null): self + { + } + + /** + * @return array some items + * @phpstan-return list + */ + public function items(): array + { + } + + /** + * @var array some values + * @phpstan-var list + */ + private $values; +} + +?> +----- +|null $excludePattern Optional regex; classes matching this are excluded + * from the layer even when $pattern matches. + */ + public function layerPattern(string $name, string|array $pattern, string|array|null $excludePattern = null): self + { + } + + /** + * @return list some items + */ + public function items(): array + { + } + + /** + * @var list some values + */ + private $values; +} + +?> diff --git a/rules/TypeDeclarationDocblocks/Rector/Property/MergePhpstanDocTagIntoNativeRector.php b/rules/TypeDeclarationDocblocks/Rector/Property/MergePhpstanDocTagIntoNativeRector.php index 0881d3cea05..c6c99ee51ad 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Property/MergePhpstanDocTagIntoNativeRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Property/MergePhpstanDocTagIntoNativeRector.php @@ -103,12 +103,23 @@ public function refactor(Node $node): ?Node $phpstanParamNames = []; $phpstanVarNames = []; $hasPhpstanReturn = false; + $nativeDescriptions = []; foreach ($phpDocNode->children as $phpDocChildNode) { if (! $phpDocChildNode instanceof PhpDocTagNode) { continue; } + // remember native tag descriptions, so they survive when the native tag is overruled + if (in_array($phpDocChildNode->name, self::PHPSTAN_TAG_TO_NATIVE_TAG, true)) { + $tagValueNode = $phpDocChildNode->value; + if (($tagValueNode instanceof ParamTagValueNode || $tagValueNode instanceof VarTagValueNode || $tagValueNode instanceof ReturnTagValueNode) && $tagValueNode->description !== '') { + $nativeDescriptions[$this->resolveTagKey($tagValueNode)] = $tagValueNode->description; + } + + continue; + } + if ($phpDocChildNode->name === '@phpstan-param' && $phpDocChildNode->value instanceof ParamTagValueNode) { $phpstanParamNames[] = $phpDocChildNode->value->parameterName; } elseif ($phpDocChildNode->name === '@phpstan-var' && $phpDocChildNode->value instanceof VarTagValueNode) { @@ -144,6 +155,12 @@ public function refactor(Node $node): ?Node // promote the @phpstan-* tag to its native counterpart $nativeTagName = self::PHPSTAN_TAG_TO_NATIVE_TAG[$phpDocChildNode->name] ?? null; if ($nativeTagName !== null) { + // keep the description of the overruled native tag + $tagValueNode = $phpDocChildNode->value; + if (($tagValueNode instanceof ParamTagValueNode || $tagValueNode instanceof VarTagValueNode || $tagValueNode instanceof ReturnTagValueNode) && $tagValueNode->description === '') { + $tagValueNode->description = $nativeDescriptions[$this->resolveTagKey($tagValueNode)] ?? ''; + } + $phpDocNode->children[$key] = new PhpDocTagNode($nativeTagName, $phpDocChildNode->value); $hasChanged = true; } @@ -195,4 +212,17 @@ private function isOverruledNativeTag( return false; } + + private function resolveTagKey(ParamTagValueNode|VarTagValueNode|ReturnTagValueNode $tagValueNode): string + { + if ($tagValueNode instanceof ParamTagValueNode) { + return '@param ' . $tagValueNode->parameterName; + } + + if ($tagValueNode instanceof VarTagValueNode) { + return '@var ' . $tagValueNode->variableName; + } + + return '@return'; + } }