forked from moodlehq/moodle-cs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replaces the Package checks in moodle-local_moodlecheck
- Loading branch information
1 parent
73f0a60
commit a0ffbd9
Showing
32 changed files
with
1,186 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
<?php | ||
// This file is part of Moodle - http://moodle.org/ | ||
// | ||
// Moodle is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// Moodle is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
namespace MoodleHQ\MoodleCS\moodle\Sniffs\Commenting; | ||
|
||
// phpcs:disable moodle.NamingConventions | ||
|
||
use MoodleHQ\MoodleCS\moodle\Util\MoodleUtil; | ||
use MoodleHQ\MoodleCS\moodle\Util\Docblocks; | ||
use PHP_CodeSniffer\Sniffs\Sniff; | ||
use PHP_CodeSniffer\Files\File; | ||
use PHPCSUtils\Utils\ObjectDeclarations; | ||
|
||
/** | ||
* Checks that all test classes and global functions have appropriate @package tags. | ||
* | ||
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk> | ||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | ||
*/ | ||
class PackageSniff implements Sniff { | ||
|
||
/** | ||
* Register for open tag (only process once per file). | ||
*/ | ||
public function register() { | ||
return [ | ||
T_OPEN_TAG, | ||
]; | ||
} | ||
|
||
/** | ||
* Processes php files and perform various checks with file. | ||
* | ||
* @param File $phpcsFile The file being scanned. | ||
* @param int $stackPtr The position in the stack. | ||
*/ | ||
public function process(File $phpcsFile, $stackPtr) { | ||
$tokens = $phpcsFile->getTokens(); | ||
|
||
$docblock = Docblocks::getDocBlock($phpcsFile, $stackPtr); | ||
if ($docblock) { | ||
$filePackageFound = $this->checkDocblock( | ||
$phpcsFile, | ||
$stackPtr, | ||
$docblock | ||
); | ||
if ($filePackageFound) { | ||
return; | ||
} | ||
} | ||
|
||
$find = [ | ||
T_CLASS, | ||
T_FUNCTION, | ||
T_TRAIT, | ||
T_INTERFACE, | ||
]; | ||
$typePtr = $stackPtr + 1; | ||
while ($typePtr = $phpcsFile->findNext($find, $typePtr + 1)) { | ||
$token = $tokens[$typePtr]; | ||
if ($token['code'] === T_FUNCTION && !empty($token['conditions'])) { | ||
// Skip methods of classes, traits and interfaces. | ||
continue; | ||
} | ||
|
||
$docblock = Docblocks::getDocBlock($phpcsFile, $typePtr); | ||
|
||
if ($docblock === null) { | ||
$objectName = $this->getObjectName($phpcsFile, $typePtr); | ||
$objectType = $this->getObjectType($phpcsFile, $typePtr); | ||
$phpcsFile->addError('Missing doc comment for %s %s', $typePtr, 'Missing', [$objectType, $objectName]); | ||
|
||
continue; | ||
} | ||
|
||
$this->checkDocblock($phpcsFile, $typePtr, $docblock); | ||
} | ||
|
||
} | ||
|
||
/** | ||
* Get the human-readable object type. | ||
* | ||
* @param File $phpcsFile | ||
* @param int $stackPtr | ||
* @return string | ||
*/ | ||
protected function getObjectType( | ||
File $phpcsFile, | ||
int $stackPtr | ||
): string { | ||
$tokens = $phpcsFile->getTokens(); | ||
if ($tokens[$stackPtr]['code'] === T_OPEN_TAG) { | ||
return 'file'; | ||
} | ||
return $tokens[$stackPtr]['content']; | ||
} | ||
|
||
/** | ||
* Get the human readable object name. | ||
* | ||
* @param File $phpcsFile | ||
* @param int $stackPtr | ||
* @return string | ||
*/ | ||
protected function getObjectName( | ||
File $phpcsFile, | ||
int $stackPtr | ||
): string { | ||
$tokens = $phpcsFile->getTokens(); | ||
if ($tokens[$stackPtr]['code'] === T_OPEN_TAG) { | ||
return basename($phpcsFile->getFilename()); | ||
} | ||
|
||
return ObjectDeclarations::getName($phpcsFile, $stackPtr); | ||
} | ||
|
||
/** | ||
* Check the docblock for a @package tag. | ||
* | ||
* @param File $phpcsFile | ||
* @param int $stackPtr | ||
* @param array $docblock | ||
* @return bool Whether any package tag was found, whether or not it was correct | ||
*/ | ||
protected function checkDocblock( | ||
File $phpcsFile, | ||
int $stackPtr, | ||
array $docblock | ||
): bool { | ||
$tokens = $phpcsFile->getTokens(); | ||
$objectName = $this->getObjectName($phpcsFile, $stackPtr); | ||
$objectType = $this->getObjectType($phpcsFile, $stackPtr); | ||
$expectedPackage = MoodleUtil::getMoodleComponent($phpcsFile, true); | ||
|
||
$packageTokens = Docblocks::getMatchingDocTags($phpcsFile, $stackPtr, '@package'); | ||
if (empty($packageTokens)) { | ||
$fix = $phpcsFile->addFixableError( | ||
'DocBlock missing a @package tag for %s %s. Expected @package %s', | ||
$stackPtr, | ||
'Missing', | ||
[$objectType, $objectName, $expectedPackage] | ||
); | ||
|
||
if ($fix) { | ||
$phpcsFile->fixer->beginChangeset(); | ||
$phpcsFile->fixer->addContentBefore($docblock['comment_closer'], '* @package ' . $expectedPackage . PHP_EOL . ' '); | ||
$phpcsFile->fixer->endChangeset(); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
if (count($packageTokens) > 1) { | ||
$fix = $phpcsFile->addFixableError( | ||
'More than one @package tag found in %s %s.', | ||
$stackPtr, | ||
'Multiple', | ||
[$objectType, $objectName] | ||
); | ||
|
||
if ($fix) { | ||
$phpcsFile->fixer->beginChangeset(); | ||
$validTokenFound = false; | ||
|
||
foreach ($packageTokens as $i => $packageToken) { | ||
$packageValuePtr = $phpcsFile->findNext( | ||
T_DOC_COMMENT_STRING, | ||
$packageToken, | ||
$docblock['comment_closer'] | ||
); | ||
$packageValue = $tokens[$packageValuePtr]['content']; | ||
if (!$validTokenFound && $packageValue === $expectedPackage) { | ||
$validTokenFound = true; | ||
continue; | ||
} | ||
$lineNo = $tokens[$packageToken]['line']; | ||
foreach (array_keys(MoodleUtil::getTokensOnLine($phpcsFile, $lineNo)) as $lineToken) { | ||
$phpcsFile->fixer->replaceToken($lineToken, ''); | ||
} | ||
} | ||
if (!$validTokenFound) { | ||
$phpcsFile->fixer->addContentBefore($packageTokens[0], ' * @package ' . $expectedPackage . PHP_EOL); | ||
} | ||
$phpcsFile->fixer->endChangeset(); | ||
} | ||
return true; | ||
} | ||
|
||
$packageToken = reset($packageTokens); | ||
|
||
// Check the value of the package tag. | ||
$packageValuePtr = $phpcsFile->findNext( | ||
T_DOC_COMMENT_STRING, | ||
$packageToken, | ||
$docblock['comment_closer'] | ||
); | ||
$packageValue = $tokens[$packageValuePtr]['content']; | ||
|
||
// Compare to expected value. | ||
if ($packageValue === $expectedPackage) { | ||
return true; | ||
} | ||
|
||
$fix = $phpcsFile->addFixableError( | ||
'Incorrect @package tag for %s %s. Expected %s, found %s.', | ||
$packageToken, | ||
'Incorrect', | ||
[$objectType, $objectName, $expectedPackage, $packageValue] | ||
); | ||
|
||
if ($fix) { | ||
$phpcsFile->fixer->beginChangeset(); | ||
$phpcsFile->fixer->replaceToken($packageValuePtr, $expectedPackage); | ||
$phpcsFile->fixer->endChangeset(); | ||
} | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?php | ||
// This file is part of Moodle - https://moodle.org/ | ||
// | ||
// Moodle is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// Moodle is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Commenting; | ||
|
||
use MoodleHQ\MoodleCS\moodle\Tests\MoodleCSBaseTestCase; | ||
|
||
// phpcs:disable moodle.NamingConventions | ||
|
||
/** | ||
* Test the TestCaseNamesSniff sniff. | ||
* | ||
* @category test | ||
* @copyright 2024 onwards Andrew Lyons <andrew@nicols.co.uk> | ||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | ||
* | ||
* @covers \MoodleHQ\MoodleCS\moodle\Sniffs\Commenting\PackageSniff | ||
*/ | ||
class PackageSniffTest extends MoodleCSBaseTestCase | ||
{ | ||
|
||
/** | ||
* @dataProvider package_correctness_provider | ||
*/ | ||
public function test_package_correctness( | ||
string $fixture, | ||
array $errors, | ||
array $warnings | ||
): void { | ||
$this->set_standard('moodle'); | ||
$this->set_sniff('moodle.Commenting.Package'); | ||
$this->set_fixture(sprintf("%s/fixtures/%s.php", __DIR__, $fixture)); | ||
$this->set_warnings($warnings); | ||
$this->set_errors($errors); | ||
$this->set_component_mapping([ | ||
'local_codechecker' => dirname(__DIR__), | ||
]); | ||
|
||
$this->verify_cs_results(); | ||
} | ||
|
||
public static function package_correctness_provider(): array { | ||
return [ | ||
'Standard fixes' => [ | ||
'fixture' => 'package_tags', | ||
'errors' => [ | ||
18 => 'DocBlock missing a @package tag for function package_missing. Expected @package local_codechecker', | ||
31 => 'DocBlock missing a @package tag for class package_absent. Expected @package local_codechecker', | ||
34 => 'Missing doc comment for function missing_docblock_in_function', | ||
38 => 'Missing doc comment for class missing_docblock_in_class', | ||
42 => 'Incorrect @package tag for function package_wrong_in_function. Expected local_codechecker, found wrong_package.', | ||
48 => 'Incorrect @package tag for class package_wrong_in_class. Expected local_codechecker, found wrong_package.', | ||
57 => 'More than one @package tag found in function package_multiple_in_function', | ||
64 => 'More than one @package tag found in class package_multiple_in_class', | ||
71 => 'More than one @package tag found in function package_multiple_in_function_all_wrong', | ||
78 => 'More than one @package tag found in class package_multiple_in_class_all_wrong', | ||
85 => 'More than one @package tag found in interface package_multiple_in_interface_all_wrong', | ||
92 => 'More than one @package tag found in trait package_multiple_in_trait_all_wrong', | ||
95 => 'Missing doc comment for interface missing_docblock_interface', | ||
101 => 'DocBlock missing a @package tag for interface missing_package_interface. Expected @package local_codechecker', | ||
106 => 'Incorrect @package tag for interface incorrect_package_interface. Expected local_codechecker, found local_codecheckers.', | ||
118 => 'Missing doc comment for trait missing_docblock_trait', | ||
124 => 'DocBlock missing a @package tag for trait missing_package_trait. Expected @package local_codechecker', | ||
129 => 'Incorrect @package tag for trait incorrect_package_trait. Expected local_codechecker, found local_codecheckers.', | ||
], | ||
'warnings' => [], | ||
], | ||
'File level tag (wrong)' => [ | ||
'fixture' => 'package_tags_file_wrong', | ||
'errors' => [ | ||
20 => 'Incorrect @package tag for file package_tags_file_wrong.php. Expected local_codechecker, found core.', | ||
], | ||
'warnings' => [], | ||
], | ||
'File level tag (right)' => [ | ||
'fixture' => 'package_tags_file_right', | ||
'errors' => [], | ||
'warnings' => [], | ||
], | ||
]; | ||
} | ||
} |
Oops, something went wrong.