mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-26 04:42:36 +00:00
improve class rename in doc blocks
This commit is contained in:
parent
056b0bbff9
commit
2c3d2616c6
|
@ -147,7 +147,8 @@
|
|||
"tests/Issues/Issue1243/Source/Twig_Error_Syntax.php",
|
||||
"tests/Issues/Issue1243/Source/Twig_Error_Runtime.php",
|
||||
"tests/Issues/Issue1243/Source/Twig_Loader_Array.php",
|
||||
"packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/MyBar.php"
|
||||
"packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/MyBar.php",
|
||||
"tests/Rector/Class_/RenameClassRector/Source/Twig_Extension_Sandbox.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -16,7 +16,7 @@ use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
|
|||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
|
||||
final class NodeTraverser
|
||||
final class PhpDocNodeTraverser
|
||||
{
|
||||
public function traverseWithCallable(PhpDocNode $phpDocNode, callable $callable): void
|
||||
{
|
|
@ -26,7 +26,7 @@ use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
|
|||
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
use Rector\BetterPhpDocParser\Ast\NodeTraverser;
|
||||
use Rector\BetterPhpDocParser\Ast\PhpDocNodeTraverser;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwareDeprecatedTagValueNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwareGenericTagValueNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwareInvalidTagValueNode;
|
||||
|
@ -57,13 +57,13 @@ use Rector\Exception\ShouldNotHappenException;
|
|||
final class AttributeAwareNodeFactory
|
||||
{
|
||||
/**
|
||||
* @var NodeTraverser
|
||||
* @var PhpDocNodeTraverser
|
||||
*/
|
||||
private $nodeTraverser;
|
||||
private $phpDocNodeTraverser;
|
||||
|
||||
public function __construct(NodeTraverser $nodeTraverser)
|
||||
public function __construct(PhpDocNodeTraverser $phpDocNodeTraverser)
|
||||
{
|
||||
$this->nodeTraverser = $nodeTraverser;
|
||||
$this->phpDocNodeTraverser = $phpDocNodeTraverser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,7 +76,7 @@ final class AttributeAwareNodeFactory
|
|||
}
|
||||
|
||||
if ($node instanceof PhpDocNode) {
|
||||
$this->nodeTraverser->traverseWithCallable($node, function (Node $node): AttributeAwareNodeInterface {
|
||||
$this->phpDocNodeTraverser->traverseWithCallable($node, function (Node $node): AttributeAwareNodeInterface {
|
||||
if ($node instanceof AttributeAwareNodeInterface) {
|
||||
return $node;
|
||||
}
|
||||
|
|
|
@ -127,11 +127,7 @@ CODE_SAMPLE
|
|||
|
||||
// process doc blocks
|
||||
if ($this->shouldImportDocBlocks) {
|
||||
$useImports = $this->docBlockManipulator->importNames($node);
|
||||
foreach ($useImports as $useImport) {
|
||||
$this->useAddingCommander->addUseImport($node, $useImport);
|
||||
}
|
||||
|
||||
$this->docBlockManipulator->importNames($node);
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,9 @@ final class ObjectToScalarDocBlockRectorTest extends AbstractRectorTestCase
|
|||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/nullable_property.php.inc',
|
||||
// __DIR__ . '/Fixture/nullable_property.php.inc',
|
||||
__DIR__ . '/Fixture/fixture2.php.inc',
|
||||
__DIR__ . '/Fixture/fixture3.php.inc',
|
||||
// __DIR__ . '/Fixture/fixture3.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,6 @@ final class ParamTypeResolver implements PerNodeTypeResolverInterface
|
|||
/** @var string $paramName */
|
||||
$paramName = $this->nameResolver->getName($param);
|
||||
|
||||
return $this->docBlockManipulator->getParamTypeByName($functionLike, $paramName);
|
||||
return $this->docBlockManipulator->getParamTypeByName($functionLike, '$' . $paramName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\PhpDocParser\Ast\Node as PhpDocParserNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\BetterPhpDocParser\Ast\PhpDocNodeTraverser;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\StaticTypeMapper;
|
||||
|
||||
final class DocBlockClassRenamer
|
||||
{
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
|
||||
/**
|
||||
* @var PhpDocNodeTraverser
|
||||
*/
|
||||
private $phpDocNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hasNodeChanged = false;
|
||||
|
||||
public function __construct(StaticTypeMapper $staticTypeMapper, PhpDocNodeTraverser $phpDocNodeTraverser)
|
||||
{
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->phpDocNodeTraverser = $phpDocNodeTraverser;
|
||||
}
|
||||
|
||||
public function renamePhpDocType(
|
||||
PhpDocNode $phpDocNode,
|
||||
Type $oldType,
|
||||
Type $newType,
|
||||
Node $phpParserNode
|
||||
): bool {
|
||||
$this->phpDocNodeTraverser->traverseWithCallable(
|
||||
$phpDocNode,
|
||||
function (PhpDocParserNode $node) use ($phpParserNode, $oldType, $newType): PhpDocParserNode {
|
||||
if (! $node instanceof IdentifierTypeNode) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
$staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($node, $phpParserNode);
|
||||
if (! $staticType->equals($oldType)) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
$newIdentifierType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
|
||||
if ($newIdentifierType === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$this->hasNodeChanged = true;
|
||||
|
||||
return $newIdentifierType;
|
||||
}
|
||||
);
|
||||
|
||||
return $this->hasNodeChanged;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ use PhpParser\Comment\Doc;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PHPStan\PhpDocParser\Ast\Node as PhpDocParserNode;
|
||||
|
@ -20,7 +19,7 @@ use PHPStan\Type\MixedType;
|
|||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\BetterPhpDocParser\Annotation\AnnotationNaming;
|
||||
use Rector\BetterPhpDocParser\Ast\NodeTraverser;
|
||||
use Rector\BetterPhpDocParser\Ast\PhpDocNodeTraverser;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\AttributeAwareNodeFactory;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocTagNode;
|
||||
|
@ -29,29 +28,17 @@ use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\Type\AttributeAwareIdentifie
|
|||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter;
|
||||
use Rector\CodingStyle\Application\UseAddingCommander;
|
||||
use Rector\CodingStyle\Imports\ImportSkipper;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineRelationTagValueNodeInterface;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\NodeTypeResolver\Exception\MissingTagException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\StaticTypeMapper;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
|
||||
/**
|
||||
* @see \Rector\NodeTypeResolver\Tests\PhpDoc\NodeAnalyzer\DocBlockManipulatorTest
|
||||
*/
|
||||
final class DocBlockManipulator
|
||||
{
|
||||
/**
|
||||
* @var bool[][]
|
||||
*/
|
||||
private $usedShortNameByClasses = [];
|
||||
|
||||
/**
|
||||
* @var PhpDocInfoFactory
|
||||
*/
|
||||
|
@ -68,24 +55,9 @@ final class DocBlockManipulator
|
|||
private $attributeAwareNodeFactory;
|
||||
|
||||
/**
|
||||
* @var NodeTraverser
|
||||
* @var PhpDocNodeTraverser
|
||||
*/
|
||||
private $nodeTraverser;
|
||||
|
||||
/**
|
||||
* @var UseAddingCommander
|
||||
*/
|
||||
private $useAddingCommander;
|
||||
|
||||
/**
|
||||
* @var BetterStandardPrinter
|
||||
*/
|
||||
private $betterStandardPrinter;
|
||||
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
private $phpDocNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
|
@ -98,30 +70,31 @@ final class DocBlockManipulator
|
|||
private $hasPhpDocChanged = false;
|
||||
|
||||
/**
|
||||
* @var ImportSkipper
|
||||
* @var DocBlockClassRenamer
|
||||
*/
|
||||
private $importSkipper;
|
||||
private $docBlockClassRenamer;
|
||||
|
||||
/**
|
||||
* @var DocBlockNameImporter
|
||||
*/
|
||||
private $docBlockNameImporter;
|
||||
|
||||
public function __construct(
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
PhpDocInfoPrinter $phpDocInfoPrinter,
|
||||
AttributeAwareNodeFactory $attributeAwareNodeFactory,
|
||||
NodeTraverser $nodeTraverser,
|
||||
NameResolver $nameResolver,
|
||||
UseAddingCommander $useAddingCommander,
|
||||
BetterStandardPrinter $betterStandardPrinter,
|
||||
PhpDocNodeTraverser $phpDocNodeTraverser,
|
||||
StaticTypeMapper $staticTypeMapper,
|
||||
ImportSkipper $importSkipper
|
||||
DocBlockClassRenamer $docBlockClassRenamer,
|
||||
DocBlockNameImporter $docBlockNameImporter
|
||||
) {
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->phpDocInfoPrinter = $phpDocInfoPrinter;
|
||||
$this->attributeAwareNodeFactory = $attributeAwareNodeFactory;
|
||||
$this->nodeTraverser = $nodeTraverser;
|
||||
$this->useAddingCommander = $useAddingCommander;
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->phpDocNodeTraverser = $phpDocNodeTraverser;
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->importSkipper = $importSkipper;
|
||||
$this->docBlockClassRenamer = $docBlockClassRenamer;
|
||||
$this->docBlockNameImporter = $docBlockNameImporter;
|
||||
}
|
||||
|
||||
public function hasTag(Node $node, string $name): bool
|
||||
|
@ -176,13 +149,16 @@ final class DocBlockManipulator
|
|||
}
|
||||
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
$this->renamePhpDocType($phpDocInfo->getPhpDocNode(), $oldType, $newType, $node);
|
||||
$hasNodeChanged = $this->docBlockClassRenamer->renamePhpDocType(
|
||||
$phpDocInfo->getPhpDocNode(),
|
||||
$oldType,
|
||||
$newType,
|
||||
$node
|
||||
);
|
||||
|
||||
if ($this->hasPhpDocChanged === false) {
|
||||
return;
|
||||
if ($hasNodeChanged) {
|
||||
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
|
||||
}
|
||||
|
||||
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
|
||||
}
|
||||
|
||||
public function replaceAnnotationInNode(Node $node, string $oldAnnotation, string $newAnnotation): void
|
||||
|
@ -262,6 +238,9 @@ final class DocBlockManipulator
|
|||
|
||||
$this->removeTagFromNode($node, 'var', true);
|
||||
$this->addTypeSpecificTag($node, 'var', $newType);
|
||||
|
||||
// to invoke the node override
|
||||
$node->setAttribute(AttributeKey::ORIGINAL_NODE, null);
|
||||
}
|
||||
|
||||
public function addReturnTag(Node $node, Type $newType): void
|
||||
|
@ -361,71 +340,18 @@ final class DocBlockManipulator
|
|||
}
|
||||
}
|
||||
|
||||
public function renamePhpDocType(PhpDocNode $phpDocNode, Type $oldType, Type $newType, Node $node): PhpDocNode
|
||||
{
|
||||
$phpParserNode = $node;
|
||||
|
||||
$this->nodeTraverser->traverseWithCallable(
|
||||
$phpDocNode,
|
||||
function (PhpDocParserNode $node) use ($phpParserNode, $oldType, $newType): PhpDocParserNode {
|
||||
if (! $node instanceof IdentifierTypeNode) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
$staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($node, $phpParserNode);
|
||||
if ($staticType->equals($oldType)) {
|
||||
$newIdentifierType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
|
||||
|
||||
if ($newIdentifierType === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
// $node->name = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
|
||||
$this->hasPhpDocChanged = true;
|
||||
return $newIdentifierType;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
);
|
||||
|
||||
return $phpDocNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FullyQualifiedObjectType[]
|
||||
*/
|
||||
public function importNames(Node $node): array
|
||||
public function importNames(Node $node): void
|
||||
{
|
||||
if ($node->getDocComment() === null) {
|
||||
return [];
|
||||
return;
|
||||
}
|
||||
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
$phpDocNode = $phpDocInfo->getPhpDocNode();
|
||||
$phpParserNode = $node;
|
||||
$hasNodeChanged = $this->docBlockNameImporter->importNames($phpDocInfo, $node);
|
||||
|
||||
$this->nodeTraverser->traverseWithCallable($phpDocNode, function (PhpDocParserNode $docNode) use (
|
||||
$node,
|
||||
$phpParserNode
|
||||
): PhpDocParserNode {
|
||||
if (! $docNode instanceof IdentifierTypeNode) {
|
||||
return $docNode;
|
||||
}
|
||||
|
||||
$staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($docNode, $phpParserNode);
|
||||
if (! $staticType instanceof FullyQualifiedObjectType) {
|
||||
return $docNode;
|
||||
}
|
||||
|
||||
return $this->processFqnNameImport($node, $docNode, $staticType);
|
||||
});
|
||||
|
||||
if ($this->hasPhpDocChanged) {
|
||||
if ($hasNodeChanged) {
|
||||
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -441,7 +367,7 @@ final class DocBlockManipulator
|
|||
$phpDocNode = $phpDocInfo->getPhpDocNode();
|
||||
$phpParserNode = $node;
|
||||
|
||||
$this->nodeTraverser->traverseWithCallable($phpDocNode, function (PhpDocParserNode $node) use (
|
||||
$this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, function (PhpDocParserNode $node) use (
|
||||
$namespacePrefix,
|
||||
$excludedClasses,
|
||||
$phpParserNode
|
||||
|
@ -490,7 +416,24 @@ final class DocBlockManipulator
|
|||
return false;
|
||||
}
|
||||
|
||||
return (bool) Strings::match($docComment->getText(), '#\@(param|throws|return|var)\b#');
|
||||
if ((bool) Strings::match($docComment->getText(), '#\@(param|throws|return|var)\b#')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
|
||||
// has any type node?
|
||||
|
||||
foreach ($phpDocInfo->getPhpDocNode()->children as $phpDocChildNode) {
|
||||
if ($phpDocChildNode instanceof PhpDocTagNode) {
|
||||
// is custom class, it can contain some type info
|
||||
if (Strings::startsWith(get_class($phpDocChildNode->value), 'Rector\\')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updateNodeWithPhpDocInfo(
|
||||
|
@ -550,6 +493,8 @@ final class DocBlockManipulator
|
|||
|
||||
public function getParamTypeByName(FunctionLike $functionLike, string $paramName): Type
|
||||
{
|
||||
$this->ensureParamNameStartsWithDollar($paramName, __METHOD__);
|
||||
|
||||
$paramTypes = $this->getParamTypesByName($functionLike);
|
||||
return $paramTypes[$paramName] ?? new MixedType();
|
||||
}
|
||||
|
@ -583,70 +528,6 @@ final class DocBlockManipulator
|
|||
}
|
||||
}
|
||||
|
||||
private function processFqnNameImport(
|
||||
Node $node,
|
||||
IdentifierTypeNode $identifierTypeNode,
|
||||
FullyQualifiedObjectType $fullyQualifiedObjectType
|
||||
): PhpDocParserNode {
|
||||
// nothing to be changed → skip
|
||||
if ($this->hasTheSameShortClassInCurrentNamespace($node, $fullyQualifiedObjectType)) {
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
if ($this->importSkipper->shouldSkipName($node, $fullyQualifiedObjectType)) {
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
if ($this->useAddingCommander->isShortImported($node, $fullyQualifiedObjectType)) {
|
||||
if ($this->useAddingCommander->isImportShortable($node, $fullyQualifiedObjectType)) {
|
||||
$identifierTypeNode->name = $fullyQualifiedObjectType->getShortName();
|
||||
$this->hasPhpDocChanged = true;
|
||||
}
|
||||
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
$identifierTypeNode->name = $fullyQualifiedObjectType->getShortName();
|
||||
$this->hasPhpDocChanged = true;
|
||||
$this->useAddingCommander->addUseImport($node, $fullyQualifiedObjectType);
|
||||
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
private function isCurrentNamespaceSameShortClassAlreadyUsed(
|
||||
Node $node,
|
||||
string $fullyQualifiedName,
|
||||
ShortenedObjectType $shortenedObjectType
|
||||
): bool {
|
||||
/** @var ClassLike|null $classNode */
|
||||
$classNode = $node->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
// cannot say, so rather yes
|
||||
return true;
|
||||
}
|
||||
|
||||
$className = $this->nameResolver->getName($classNode);
|
||||
|
||||
if (isset($this->usedShortNameByClasses[$className][$shortenedObjectType->getShortName()])) {
|
||||
return $this->usedShortNameByClasses[$className][$shortenedObjectType->getShortName()];
|
||||
}
|
||||
|
||||
$printedClass = $this->betterStandardPrinter->print($classNode->stmts);
|
||||
|
||||
// short with space " Type"| fqn
|
||||
$shortNameOrFullyQualifiedNamePattern = sprintf(
|
||||
'#(\s%s\b|\b%s\b)#',
|
||||
preg_quote($shortenedObjectType->getShortName()),
|
||||
preg_quote($fullyQualifiedName)
|
||||
);
|
||||
|
||||
$isShortClassUsed = (bool) Strings::match($printedClass, $shortNameOrFullyQualifiedNamePattern);
|
||||
|
||||
$this->usedShortNameByClasses[$className][$shortenedObjectType->getShortName()] = $isShortClassUsed;
|
||||
|
||||
return $isShortClassUsed;
|
||||
}
|
||||
|
||||
private function areTypesEquals(Type $firstType, Type $secondType): bool
|
||||
{
|
||||
return $this->staticTypeMapper->createTypeHash($firstType) === $this->staticTypeMapper->createTypeHash(
|
||||
|
@ -654,35 +535,16 @@ final class DocBlockManipulator
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The class in the same namespace as different file can se used in this code, the short names would colide → skip
|
||||
*
|
||||
* E.g. this namespace:
|
||||
* App\Product
|
||||
*
|
||||
* And the FQN:
|
||||
* App\SomeNesting\Product
|
||||
*/
|
||||
private function hasTheSameShortClassInCurrentNamespace(
|
||||
Node $node,
|
||||
FullyQualifiedObjectType $fullyQualifiedObjectType
|
||||
): bool {
|
||||
// the name is already in the same namespace implicitly
|
||||
$namespaceName = $node->getAttribute(AttributeKey::NAMESPACE_NAME);
|
||||
$currentNamespaceShortName = $namespaceName . '\\' . $fullyQualifiedObjectType->getShortName();
|
||||
|
||||
if (ClassExistenceStaticHelper::doesClassLikeExist($currentNamespaceShortName)) {
|
||||
return false;
|
||||
private function ensureParamNameStartsWithDollar(string $paramName, string $location): void
|
||||
{
|
||||
if (Strings::startsWith($paramName, '$')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($currentNamespaceShortName === $fullyQualifiedObjectType->getClassName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isCurrentNamespaceSameShortClassAlreadyUsed(
|
||||
$node,
|
||||
$currentNamespaceShortName,
|
||||
$fullyQualifiedObjectType->getShortNameType()
|
||||
);
|
||||
throw new ShouldNotHappenException(sprintf(
|
||||
'Param name "%s" must start with "$" in "%s()" method.',
|
||||
$paramName,
|
||||
$location
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PHPStan\PhpDocParser\Ast\Node as PhpDocParserNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\Ast\PhpDocNodeTraverser;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\CodingStyle\Application\UseAddingCommander;
|
||||
use Rector\CodingStyle\Imports\ImportSkipper;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\StaticTypeMapper;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
|
||||
final class DocBlockNameImporter
|
||||
{
|
||||
/**
|
||||
* @var PhpDocNodeTraverser
|
||||
*/
|
||||
private $phpDocNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
|
||||
/**
|
||||
* @var UseAddingCommander
|
||||
*/
|
||||
private $useAddingCommander;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hasPhpDocChanged = false;
|
||||
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
/**
|
||||
* @var bool[][]
|
||||
*/
|
||||
private $usedShortNameByClasses = [];
|
||||
|
||||
/**
|
||||
* @var BetterStandardPrinter
|
||||
*/
|
||||
private $betterStandardPrinter;
|
||||
|
||||
/**
|
||||
* @var ImportSkipper
|
||||
*/
|
||||
private $importSkipper;
|
||||
|
||||
public function __construct(
|
||||
PhpDocNodeTraverser $phpDocNodeTraverser,
|
||||
StaticTypeMapper $staticTypeMapper,
|
||||
UseAddingCommander $useAddingCommander,
|
||||
NameResolver $nameResolver,
|
||||
BetterStandardPrinter $betterStandardPrinter,
|
||||
ImportSkipper $importSkipper
|
||||
) {
|
||||
$this->phpDocNodeTraverser = $phpDocNodeTraverser;
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->useAddingCommander = $useAddingCommander;
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
$this->importSkipper = $importSkipper;
|
||||
}
|
||||
|
||||
public function importNames(PhpDocInfo $phpDocInfo, Node $phpParserNode): bool
|
||||
{
|
||||
$phpDocNode = $phpDocInfo->getPhpDocNode();
|
||||
|
||||
$this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, function (PhpDocParserNode $docNode) use (
|
||||
$phpParserNode
|
||||
): PhpDocParserNode {
|
||||
if (! $docNode instanceof IdentifierTypeNode) {
|
||||
return $docNode;
|
||||
}
|
||||
|
||||
$staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($docNode, $phpParserNode);
|
||||
if (! $staticType instanceof FullyQualifiedObjectType) {
|
||||
return $docNode;
|
||||
}
|
||||
|
||||
return $this->processFqnNameImport($phpParserNode, $docNode, $staticType);
|
||||
});
|
||||
|
||||
return $this->hasPhpDocChanged;
|
||||
}
|
||||
|
||||
private function processFqnNameImport(
|
||||
Node $node,
|
||||
IdentifierTypeNode $identifierTypeNode,
|
||||
FullyQualifiedObjectType $fullyQualifiedObjectType
|
||||
): PhpDocParserNode {
|
||||
// nothing to be changed → skip
|
||||
if ($this->hasTheSameShortClassInCurrentNamespace($node, $fullyQualifiedObjectType)) {
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
if ($this->importSkipper->shouldSkipName($node, $fullyQualifiedObjectType)) {
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
if ($this->useAddingCommander->isShortImported($node, $fullyQualifiedObjectType)) {
|
||||
if ($this->useAddingCommander->isImportShortable($node, $fullyQualifiedObjectType)) {
|
||||
$identifierTypeNode->name = $fullyQualifiedObjectType->getShortName();
|
||||
$this->hasPhpDocChanged = true;
|
||||
}
|
||||
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
$identifierTypeNode->name = $fullyQualifiedObjectType->getShortName();
|
||||
$this->hasPhpDocChanged = true;
|
||||
$this->useAddingCommander->addUseImport($node, $fullyQualifiedObjectType);
|
||||
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The class in the same namespace as different file can se used in this code, the short names would colide → skip
|
||||
*
|
||||
* E.g. this namespace:
|
||||
* App\Product
|
||||
*
|
||||
* And the FQN:
|
||||
* App\SomeNesting\Product
|
||||
*/
|
||||
private function hasTheSameShortClassInCurrentNamespace(
|
||||
Node $node,
|
||||
FullyQualifiedObjectType $fullyQualifiedObjectType
|
||||
): bool {
|
||||
// the name is already in the same namespace implicitly
|
||||
$namespaceName = $node->getAttribute(AttributeKey::NAMESPACE_NAME);
|
||||
$currentNamespaceShortName = $namespaceName . '\\' . $fullyQualifiedObjectType->getShortName();
|
||||
|
||||
if (ClassExistenceStaticHelper::doesClassLikeExist($currentNamespaceShortName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($currentNamespaceShortName === $fullyQualifiedObjectType->getClassName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isCurrentNamespaceSameShortClassAlreadyUsed(
|
||||
$node,
|
||||
$currentNamespaceShortName,
|
||||
$fullyQualifiedObjectType->getShortNameType()
|
||||
);
|
||||
}
|
||||
|
||||
private function isCurrentNamespaceSameShortClassAlreadyUsed(
|
||||
Node $node,
|
||||
string $fullyQualifiedName,
|
||||
ShortenedObjectType $shortenedObjectType
|
||||
): bool {
|
||||
/** @var ClassLike|null $classNode */
|
||||
$classNode = $node->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
// cannot say, so rather yes
|
||||
return true;
|
||||
}
|
||||
|
||||
$className = $this->nameResolver->getName($classNode);
|
||||
|
||||
if (isset($this->usedShortNameByClasses[$className][$shortenedObjectType->getShortName()])) {
|
||||
return $this->usedShortNameByClasses[$className][$shortenedObjectType->getShortName()];
|
||||
}
|
||||
|
||||
$printedClass = $this->betterStandardPrinter->print($classNode->stmts);
|
||||
|
||||
// short with space " Type"| fqn
|
||||
$shortNameOrFullyQualifiedNamePattern = sprintf(
|
||||
'#(\s%s\b|\b%s\b)#',
|
||||
preg_quote($shortenedObjectType->getShortName()),
|
||||
preg_quote($fullyQualifiedName)
|
||||
);
|
||||
|
||||
$isShortClassUsed = (bool) Strings::match($printedClass, $shortNameOrFullyQualifiedNamePattern);
|
||||
|
||||
$this->usedShortNameByClasses[$className][$shortenedObjectType->getShortName()] = $isShortClassUsed;
|
||||
|
||||
return $isShortClassUsed;
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer;
|
||||
|
||||
use Nette\Utils\Reflection;
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocTagNode;
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNodeDecoratorInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Changes @Inject name to @Full\Namespace\Inject
|
||||
*/
|
||||
final class FqnAnnotationTypeDecorator implements PhpDocNodeDecoratorInterface
|
||||
{
|
||||
public function decorate(AttributeAwarePhpDocNode $attributeAwarePhpDocNode, Node $node): AttributeAwarePhpDocNode
|
||||
{
|
||||
foreach ($attributeAwarePhpDocNode->children as $phpDocChildNode) {
|
||||
if (! $phpDocChildNode instanceof AttributeAwarePhpDocTagNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tagShortName = ltrim($phpDocChildNode->name, '@');
|
||||
|
||||
// probably not a class like type
|
||||
if (ctype_lower($tagShortName[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tagShortName = $this->joinWithValue($phpDocChildNode, $tagShortName);
|
||||
$tagFqnName = $this->resolveTagFqnName($node, $tagShortName);
|
||||
|
||||
$phpDocChildNode->setAttribute('annotation_class', $tagFqnName);
|
||||
}
|
||||
|
||||
return $attributeAwarePhpDocNode;
|
||||
}
|
||||
|
||||
private function joinWithValue(PhpDocTagNode $phpDocTagNode, string $tagShortName): string
|
||||
{
|
||||
$innerValue = (string) $phpDocTagNode->value;
|
||||
|
||||
if (! Strings::startsWith($innerValue, '\\')) {
|
||||
return $tagShortName;
|
||||
}
|
||||
|
||||
// drop () args
|
||||
if (Strings::contains($innerValue, '(')) {
|
||||
return $tagShortName . Strings::before($innerValue, '(');
|
||||
}
|
||||
|
||||
return $tagShortName . $innerValue;
|
||||
}
|
||||
|
||||
private function resolveTagFqnName(Node $node, string $tagShortName): string
|
||||
{
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if (! $className) {
|
||||
return $tagShortName;
|
||||
}
|
||||
|
||||
// @todo use Use[] nodes?
|
||||
|
||||
return Reflection::expandClassName($tagShortName, new ReflectionClass($className));
|
||||
}
|
||||
}
|
|
@ -313,8 +313,7 @@ final class StaticTypeMapper
|
|||
}
|
||||
|
||||
if ($phpStanType instanceof ShortenedObjectType) {
|
||||
// no preslash for alias
|
||||
return $phpStanType->getFullyQualifiedName();
|
||||
return '\\' . $phpStanType->getFullyQualifiedName();
|
||||
}
|
||||
|
||||
if ($phpStanType instanceof FullyQualifiedObjectType) {
|
||||
|
@ -463,6 +462,10 @@ final class StaticTypeMapper
|
|||
|
||||
public function createTypeHash(Type $type): string
|
||||
{
|
||||
if ($type instanceof MixedType) {
|
||||
return serialize($type);
|
||||
}
|
||||
|
||||
if ($type instanceof ArrayType) {
|
||||
// @todo sort to make different order identical
|
||||
return $this->createTypeHash($type->getItemType()) . '[]';
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ParamTypeResolver;
|
||||
|
||||
use Iterator;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Param;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\AbstractNodeTypeResolverTest;
|
||||
|
@ -19,9 +19,10 @@ final class ParamTypeResolverTest extends AbstractNodeTypeResolverTest
|
|||
*/
|
||||
public function test(string $file, int $nodePosition, Type $expectedType): void
|
||||
{
|
||||
$variableNodes = $this->getNodesForFileOfType($file, Variable::class);
|
||||
$variableNodes = $this->getNodesForFileOfType($file, Param::class);
|
||||
|
||||
$this->assertEquals($expectedType, $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]));
|
||||
$resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]);
|
||||
$this->assertEquals($expectedType, $resolvedType);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
/**
|
||||
* @param \PHP\Filter[] $phpFilters
|
||||
* @param \PHP\Filter[]|null $phpFilters2
|
||||
* @param \PHP\Filter|null $phpFilters3
|
||||
* @param \PHP\Filter|null $phpFilters4
|
||||
*/
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* @param \PHP\Filter $phpFilter
|
||||
* @return \PHP\Filter|null
|
||||
*/
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
use PHP\Filter;
|
||||
|
||||
/**
|
||||
* @param \AnotherFilter $phpFilter
|
||||
*/
|
||||
function some($phpFilter)
|
||||
{
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
/**
|
||||
* @param PHP_Filter[] $phpFilters
|
||||
* @param PHP_Filter[]|null $phpFilters2
|
||||
* @param PHP_Filter|null $phpFilters3
|
||||
* @param \PHP_Filter|null $phpFilters4
|
||||
*/
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* @param PHP_Filter $phpFilter
|
||||
* @return PHP_Filter|null
|
||||
*/
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
use PHP\Filter;
|
||||
|
||||
/**
|
||||
* @param Filter $phpFilter
|
||||
*/
|
||||
function some($phpFilter)
|
||||
{
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Tests\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
|
||||
use Iterator;
|
||||
use Nette\Utils\FileSystem;
|
||||
use PhpParser\Comment\Doc;
|
||||
use PhpParser\Node\Stmt\Nop;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter;
|
||||
use Rector\HttpKernel\RectorKernel;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Symplify\PackageBuilder\Tests\AbstractKernelTestCase;
|
||||
|
||||
final class ReplaceTest extends AbstractKernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var PhpDocInfoFactory
|
||||
*/
|
||||
private $phpDocInfoFactory;
|
||||
|
||||
/**
|
||||
* @var PhpDocInfoPrinter
|
||||
*/
|
||||
private $phpDocInfoPrinter;
|
||||
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
*/
|
||||
private $docBlockManipulator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->bootKernel(RectorKernel::class);
|
||||
|
||||
$this->phpDocInfoFactory = self::$container->get(PhpDocInfoFactory::class);
|
||||
$this->phpDocInfoPrinter = self::$container->get(PhpDocInfoPrinter::class);
|
||||
$this->docBlockManipulator = self::$container->get(DocBlockManipulator::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(string $originalFile, Type $oldType, Type $newType, string $expectedFile): void
|
||||
{
|
||||
$phpDocInfo = $this->createPhpDocInfoFromFile($originalFile);
|
||||
|
||||
$node = new Nop();
|
||||
$node->setDocComment(new Doc(Filesystem::read($originalFile)));
|
||||
|
||||
$this->docBlockManipulator->renamePhpDocType($phpDocInfo->getPhpDocNode(), $oldType, $newType, $node);
|
||||
|
||||
$newPhpDocContent = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo);
|
||||
$this->assertStringEqualsFile($expectedFile, $newPhpDocContent);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$oldObjectType = new ObjectType('PHP_Filter');
|
||||
$newObjectType = new ObjectType('PHP\Filter');
|
||||
|
||||
yield [
|
||||
__DIR__ . '/ReplaceSource/before.txt',
|
||||
$oldObjectType,
|
||||
$newObjectType,
|
||||
__DIR__ . '/ReplaceSource/after.txt',
|
||||
];
|
||||
yield [
|
||||
__DIR__ . '/ReplaceSource/before2.txt',
|
||||
$oldObjectType,
|
||||
$newObjectType,
|
||||
__DIR__ . '/ReplaceSource/after2.txt',
|
||||
];
|
||||
}
|
||||
|
||||
private function createPhpDocInfoFromFile(string $originalFile): PhpDocInfo
|
||||
{
|
||||
$docContent = FileSystem::read($originalFile);
|
||||
$node = new Nop();
|
||||
$node->setDocComment(new Doc($docContent));
|
||||
|
||||
return $this->phpDocInfoFactory->createFromNode($node);
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ namespace Rector\PHPUnit\Tests\Rector\Class_\ArrayArgumentInTestToDataProviderRe
|
|||
class VariousTypesTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @param float|int|string $variable
|
||||
* @param int|string|float $variable
|
||||
* @dataProvider provideDataForTest()
|
||||
*/
|
||||
public function test($variable)
|
||||
|
@ -27,7 +27,7 @@ class VariousTypesTest extends \PHPUnit\Framework\TestCase
|
|||
$this->doTestSingle($variable);
|
||||
}
|
||||
/**
|
||||
* @return float|int|string
|
||||
* @return int|string|float
|
||||
*/
|
||||
public function provideDataForTest(): iterable
|
||||
{
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace Rector\Php\Rector\Property;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Type\MixedType;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
|
@ -37,7 +36,7 @@ final class CompleteVarDocTypePropertyRector extends AbstractRector
|
|||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Complete property `@var` annotations for missing one, yet known.', [
|
||||
return new RectorDefinition('Complete property `@var` annotations or correct the old ones', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
|
@ -82,22 +81,12 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
// @todo use property type resolver
|
||||
$varType = $this->docBlockManipulator->getVarType($node);
|
||||
|
||||
// already completed
|
||||
if (! $varType instanceof MixedType) {
|
||||
$propertyType = $this->propertyTypeInferer->inferProperty($node);
|
||||
if ($propertyType instanceof MixedType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$varType = $this->propertyTypeInferer->inferProperty($node);
|
||||
if ($varType instanceof MixedType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->docBlockManipulator->changeVarTag($node, $varType);
|
||||
|
||||
$node->setAttribute(AttributeKey::ORIGINAL_NODE, null);
|
||||
$this->docBlockManipulator->changeVarTag($node, $propertyType);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
|
|
@ -2,22 +2,29 @@
|
|||
|
||||
namespace Rector\Php\Tests\Rector\Property\CompleteVarDocTypePropertyRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Php\Rector\Property\CompleteVarDocTypePropertyRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class CompleteVarDocTypePropertyRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
/**
|
||||
* @dataProvider provideDataForTest()
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/property_assign.php.inc',
|
||||
__DIR__ . '/Fixture/default_value.php.inc',
|
||||
__DIR__ . '/Fixture/assign_conflict.php.inc',
|
||||
__DIR__ . '/Fixture/callable_type.php.inc',
|
||||
__DIR__ . '/Fixture/typed_array.php.inc',
|
||||
__DIR__ . '/Fixture/typed_array_nested.php.inc',
|
||||
__DIR__ . '/Fixture/symfony_console_command.php.inc',
|
||||
]);
|
||||
$this->doTestFile($file);
|
||||
}
|
||||
|
||||
public function provideDataForTest(): Iterator
|
||||
{
|
||||
yield [__DIR__ . '/Fixture/property_assign.php.inc'];
|
||||
yield [__DIR__ . '/Fixture/default_value.php.inc'];
|
||||
yield [__DIR__ . '/Fixture/assign_conflict.php.inc'];
|
||||
yield [__DIR__ . '/Fixture/callable_type.php.inc'];
|
||||
yield [__DIR__ . '/Fixture/typed_array.php.inc'];
|
||||
yield [__DIR__ . '/Fixture/typed_array_nested.php.inc'];
|
||||
yield [__DIR__ . '/Fixture/symfony_console_command.php.inc'];
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
|
|
|
@ -583,7 +583,7 @@ class Command
|
|||
*/
|
||||
protected static $defaultName;
|
||||
/**
|
||||
* @var \Symfony\Component\Console\Application|null
|
||||
* @var \Symfony\Component\Console\Application
|
||||
*/
|
||||
private $application;
|
||||
/**
|
||||
|
@ -627,7 +627,7 @@ class Command
|
|||
*/
|
||||
private $code;
|
||||
/**
|
||||
* @var \Symfony\Component\Console\Helper\HelperSet|null
|
||||
* @var \Symfony\Component\Console\Helper\HelperSet
|
||||
*/
|
||||
private $helperSet;
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,7 @@ use PhpParser\Node\Scalar\String_;
|
|||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use Rector\Bridge\Contract\AnalyzedApplicationContainerInterface;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
|
@ -300,7 +301,8 @@ CODE_SAMPLE
|
|||
$classMethod->returnType = new Identifier('array');
|
||||
}
|
||||
|
||||
$this->docBlockManipulator->addReturnTag($classMethod, new MixedType(true));
|
||||
$arrayMixedType = new ArrayType(new MixedType(), new MixedType(true));
|
||||
$this->docBlockManipulator->addReturnTag($classMethod, $arrayMixedType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,9 +42,6 @@ final class RenameClassRectorTest extends AbstractRectorTestCase
|
|||
yield [__DIR__ . '/Fixture/class_annotations_serializer_type.php.inc'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function provideDataForTestWithNamespaceRename(): Iterator
|
||||
{
|
||||
yield [__DIR__ . '/Fixture/rename_class_without_namespace.php.inc'];
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
final class Twig_Extension_Sandbox
|
||||
{
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user