diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md index 9b44b8bb876..a13ae60d05c 100644 --- a/build/target-repository/docs/rector_rules_overview.md +++ b/build/target-repository/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 406 Rules Overview +# 409 Rules Overview
@@ -64,7 +64,7 @@ - [Transform](#transform) (34) -- [TypeDeclaration](#typedeclaration) (34) +- [TypeDeclaration](#typedeclaration) (37) - [Visibility](#visibility) (3) @@ -5659,23 +5659,8 @@ Add null default to properties with PHP 7.4 property nullable type Changes property type by `@var` annotations or default value. -:wrench: **configure it!** - - class: [`Rector\Php74\Rector\Property\TypedPropertyRector`](../rules/Php74/Rector/Property/TypedPropertyRector.php) -```php -use Rector\Config\RectorConfig; -use Rector\Php74\Rector\Property\TypedPropertyRector; - -return static function (RectorConfig $rectorConfig): void { - $rectorConfig->ruleWithConfiguration(TypedPropertyRector::class, [ - TypedPropertyRector::INLINE_PUBLIC => false, - ]); -}; -``` - -↓ - ```diff final class SomeClass { @@ -5826,8 +5811,23 @@ Change `$this::class` to static::class or self::class depends on class modifier Change simple property init and assign to constructor promotion +:wrench: **configure it!** + - class: [`Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector`](../rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php) +```php +use Rector\Config\RectorConfig; +use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector; + +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(ClassPropertyAssignToConstructorPromotionRector::class, [ + ClassPropertyAssignToConstructorPromotionRector::INLINE_PUBLIC => false, + ]); +}; +``` + +↓ + ```diff class SomeClass { @@ -9296,6 +9296,25 @@ Change `@return` types and type from static analysis to type declarations if not
+### ReturnTypeFromReturnDirectArrayRector + +Add return type from return direct array + +- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnDirectArrayRector`](../rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector.php) + +```diff + final class AddReturnArray + { +- public function getArray() ++ public function getArray(): array + { + return [1, 2, 3]; + } + } +``` + +
+ ### ReturnTypeFromReturnNewRector Add return type to function like with return new @@ -9334,6 +9353,27 @@ Add strict return type based on returned strict expr type
+### ReturnTypeFromStrictConstantReturnRector + +Add strict type declaration based on returned constants + +- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictConstantReturnRector`](../rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector.php) + +```diff + class SomeClass + { + public const NAME = 'name'; + +- public function run() ++ public function run(): string + { + return self::NAME; + } + } +``` + +
+ ### ReturnTypeFromStrictNativeCallRector Add strict return type based native function or class method return @@ -9374,6 +9414,30 @@ Add strict return array type based on created empty array and returned
+### ReturnTypeFromStrictTypedCallRector + +Add return type from strict return type of call + +- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector`](../rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector.php) + +```diff + final class SomeClass + { +- public function getData() ++ public function getData(): int + { + return $this->getNumber(); + } + + private function getNumber(): int + { + return 1000; + } + } +``` + +
+ ### ReturnTypeFromStrictTypedPropertyRector Add return method return type based on strict typed property diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/final_class_non_typed_protected_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/final_class_non_typed_protected_property.php.inc new file mode 100644 index 00000000000..86f5ab54cc6 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/final_class_non_typed_protected_property.php.inc @@ -0,0 +1,31 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/non_final_non_typed_private_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/non_final_non_typed_private_property.php.inc new file mode 100644 index 00000000000..74aea1f086e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/non_final_non_typed_private_property.php.inc @@ -0,0 +1,31 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/skip_non_typed_non_final_class_protected_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/skip_non_typed_non_final_class_protected_property.php.inc new file mode 100644 index 00000000000..766a5c81689 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/skip_non_typed_non_final_class_protected_property.php.inc @@ -0,0 +1,16 @@ +x = $x; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/typed_non_final_class_protected_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/typed_non_final_class_protected_property.php.inc new file mode 100644 index 00000000000..41bda608153 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/typed_non_final_class_protected_property.php.inc @@ -0,0 +1,28 @@ +x = $x; + } +} + +?> +----- + \ No newline at end of file diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/InlinePublicDisableTest.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/InlinePublicDisableTest.php new file mode 100644 index 00000000000..7f62fa20f45 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/InlinePublicDisableTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureInlinePublicDisable'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_disable_inline_public.php'; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/auto_import_configured_rule.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/auto_import_configured_rule.php index 8fa482a914d..3bd25f27c77 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/auto_import_configured_rule.php +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/auto_import_configured_rule.php @@ -7,5 +7,7 @@ return static function (RectorConfig $rectorConfig): void { $rectorConfig->importNames(); - $rectorConfig->rule(ClassPropertyAssignToConstructorPromotionRector::class); + $rectorConfig->ruleWithConfiguration(ClassPropertyAssignToConstructorPromotionRector::class, [ + ClassPropertyAssignToConstructorPromotionRector::INLINE_PUBLIC => true, + ]); }; diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule.php index 7d335b78e44..2202792ff48 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule.php @@ -6,5 +6,7 @@ use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector; return static function (RectorConfig $rectorConfig): void { - $rectorConfig->rule(ClassPropertyAssignToConstructorPromotionRector::class); + $rectorConfig->ruleWithConfiguration(ClassPropertyAssignToConstructorPromotionRector::class, [ + ClassPropertyAssignToConstructorPromotionRector::INLINE_PUBLIC => true, + ]); }; diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disable_inline_public.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disable_inline_public.php new file mode 100644 index 00000000000..404b796d8e3 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disable_inline_public.php @@ -0,0 +1,11 @@ +rule(ClassPropertyAssignToConstructorPromotionRector::class); +}; diff --git a/rules/Php74/Guard/MakePropertyTypedGuard.php b/rules/Php74/Guard/MakePropertyTypedGuard.php index d84e5ceb8ee..e008ae5afdc 100644 --- a/rules/Php74/Guard/MakePropertyTypedGuard.php +++ b/rules/Php74/Guard/MakePropertyTypedGuard.php @@ -4,25 +4,12 @@ namespace Rector\Php74\Guard; -use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Property; -use PHPStan\Reflection\ClassReflection; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\NodeAnalyzer\PropertyAnalyzer; -use Rector\Core\NodeManipulator\PropertyManipulator; -use Rector\Core\Reflection\ReflectionResolver; -use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\Privatization\Guard\ParentPropertyLookupGuard; final class MakePropertyTypedGuard { public function __construct( - private readonly NodeNameResolver $nodeNameResolver, - private readonly PropertyAnalyzer $propertyAnalyzer, - private readonly PropertyManipulator $propertyManipulator, - private readonly ParentPropertyLookupGuard $parentPropertyLookupGuard, - private readonly ReflectionResolver $reflectionResolver + private readonly PropertyTypeChangeGuard $propertyTypeChangeGuard ) { } @@ -32,55 +19,6 @@ public function isLegal(Property $property, bool $inlinePublic = true): bool return false; } - if (count($property->props) > 1) { - return false; - } - - $classReflection = $this->reflectionResolver->resolveClassReflection($property); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - /** - * - trait properties are unpredictable based on class context they appear in - * - on interface properties as well, as interface not allowed to have property - */ - if (! $classReflection->isClass()) { - return false; - } - - $propertyName = $this->nodeNameResolver->getName($property); - - if ($this->propertyManipulator->isUsedByTrait($classReflection, $propertyName)) { - return false; - } - - if ($inlinePublic) { - return ! $this->propertyAnalyzer->hasForbiddenType($property); - } - - if ($property->isPrivate()) { - return ! $this->propertyAnalyzer->hasForbiddenType($property); - } - - return $this->isSafeProtectedProperty($property); - } - - private function isSafeProtectedProperty(Property $property): bool - { - if (! $property->isProtected()) { - return false; - } - - $parentNode = $property->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Class_) { - throw new ShouldNotHappenException(); - } - - if (! $parentNode->isFinal()) { - return false; - } - - return $this->parentPropertyLookupGuard->isLegal($property); + return $this->propertyTypeChangeGuard->isLegal($property, $inlinePublic); } } diff --git a/rules/Php74/Guard/PropertyTypeChangeGuard.php b/rules/Php74/Guard/PropertyTypeChangeGuard.php new file mode 100644 index 00000000000..2ffecc7a2bb --- /dev/null +++ b/rules/Php74/Guard/PropertyTypeChangeGuard.php @@ -0,0 +1,90 @@ +props) > 1) { + return false; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($property); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + /** + * - trait properties are unpredictable based on class context they appear in + * - on interface properties as well, as interface not allowed to have property + */ + if (! $classReflection->isClass()) { + return false; + } + + $propertyName = $this->nodeNameResolver->getName($property); + + if ($this->propertyManipulator->isUsedByTrait($classReflection, $propertyName)) { + return false; + } + + if ($this->propertyAnalyzer->hasForbiddenType($property)) { + return false; + } + + if ($inlinePublic) { + return true; + } + + if ($property->isPrivate()) { + return true; + } + + if ($isConstructorPromotion) { + return true; + } + + return $this->isSafeProtectedProperty($property); + } + + private function isSafeProtectedProperty(Property $property): bool + { + if (! $property->isProtected()) { + return false; + } + + $parentNode = $property->getAttribute(AttributeKey::PARENT_NODE); + if (! $parentNode instanceof Class_) { + throw new ShouldNotHappenException(); + } + + if (! $parentNode->isFinal()) { + return false; + } + + return $this->parentPropertyLookupGuard->isLegal($property); + } +} diff --git a/rules/Php80/Guard/MakePropertyPromotionGuard.php b/rules/Php80/Guard/MakePropertyPromotionGuard.php new file mode 100644 index 00000000000..ea65435e0d0 --- /dev/null +++ b/rules/Php80/Guard/MakePropertyPromotionGuard.php @@ -0,0 +1,44 @@ +propertyTypeChangeGuard->isLegal($property, $inlinePublic, true)) { + return false; + } + + if ($class->isFinal()) { + return true; + } + + if ($inlinePublic) { + return true; + } + + if ($property->isPrivate()) { + return true; + } + + if (! $param->type instanceof Node) { + return true; + } + + return $property->type instanceof Node; + } +} diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index 7ab663ea265..2857b1be0c7 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -16,18 +16,19 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey; +use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface; use Rector\Core\NodeAnalyzer\ParamAnalyzer; -use Rector\Core\NodeAnalyzer\PropertyAnalyzer; use Rector\Core\Rector\AbstractRector; use Rector\Core\ValueObject\MethodName; use Rector\Core\ValueObject\PhpVersionFeature; use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover; use Rector\Naming\VariableRenamer; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Php80\Guard\MakePropertyPromotionGuard; use Rector\Php80\NodeAnalyzer\PromotedPropertyCandidateResolver; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\VersionBonding\Contract\MinPhpVersionInterface; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** @@ -35,15 +36,31 @@ * * @see \Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\ClassPropertyAssignToConstructorPromotionRectorTest */ -final class ClassPropertyAssignToConstructorPromotionRector extends AbstractRector implements MinPhpVersionInterface +final class ClassPropertyAssignToConstructorPromotionRector extends AbstractRector implements MinPhpVersionInterface, AllowEmptyConfigurableRectorInterface { + /** + * @api + * @var string + */ + public const INLINE_PUBLIC = 'inline_public'; + + /** + * Default to false, which only apply changes: + * + * – private modifier property + * - protected/public modifier property when property typed + * + * Set to true will allow change whether property is typed or not as far as not forbidden, eg: callable type, null type, etc. + */ + private bool $inlinePublic = false; + public function __construct( private readonly PromotedPropertyCandidateResolver $promotedPropertyCandidateResolver, private readonly VariableRenamer $variableRenamer, private readonly VarTagRemover $varTagRemover, private readonly ParamAnalyzer $paramAnalyzer, private readonly PhpDocTypeChanger $phpDocTypeChanger, - private readonly PropertyAnalyzer $propertyAnalyzer + private readonly MakePropertyPromotionGuard $makePropertyPromotionGuard ) { } @@ -52,7 +69,7 @@ public function getRuleDefinition(): RuleDefinition return new RuleDefinition( 'Change simple property init and assign to constructor promotion', [ - new CodeSample( + new ConfiguredCodeSample( <<<'CODE_SAMPLE' class SomeClass { @@ -75,11 +92,20 @@ public function __construct( } } CODE_SAMPLE + , + [ + self::INLINE_PUBLIC => false, + ] ), ] ); } + public function configure(array $configuration): void + { + $this->inlinePublic = $configuration[self::INLINE_PUBLIC] ?? (bool) current($configuration); + } + /** * @return array> */ @@ -112,7 +138,7 @@ public function refactor(Node $node): ?Node continue; } - if ($this->propertyAnalyzer->hasForbiddenType($property)) { + if (! $this->makePropertyPromotionGuard->isLegal($node, $property, $param, $this->inlinePublic)) { continue; }