From fbe6264684e1995e87ff714a24ccdb3de6fcdb49 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 22 Mar 2024 22:39:06 +0800 Subject: [PATCH] Add sniff to detect missing docs for constants --- .../Commenting/MissingDocblockSniff.php | 64 ++++++++++++++++++- .../Commenting/MissingDocblockSniffTest.php | 21 ++++++ .../MissingDocblock/imported_constants.php | 44 +++++++++++++ .../MissingDocblock/typed_constants.php | 44 +++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 moodle/Tests/Sniffs/Commenting/fixtures/MissingDocblock/imported_constants.php create mode 100644 moodle/Tests/Sniffs/Commenting/fixtures/MissingDocblock/typed_constants.php diff --git a/moodle/Sniffs/Commenting/MissingDocblockSniff.php b/moodle/Sniffs/Commenting/MissingDocblockSniff.php index 3b83c48..06b419c 100644 --- a/moodle/Sniffs/Commenting/MissingDocblockSniff.php +++ b/moodle/Sniffs/Commenting/MissingDocblockSniff.php @@ -23,6 +23,7 @@ use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Util\Tokens; +use PHPCSUtils\Tokens\Collections; use PHPCSUtils\Utils\ObjectDeclarations; /** @@ -59,6 +60,7 @@ public function register() { public function process(File $phpcsFile, $stackPtr) { $this->processScopes($phpcsFile, $stackPtr); $this->processFunctions($phpcsFile, $stackPtr); + $this->processConstants($phpcsFile, $stackPtr); } protected function processScopes(File $phpcsFile, int $stackPtr): void { @@ -147,7 +149,11 @@ protected function processFunctions(File $phpcsFile, int $stackPtr): void { if (count($token['conditions']) > 0) { // This method has conditions (a Class, Interface, Trait, etc.). // Check if that container extends or implements anything. - foreach (array_keys($token['conditions']) as $condition) { + foreach ($token['conditions'] as $condition => $conditionCode) { + if ($conditionCode === T_USE) { + // Skip any method inside a USE. + continue 2; + } if (!array_key_exists($condition, $knownClasses)) { $extendsOrImplements = $extendsOrImplements || ObjectDeclarations::findExtendedClassName( $phpcsFile, @@ -196,4 +202,60 @@ protected function processFunctions(File $phpcsFile, int $stackPtr): void { } } } + + /** + * Process constants. + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr The position in the stack. + */ + protected function processConstants(File $phpcsFile, int $stackPtr): void { + $tokens = $phpcsFile->getTokens(); + + $typePtr = $stackPtr + 1; + while ($typePtr = $phpcsFile->findNext(T_CONST, $typePtr + 1)) { + $token = $tokens[$typePtr]; + $containerName = null; + + if (count($token['conditions']) > 0) { + foreach ($token['conditions'] as $conditionPtr => $conditionCode) { + // Skip any constant inside a USE. + if ($conditionCode === T_USE) { + continue 2; + } + if (in_array($conditionCode, Collections::closedScopes())) { + $containerName = TokenUtil::getObjectName($phpcsFile, $conditionPtr); + } + } + } + + if (Docblocks::getDocBlock($phpcsFile, $typePtr)) { + // This is documented. + continue; + } + + // Get the constant name + // We have to find the equals and step back from there. + // PHP 8.3 introduces the concept of typed constants but both the type and name are presented as T_STRING + $equalPtr = $phpcsFile->findNext(T_EQUAL, $typePtr + 1); + $namePtr = $phpcsFile->findPrevious(T_STRING, $equalPtr - 1, $typePtr); + $objectName = $tokens[$namePtr]['content']; + + if ($containerName) { + $phpcsFile->addError( + 'Missing docblock for constant %s::%s', + $typePtr, + 'Missing', + [$containerName, $objectName] + ); + } else { + $phpcsFile->addError( + 'Missing docblock for constant %s', + $typePtr, + 'Missing', + [$objectName] + ); + } + } + } } diff --git a/moodle/Tests/Sniffs/Commenting/MissingDocblockSniffTest.php b/moodle/Tests/Sniffs/Commenting/MissingDocblockSniffTest.php index 0f92795..39f5d67 100644 --- a/moodle/Tests/Sniffs/Commenting/MissingDocblockSniffTest.php +++ b/moodle/Tests/Sniffs/Commenting/MissingDocblockSniffTest.php @@ -146,6 +146,15 @@ public static function docblockCorrectnessProvider(): array { 'errors' => [], 'warnings' => [], ], + 'Constants' => [ + 'fixture' => 'imported_constants', + 'fixtureFilename' => null, + 'errors' => [ + 16 => 'Missing docblock for constant UNDOCUMENTED_CONST', + 32 => 'Missing docblock for constant example_class::UNDOCUMENTED_CONST', + ], + 'warnings' => [], + ], 'Testcase' => [ 'fixture' => 'testcase_class', 'fixtureFilename' => '/lib/tests/example_test.php', @@ -182,6 +191,18 @@ public static function docblockCorrectnessProvider(): array { ]; } + if (version_compare(PHP_VERSION, '8.3.0') >= 0) { + $cases['Typed constants'] = [ + 'fixture' => 'typed_constants', + 'fixtureFilename' => null, + 'errors' => [ + 16 => 'Missing docblock for constant UNDOCUMENTED_CONST', + 32 => 'Missing docblock for constant example_class::UNDOCUMENTED_CONST', + ], + 'warnings' => [], + ]; + } + return $cases; } } diff --git a/moodle/Tests/Sniffs/Commenting/fixtures/MissingDocblock/imported_constants.php b/moodle/Tests/Sniffs/Commenting/fixtures/MissingDocblock/imported_constants.php new file mode 100644 index 0000000..7d11c23 --- /dev/null +++ b/moodle/Tests/Sniffs/Commenting/fixtures/MissingDocblock/imported_constants.php @@ -0,0 +1,44 @@ +