From e37482499581e9a29a33e079dffbe9e94076a9dc Mon Sep 17 00:00:00 2001 From: Max Antipin Date: Sun, 19 Jul 2026 22:23:11 +0300 Subject: [PATCH] Bugfix: PropertyTypeDeclarationSniff and property hooks --- .../Classes/PropertyTypeDeclarationSniff.php | 37 +++++++++------- .../Functions/ReturnTypeDeclarationSniff.php | 3 ++ ... => PropertyTypeDeclarationUnitTest.1.inc} | 0 .../PropertyTypeDeclarationUnitTest.2.inc | 44 +++++++++++++++++++ .../PropertyTypeDeclarationUnitTest.php | 22 ++++++++-- .../ReturnTypeDeclarationUnitTest.2.inc | 39 ---------------- ....inc => ReturnTypeDeclarationUnitTest.inc} | 0 .../ReturnTypeDeclarationUnitTest.php | 30 +++---------- Makefile | 4 +- README.md | 2 +- 10 files changed, 97 insertions(+), 84 deletions(-) rename AntipinCS/Tests/Classes/{PropertyTypeDeclarationUnitTest.inc => PropertyTypeDeclarationUnitTest.1.inc} (100%) create mode 100644 AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.2.inc delete mode 100644 AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.2.inc rename AntipinCS/Tests/Functions/{ReturnTypeDeclarationUnitTest.1.inc => ReturnTypeDeclarationUnitTest.inc} (100%) diff --git a/AntipinCS/Sniffs/Classes/PropertyTypeDeclarationSniff.php b/AntipinCS/Sniffs/Classes/PropertyTypeDeclarationSniff.php index edaf264..121fb29 100644 --- a/AntipinCS/Sniffs/Classes/PropertyTypeDeclarationSniff.php +++ b/AntipinCS/Sniffs/Classes/PropertyTypeDeclarationSniff.php @@ -10,7 +10,6 @@ namespace MaxAntipin\PHPCS\Standards\AntipinCS\Sniffs\Classes; -use Exception; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Sniffs\AbstractVariableSniff; use PHP_CodeSniffer\Util\Tokens; @@ -24,25 +23,31 @@ protected function processMemberVar(File $phpcsFile, int $stackPtr): void // Detect multiple properties defined at the same time. Throw an error // for this, but also only process the first property in the list so we don't // repeat errors. - $find = Tokens::$scopeModifiers; - $find[] = T_VARIABLE; - $find[] = T_VAR; - $find[] = T_READONLY; - $find[] = T_SEMICOLON; - $find[] = T_OPEN_CURLY_BRACKET; - - $prev = $phpcsFile->findPrevious($find, ($stackPtr - 1)); - if ($tokens[$prev]['code'] === T_VARIABLE) { + static $find = [...Tokens::SCOPE_MODIFIERS, + T_VARIABLE, + T_VAR, + T_READONLY, + T_FINAL, + T_ABSTRACT, + T_SEMICOLON, + T_OPEN_CURLY_BRACKET, + ]; + $prevPtr = $phpcsFile->findPrevious($find, ($stackPtr - 1)); + if ($tokens[$prevPtr]['code'] === T_VARIABLE) { return; } - - try { - $propertyInfo = $phpcsFile->getMemberProperties($stackPtr); - if (empty($propertyInfo)) { + if ($tokens[$prevPtr]['code'] === T_OPEN_CURLY_BRACKET) { + $prevPtr = $phpcsFile->findPrevious(Tokens::EMPTY_TOKENS, $prevPtr - 1, null, true); + if ($tokens[$prevPtr]['content'] === 'get') { return; } - } catch (Exception $e) { - // Turns out not to be a property after all. + } + $nextPtr = $phpcsFile->findNext(Tokens::EMPTY_TOKENS, $stackPtr + 1, null, true); + static $exclude = [T_OBJECT_OPERATOR]; + if ( + in_array($tokens[$nextPtr]['code'], $exclude, true) || + !($propertyInfo = $phpcsFile->getMemberProperties($stackPtr)) + ) { return; } $property = $tokens[$stackPtr]['content']; diff --git a/AntipinCS/Sniffs/Functions/ReturnTypeDeclarationSniff.php b/AntipinCS/Sniffs/Functions/ReturnTypeDeclarationSniff.php index ae81f92..8123500 100644 --- a/AntipinCS/Sniffs/Functions/ReturnTypeDeclarationSniff.php +++ b/AntipinCS/Sniffs/Functions/ReturnTypeDeclarationSniff.php @@ -27,6 +27,7 @@ public function register(): array public function process(File $phpcsFile, int $stackPtr): void { $tokens = $phpcsFile->getTokens(); + /** @var array $token */ $token = $tokens[$stackPtr]; if (!isset($token['parenthesis_opener']) || !isset($token['parenthesis_closer'])) { $phpcsFile->addError( @@ -36,7 +37,9 @@ public function process(File $phpcsFile, int $stackPtr): void ); return; } + /** @var array $skipTypes */ static $skipTypes = ['T_CLOSURE' => true, 'T_FN' => true]; + /** @var array $skipMethods */ static $skipMethods = ['__construct' => true, '__destruct' => true]; if ( !isset($skipTypes[$token['type']]) diff --git a/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.inc b/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.1.inc similarity index 100% rename from AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.inc rename to AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.1.inc diff --git a/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.2.inc b/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.2.inc new file mode 100644 index 0000000..cb916a1 --- /dev/null +++ b/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.2.inc @@ -0,0 +1,44 @@ +dnsFirewall ??= new DnsFirewallResource(new DnsFirewallServiceClient($this->client)); + } + } + + public private(set) DnsInboundEndpointResource $dnsInboundEndpoint { + get { + $a = 5; + return $this->dnsInboundEndpoint ??= new DnsInboundEndpointResource( + new DnsInboundEndpointServiceClient($this->client) + ); + } + } + + public private(set) DnsZoneResource $dnsZone { + get => $this->dnsZone ??= new DnsZoneResource(new DnsZoneServiceClient($this->client)); + } + + public function __construct( + private readonly GRPCClient $client, + ) { + } +} diff --git a/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.php b/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.php index d8dd1d8..8bf2dbf 100644 --- a/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.php +++ b/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.php @@ -17,6 +17,18 @@ #[CoversClass(PropertyTypeDeclarationSniff::class)] final class PropertyTypeDeclarationUnitTest extends AbstractSniffTestCase { + /** + * Get a list of all test files to check. + * + * @param string $testFileBase The base path that the unit tests files will have. + * + * @return string[] + */ + protected function getTestFiles(string $testFileBase): array + { + return array_map(static fn (int $i): string => $testFileBase . $i . '.inc', range(1, 2)); + } + /** * Returns the lines where errors should occur. * @@ -25,9 +37,10 @@ final class PropertyTypeDeclarationUnitTest extends AbstractSniffTestCase * * @return array */ - public function getErrorList(): array + public function getErrorList(string $testFile = ''): array { - return [ + return match ($testFile) { + 'PropertyTypeDeclarationUnitTest.1.inc' => [ 4 => 1, 5 => 1, 6 => 1, @@ -45,7 +58,10 @@ public function getErrorList(): array 37 => 1, 38 => 1, 51 => 1, - ]; + ], + 'PropertyTypeDeclarationUnitTest.2.inc' => [], + default => throw new \RuntimeException('Unhandled test file: ' . $testFile) + }; } /** diff --git a/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.2.inc b/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.2.inc deleted file mode 100644 index 25420e5..0000000 --- a/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.2.inc +++ /dev/null @@ -1,39 +0,0 @@ -cloud ??= new CloudResource(new CloudServiceClient($this->client)); - } - } - - public private(set) FolderResource $folder { - get { - return $this->folder ??= new FolderResource(new FolderServiceClient($this->client)); - } - } - - public function __construct( - private readonly GRPCClient $client, - ) { - } - - public function __destruct() - { - } -} diff --git a/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.1.inc b/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.inc similarity index 100% rename from AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.1.inc rename to AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.inc diff --git a/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.php b/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.php index 41cda81..30f884d 100644 --- a/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.php +++ b/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.php @@ -17,34 +17,18 @@ #[CoversClass(ReturnTypeDeclarationSniff::class)] final class ReturnTypeDeclarationUnitTest extends AbstractSniffTestCase { - /** - * Get a list of all test files to check. - * - * @param string $testFileBase The base path that the unit tests files will have. - * - * @return string[] - */ - protected function getTestFiles(string $testFileBase): array - { - return array_map(static fn (int $i): string => $testFileBase . $i . '.inc', range(1, 2)); - } - /** * @return array */ protected function getErrorList(string $testFile = ''): array { - return match ($testFile) { - 'ReturnTypeDeclarationUnitTest.1.inc' => [ - 65 => 1, - 68 => 1, - 73 => 1, - 75 => 1, - 77 => 1, - ], - 'ReturnTypeDeclarationUnitTest.2.inc' => [], - default => throw new \RuntimeException('Unhandled test file: ' . $testFile) - }; + return [ + 65 => 1, + 68 => 1, + 73 => 1, + 75 => 1, + 77 => 1, + ]; } /** diff --git a/Makefile b/Makefile index 7383013..edb5b34 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ check-dockerfile: docker run --rm -i ghcr.io/hadolint/hadolint < .docker/Dockerfile lint: - php ./vendor/bin/phpcs + php ./vendor/bin/phpcs -v -s php ./vendor/bin/phpstan analyze php ./vendor/bin/phpcs-check-feature-completeness @@ -23,7 +23,7 @@ fix-lint: php ./vendor/bin/phpcbf test-cs: - cd ../cs-test/ && php ./vendor/bin/phpunit --no-coverage -v -s --filter AntipinCS + cd ../cs-test/ && php ./vendor/bin/phpunit --no-coverage --filter AntipinCS test-coverage: cd ../cs-test/ && XDEBUG_MODE=coverage php ./vendor/bin/phpunit --filter AntipinCS diff --git a/README.md b/README.md index 48438cc..800645f 100644 --- a/README.md +++ b/README.md @@ -89,5 +89,5 @@ Two errors with property hooks: - Missing type declaration for property "$this" ```Shell -cat AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.2.inc | ./vendor/bin/phpcs --stdin-path=/php-code-sniffs/AntipinCS/Tests/Functions/ReturnTypeDeclarationUnitTest.2.php -v -s - +cat AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.2.inc | ./vendor/bin/phpcs --stdin-path=/php-code-sniffs/AntipinCS/Tests/Classes/PropertyTypeDeclarationUnitTest.2.php -v -s - ``` \ No newline at end of file