diff --git a/.dev-tools/composer.json b/.dev-tools/composer.json index b44a5699..3dfc4267 100644 --- a/.dev-tools/composer.json +++ b/.dev-tools/composer.json @@ -16,7 +16,7 @@ "shipmonk/phpstan-rules": "^4.1.4", "squizlabs/php_codesniffer": "^3.13.2", "tomasvotruba/type-coverage": "^2.0.2", - "vimeo/psalm": "^6.12.1" + "vimeo/psalm": "^6.13.0" }, "autoload": { "psr-4": { diff --git a/.dev-tools/composer.lock b/.dev-tools/composer.lock index 2bbc2bb5..f3bb138b 100644 --- a/.dev-tools/composer.lock +++ b/.dev-tools/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cc29da6bc114fd14f8df10b0cfcbe6c9", + "content-hash": "ab0b2efbd9f8b7f3444da6ac50cd0e93", "packages": [ { "name": "amphp/amp", @@ -2994,16 +2994,16 @@ }, { "name": "vimeo/psalm", - "version": "6.12.1", + "version": "6.13.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "e71404b0465be25cf7f8a631b298c01c5ddd864f" + "reference": "70cdf647255a1362b426bb0f522a85817b8c791c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/e71404b0465be25cf7f8a631b298c01c5ddd864f", - "reference": "e71404b0465be25cf7f8a631b298c01c5ddd864f" + "url": "https://api.github.com/repos/vimeo/psalm/zipball/70cdf647255a1362b426bb0f522a85817b8c791c", + "reference": "70cdf647255a1362b426bb0f522a85817b8c791c" }, "require": { "amphp/amp": "^3", diff --git a/.dev-tools/psalm.xml b/.dev-tools/psalm.xml index 4fc196dd..e92d3fbb 100644 --- a/.dev-tools/psalm.xml +++ b/.dev-tools/psalm.xml @@ -15,6 +15,10 @@ + + + + diff --git a/.dev-tools/psalm_stub.php b/.dev-tools/psalm_stub.php new file mode 100644 index 00000000..3c21f86a --- /dev/null +++ b/.dev-tools/psalm_stub.php @@ -0,0 +1,22 @@ +> */ - private array $predecessorKindMap; - - public function __construct() - { - if (\defined('T_PUBLIC_SET')) { - $this->predecessorKindMap = [ - \T_PUBLIC_SET => [\T_PUBLIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC], - \T_PROTECTED_SET => [\T_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED], - \T_PRIVATE_SET => [\T_PRIVATE, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE], - ]; - } - } + private const PREDECESSOR_KIND_MAP = [ + FCT::T_PUBLIC_SET => [\T_PUBLIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC], + FCT::T_PROTECTED_SET => [\T_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED], + FCT::T_PRIVATE_SET => [\T_PRIVATE, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE], + ]; public function getDefinition(): FixerDefinitionInterface { @@ -61,7 +54,7 @@ public function getPriority(): int public function isCandidate(Tokens $tokens): bool { - return \defined('T_PUBLIC_SET') && $tokens->isAnyTokenKindsFound(\array_keys($this->predecessorKindMap)); + return $tokens->isAnyTokenKindsFound([FCT::T_PUBLIC_SET, FCT::T_PROTECTED_SET, FCT::T_PRIVATE_SET]); } public function isRisky(): bool @@ -71,14 +64,14 @@ public function isRisky(): bool public function fix(\SplFileInfo $file, Tokens $tokens): void { - foreach ($tokens->findGivenKind(\array_keys($this->predecessorKindMap)) as $kind => $elements) { + foreach ($tokens->findGivenKind([FCT::T_PUBLIC_SET, FCT::T_PROTECTED_SET, FCT::T_PRIVATE_SET]) as $kind => $elements) { foreach (\array_keys($elements) as $index) { - $this->fixVisibility($tokens, $index, $kind, $kind === \T_PUBLIC_SET); + self::fixVisibility($tokens, $index, $kind, $kind === \T_PUBLIC_SET); } } } - private function fixVisibility(Tokens $tokens, int $index, int $kind, bool $makePublicIfNone): void + private static function fixVisibility(Tokens $tokens, int $index, int $kind, bool $makePublicIfNone): void { $prevIndex = $tokens->getPrevMeaningfulToken($index); \assert(\is_int($prevIndex)); @@ -87,7 +80,7 @@ private function fixVisibility(Tokens $tokens, int $index, int $kind, bool $make \assert(\is_int($prevIndex)); } - if (!$tokens[$prevIndex]->isGivenKind($this->predecessorKindMap[$kind])) { + if (!$tokens[$prevIndex]->isGivenKind(self::PREDECESSOR_KIND_MAP[$kind])) { if ($makePublicIfNone) { $prevDeciderIndex = $tokens->getPrevTokenOfKind($index, ['(', ';', '{']); \assert(\is_int($prevDeciderIndex)); diff --git a/src/Fixer/PhpdocNoIncorrectVarAnnotationFixer.php b/src/Fixer/PhpdocNoIncorrectVarAnnotationFixer.php index 2899ca1b..13c92f56 100644 --- a/src/Fixer/PhpdocNoIncorrectVarAnnotationFixer.php +++ b/src/Fixer/PhpdocNoIncorrectVarAnnotationFixer.php @@ -17,6 +17,7 @@ use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Preg; use PhpCsFixer\Tokenizer\CT; +use PhpCsFixer\Tokenizer\FCT; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use PhpCsFixerCustomFixers\TokenRemover; @@ -96,7 +97,7 @@ private static function getIndexAfterPhpDoc(Tokens $tokens, int $index): ?int { $nextIndex = $tokens->getNextMeaningfulToken($index); - while ($nextIndex !== null && \defined('T_ATTRIBUTE') && $tokens[$nextIndex]->isGivenKind(\T_ATTRIBUTE)) { + while ($nextIndex !== null && $tokens[$nextIndex]->isGivenKind(FCT::T_ATTRIBUTE)) { $nextIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $nextIndex); $nextIndex = $tokens->getNextMeaningfulToken($nextIndex); } @@ -106,14 +107,7 @@ private static function getIndexAfterPhpDoc(Tokens $tokens, int $index): ?int private static function removeForClassElement(Tokens $tokens, int $index, int $propertyStartIndex): void { - $tokenKinds = [\T_NS_SEPARATOR, \T_STATIC, \T_STRING, \T_WHITESPACE, CT::T_ARRAY_TYPEHINT, CT::T_NULLABLE_TYPE, CT::T_TYPE_ALTERNATION]; - - if (\defined('T_READONLY')) { - $tokenKinds[] = CT::T_TYPE_INTERSECTION; - $tokenKinds[] = \T_READONLY; - } - - $variableIndex = $tokens->getTokenNotOfKindsSibling($propertyStartIndex, 1, $tokenKinds); + $variableIndex = $tokens->getTokenNotOfKindsSibling($propertyStartIndex, 1, [\T_NS_SEPARATOR, \T_STATIC, \T_STRING, \T_WHITESPACE, CT::T_ARRAY_TYPEHINT, CT::T_NULLABLE_TYPE, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION, FCT::T_READONLY]); \assert(\is_int($variableIndex)); if (!$tokens[$variableIndex]->isGivenKind(\T_VARIABLE)) { diff --git a/src/Fixer/ReadonlyPromotedPropertiesFixer.php b/src/Fixer/ReadonlyPromotedPropertiesFixer.php index 50c322d6..a3a6ca47 100644 --- a/src/Fixer/ReadonlyPromotedPropertiesFixer.php +++ b/src/Fixer/ReadonlyPromotedPropertiesFixer.php @@ -16,6 +16,7 @@ use PhpCsFixer\FixerDefinition\VersionSpecification; use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample; use PhpCsFixer\Tokenizer\CT; +use PhpCsFixer\Tokenizer\FCT; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use PhpCsFixerCustomFixers\Analyzer\ConstructorAnalyzer; @@ -43,23 +44,14 @@ final class ReadonlyPromotedPropertiesFixer extends AbstractFixer [\T_COALESCE_EQUAL, '??='], [\T_CONCAT_EQUAL, '.='], ]; - - /** @var list */ - private array $promotedPropertyVisibilityKinds; - - public function __construct() - { - $this->promotedPropertyVisibilityKinds = [ - CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, - CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, - CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, - ]; - if (\defined('T_PUBLIC_SET')) { - $this->promotedPropertyVisibilityKinds[] = \T_PUBLIC_SET; - $this->promotedPropertyVisibilityKinds[] = \T_PROTECTED_SET; - $this->promotedPropertyVisibilityKinds[] = \T_PRIVATE_SET; - } - } + private const PROMOTED_PROPERTY_VISIBILITY_KINDS = [ + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, + FCT::T_PUBLIC_SET, + FCT::T_PROTECTED_SET, + FCT::T_PRIVATE_SET, + ]; public function getDefinition(): FixerDefinitionInterface { @@ -92,7 +84,7 @@ public function getPriority(): int public function isCandidate(Tokens $tokens): bool { - return \defined('T_READONLY') && $tokens->isAnyTokenKindsFound($this->promotedPropertyVisibilityKinds); + return $tokens->isAnyTokenKindsFound(self::PROMOTED_PROPERTY_VISIBILITY_KINDS); } public function isRisky(): bool @@ -126,7 +118,7 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void \assert(\is_int($constructorOpenParenthesisIndex)); $constructorCloseParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $constructorOpenParenthesisIndex); - $this->fixParameters( + self::fixParameters( $tokens, $classOpenBraceIndex, $classCloseBraceIndex, @@ -146,7 +138,7 @@ private static function isClassReadonly(Tokens $tokens, int $index): bool return $tokens[$index]->isGivenKind(\T_READONLY); } - private function fixParameters( + private static function fixParameters( Tokens $tokens, int $classOpenBraceIndex, int $classCloseBraceIndex, @@ -158,7 +150,7 @@ private function fixParameters( continue; } - $insertIndex = $this->getInsertIndex($tokens, $index); + $insertIndex = self::getInsertIndex($tokens, $index); if ($insertIndex === null) { continue; } @@ -177,7 +169,7 @@ private function fixParameters( } } - private function getInsertIndex(Tokens $tokens, int $index): ?int + private static function getInsertIndex(Tokens $tokens, int $index): ?int { $insertIndex = null; @@ -189,7 +181,7 @@ private function getInsertIndex(Tokens $tokens, int $index): ?int if ($tokens[$index]->isGivenKind(\T_READONLY)) { return null; } - if ($insertIndex === null && $tokens[$index]->isGivenKind($this->promotedPropertyVisibilityKinds)) { + if ($insertIndex === null && $tokens[$index]->isGivenKind(self::PROMOTED_PROPERTY_VISIBILITY_KINDS)) { $insertIndex = $index; } } diff --git a/tests/AutoReview/SrcCodeTest.php b/tests/AutoReview/SrcCodeTest.php index b691050e..a151d29f 100644 --- a/tests/AutoReview/SrcCodeTest.php +++ b/tests/AutoReview/SrcCodeTest.php @@ -89,9 +89,9 @@ public function testFixerSupportsAllFilesByDefault(): void /** * @param class-string $className * - * @dataProvider provideThereIsNoPregFunctionUsedDirectlyCases + * @dataProvider provideThereIsNoDisallowedFunctionUsedDirectlyCases */ - public function testThereIsNoPregFunctionUsedDirectly(string $className): void + public function testThereIsNoDisallowedFunctionUsedDirectly(string $className): void { $reflectionClass = new \ReflectionClass($className); @@ -113,8 +113,8 @@ public function testThereIsNoPregFunctionUsedDirectly(string $className): void $stringTokens, ); $strings = \array_unique($strings); - $message = \sprintf('Class %s must not use preg_*, it shall use Preg::* instead.', $className); + $message = \sprintf('Class %s must not use preg_*, it shall use Preg::* instead.', $className); self::assertNotContains('preg_filter', $strings, $message); self::assertNotContains('preg_grep', $strings, $message); self::assertNotContains('preg_match', $strings, $message); @@ -122,18 +122,18 @@ public function testThereIsNoPregFunctionUsedDirectly(string $className): void self::assertNotContains('preg_replace', $strings, $message); self::assertNotContains('preg_replace_callback', $strings, $message); self::assertNotContains('preg_split', $strings, $message); + + self::assertNotContains('defined', $strings); } /** * @return iterable */ - public static function provideThereIsNoPregFunctionUsedDirectlyCases(): iterable + public static function provideThereIsNoDisallowedFunctionUsedDirectlyCases(): iterable { $finder = Finder::create() ->files() ->in(__DIR__ . '/../../src') - ->notName('php-cs-fixer.config.*.php') - ->notName('run') ->sortByName(); /** @var SplFileInfo $file */