diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/AddParamStringTypeFromSprintfUseRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/AddParamStringTypeFromSprintfUseRectorTest.php new file mode 100644 index 00000000000..ee0cfa78f73 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/AddParamStringTypeFromSprintfUseRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/pass_sprintf.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/pass_sprintf.php.inc new file mode 100644 index 00000000000..246b1e0dc56 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/pass_sprintf.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_non_string_mask.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_non_string_mask.php.inc new file mode 100644 index 00000000000..1c7aff48498 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_non_string_mask.php.inc @@ -0,0 +1,11 @@ +rule(AddParamStringTypeFromSprintfUseRector::class); +}; diff --git a/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php b/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php deleted file mode 100644 index 560a6e7b42a..00000000000 --- a/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php +++ /dev/null @@ -1,75 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Class_ - { - throw new ShouldNotHappenException(sprintf( - 'The %s rule is deprecated. Use %s instead', - self::class, - ConvertStaticToSelfRector::class, - )); - } -} diff --git a/rules/Privatization/TypeManipulator/TypeNormalizer.php b/rules/Privatization/TypeManipulator/TypeNormalizer.php index fae0c78b942..eaf46f1254f 100644 --- a/rules/Privatization/TypeManipulator/TypeNormalizer.php +++ b/rules/Privatization/TypeManipulator/TypeNormalizer.php @@ -42,15 +42,6 @@ public function __construct( } - /** - * @deprecated This method is deprecated and will be removed in the next major release. - * Use @see generalizeConstantTypes() instead. - */ - public function generalizeConstantBoolTypes(Type $type): Type - { - return $this->generalizeConstantTypes($type); - } - /** * Generalize false/true constantArrayType to bool, * as mostly default value but accepts both diff --git a/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php b/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php new file mode 100644 index 00000000000..b69a16007b9 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php @@ -0,0 +1,79 @@ +betterNodeFinder->findInstancesOfScoped((array) $functionLike->stmts, FuncCall::class); + + foreach ($funcCalls as $funcCall) { + if (! $this->nodeNameResolver->isName($funcCall->name, 'sprintf')) { + continue; + } + + if ($funcCall->isFirstClassCallable()) { + continue; + } + + $args = $funcCall->getArgs(); + if (count($args) < 2) { + continue; + } + + /** @var Arg $messageArg */ + $messageArg = array_shift($args); + if (! $messageArg->value instanceof String_) { + continue; + } + + $string = $messageArg->value; + + // match all %s, %d types by position + $masks = Strings::match($string->value, '#%[sd]#'); + + foreach ($args as $position => $arg) { + if (! $arg->value instanceof Variable) { + continue; + } + + if (! $this-> nodeNameResolver->isName($arg->value, $variableName)) { + continue; + } + + if (! isset($masks[$position])) { + continue; + } + + $knownMaskOnPosition = $masks[$position]; + if ($knownMaskOnPosition !== $mask) { + continue; + } + + return true; + } + } + + return false; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector.php new file mode 100644 index 00000000000..ae658b909c8 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector.php @@ -0,0 +1,99 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ClassMethod|Function_|null + { + if ($node->stmts === null) { + return null; + } + + if ($node->getParams() === []) { + return null; + } + + $hasChanged = false; + foreach ($node->getParams() as $param) { + if ($param->type instanceof Node) { + continue; + } + + /** @var string $variableName */ + $variableName = $this->getName($param->var); + + if (! $this->variableInSprintfMaskMatcher->matchMask($node, $variableName, '%s')) { + continue; + } + + $param->type = new Identifier('string'); + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/src/Config/Level/TypeDeclarationLevel.php b/src/Config/Level/TypeDeclarationLevel.php index f74a24201b1..f4ae649db3e 100644 --- a/src/Config/Level/TypeDeclarationLevel.php +++ b/src/Config/Level/TypeDeclarationLevel.php @@ -17,6 +17,7 @@ use Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector; use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector; use Rector\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector; +use Rector\TypeDeclaration\Rector\ClassMethod\AddParamStringTypeFromSprintfUseRector; use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeBasedOnPHPUnitDataProviderRector; use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector; use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationBasedOnParentClassMethodRector; @@ -148,6 +149,7 @@ final class TypeDeclarationLevel // array parameter from dim fetch assign inside StrictArrayParamDimFetchRector::class, AddParamFromDimFetchKeyUseRector::class, + AddParamStringTypeFromSprintfUseRector::class, // possibly based on docblocks, but also helpful, intentionally last AddArrayFunctionClosureParamTypeRector::class,