Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/check-before-after-same-fixtures.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function run(array $testDirectories): int
}

if ($invalidFixturePaths === []) {
$this->symfonyStyle->success('All fixtures are valid');
$this->symfonyStyle->success(sprintf('All %d fixtures are valid', count($fixtureFiles)));
return Command::SUCCESS;
}

Expand Down
4 changes: 3 additions & 1 deletion config/set/php85.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
use Rector\Renaming\ValueObject\RenameClassAndConstFetch;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([ArrayFirstLastRector::class, RemoveFinfoBufferContextArgRector::class, NullDebugInfoReturnRector::class]);
$rectorConfig->rules(
[ArrayFirstLastRector::class, RemoveFinfoBufferContextArgRector::class, NullDebugInfoReturnRector::class]
);

$rectorConfig->ruleWithConfiguration(
RemoveFuncCallArgRector::class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Fixture;

use PHPUnit\Framework\TestCase;
use Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Source\SomeDocblockType;

final class MagicSetupTestAssign extends TestCase
{
/**
* @var SomeDocblockType
*/
private $someType;

protected function setUp(): void
{
$this->someType = $this->create('string');
}
}

?>
-----
<?php

namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Fixture;

use PHPUnit\Framework\TestCase;
use Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Source\SomeDocblockType;

final class MagicSetupTestAssign extends TestCase
{
private \Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Source\SomeDocblockType $someType;

protected function setUp(): void
{
$this->someType = $this->create('string');
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Fixture;

use PHPUnit\Framework\TestCase;
use Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Source\SomeDocblockType;

final class SkipNoDocblock extends TestCase
{
private $someType;

protected function setUp(): void
{
$this->someType = $this->create('string');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Fixture;

use PHPUnit\Framework\TestCase;
use Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Source\SomeDocblockType;

final class SkipPublicProperty extends TestCase
{
/**
* @var SomeDocblockType
*/
public $someType;

protected function setUp(): void
{
$this->someType = $this->create('string');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\Source;

final class SomeDocblockType
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class TypedPropertyFromDocblockSetUpDefinedRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/rule_config.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector;
use Rector\ValueObject\PhpVersionFeature;

return RectorConfig::configure()
->withRules([TypedPropertyFromDocblockSetUpDefinedRector::class])
->withPhpVersion(PhpVersionFeature::TYPED_PROPERTIES);
5 changes: 4 additions & 1 deletion rules/Php85/Rector/ClassMethod/NullDebugInfoReturnRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ public function refactor(Node $node): ?Node
}

$hasChanged = \false;
$this->traverseNodesWithCallable((array) $node->stmts, function (Node $node) use (&$hasChanged): int|Return_|null {

$this->traverseNodesWithCallable((array) $node->stmts, function (Node $node) use (
&$hasChanged
): int|Return_|null {
if ($node instanceof Class_ || $node instanceof Function_ || $node instanceof Closure) {
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php

declare(strict_types=1);

namespace Rector\TypeDeclaration\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\Type\MixedType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector;
use Rector\ValueObject\MethodName;
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector\TypedPropertyFromDocblockSetUpDefinedRectorTest
*/
final class TypedPropertyFromDocblockSetUpDefinedRector extends AbstractRector implements MinPhpVersionInterface
{
public function __construct(
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
private readonly ConstructorAssignDetector $constructorAssignDetector,
private readonly PhpDocInfoFactory $phpDocInfoFactory,
private readonly StaticTypeMapper $staticTypeMapper,
private readonly DocBlockUpdater $docBlockUpdater
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add property type in PHPUnit test from docblock, if defined in setUp() method', [
new CodeSample(
<<<'CODE_SAMPLE'
use PHPUnit\Framework\TestCase;

class SomeClass extends TestCase
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
private $doctrine;

protected function setUp(): void
{
$this->doctrine = $this->container('doctrine.orm.entity_manager');
}
}
CODE_SAMPLE

,
<<<'CODE_SAMPLE'
use PHPUnit\Framework\TestCase;

class SomeClass extends TestCase
{
private \Doctrine\ORM\EntityManagerInterface $doctrine;

protected function setUp(): void
{
$this->doctrine = $this->container('doctrine.orm.entity_manager');
}
}
CODE_SAMPLE
),
]);
}

/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Class_::class];
}

/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
return null;
}

// nothing useful here
$setUpClassMethod = $node->getMethod(MethodName::SET_UP);
if (! $setUpClassMethod instanceof ClassMethod) {
return null;
}

$hasChanged = false;

foreach ($node->getProperties() as $property) {
// already known type
if ($property->type instanceof Node) {
continue;
}

// some magic might be going on
if ($property->isStatic()) {
continue;
}

if (! $property->isPrivate()) {
continue;
}

// exactly one property
if (count($property->props) !== 1) {
continue;
}

$propertyName = $property->props[0]->name->toString();
if (! $this->constructorAssignDetector->isPropertyAssigned($node, $propertyName)) {
continue;
}

$propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNode($property);
if (! $propertyPhpDocInfo instanceof PhpDocInfo) {
continue;
}

$varType = $propertyPhpDocInfo->getVarType();
if ($varType instanceof MixedType) {
continue;
}

$nativePropertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
$varType,
TypeKind::PROPERTY
);
if (! $nativePropertyTypeNode instanceof Node) {
continue;
}

// remove var tag
$this->removeVarTag($propertyPhpDocInfo, $property);

$property->type = $nativePropertyTypeNode;
$hasChanged = true;
}

if ($hasChanged) {
return $node;
}

return null;
}

public function provideMinPhpVersion(): int
{
return PhpVersionFeature::TYPED_PROPERTIES;
}

private function removeVarTag(PhpDocInfo $propertyPhpDocInfo, Property $property): void
{
$propertyPhpDocInfo->removeByType(VarTagValueNode::class);
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property);
}
}
3 changes: 3 additions & 0 deletions src/Config/Level/TypeDeclarationLevel.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Rector\TypeDeclaration\Rector\Class_\PropertyTypeFromStrictSetterGetterRector;
use Rector\TypeDeclaration\Rector\Class_\ReturnTypeFromStrictTernaryRector;
use Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromCreateMockAssignRector;
use Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector;
use Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeBasedOnPHPUnitDataProviderRector;
Expand Down Expand Up @@ -142,5 +143,7 @@ final class TypeDeclarationLevel

// possibly based on docblocks, but also helpful, intentionally last
AddArrayFunctionClosureParamTypeRector::class,

TypedPropertyFromDocblockSetUpDefinedRector::class,
];
}
4 changes: 2 additions & 2 deletions src/ValueObject/PhpVersionFeature.php
Original file line number Diff line number Diff line change
Expand Up @@ -768,12 +768,12 @@ final class PhpVersionFeature
* @var int
*/
public const ARRAY_ANY = PhpVersion::PHP_84;

/**
* @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_context_parameter_for_finfo_buffer
* @var int
*/
public const DEPRECATE_FINFO_BUFFER_CONTEXT = PhpVersion::PHP_85;
public const DEPRECATE_FINFO_BUFFER_CONTEXT = PhpVersion::PHP_85;

/**
* @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_debuginfo_returning_null
Expand Down
Loading