diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..454ad965 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +composer.phar diff --git a/composer.json b/composer.json index 2ee777e5..53229b05 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "license": "GPL-3.0-or-later", "require": { "php": ">=7.4", - "moodlehq/moodle-cs": "^3.3.9", + "moodlehq/moodle-cs": "^v3.3.10", "phpcompatibility/php-compatibility": "dev-develop#0a17f9ed" }, "config": { diff --git a/composer.lock b/composer.lock index 6da1f498..8bd99525 100644 --- a/composer.lock +++ b/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": "58cae4c2ddbeacb3ed22b072ffaf089a", + "content-hash": "1f240b14e4e06c4a29dfe747bfe9fbb1", "packages": [ { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -86,16 +86,16 @@ }, { "name": "moodlehq/moodle-cs", - "version": "v3.3.9", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/moodlehq/moodle-cs.git", - "reference": "5503396df563cbe3244b67625246664ba5b663f1" + "reference": "13192ea4f0e7ac0c94571a6c7e33948db740f1ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/moodlehq/moodle-cs/zipball/5503396df563cbe3244b67625246664ba5b663f1", - "reference": "5503396df563cbe3244b67625246664ba5b663f1", + "url": "https://api.github.com/repos/moodlehq/moodle-cs/zipball/13192ea4f0e7ac0c94571a6c7e33948db740f1ab", + "reference": "13192ea4f0e7ac0c94571a6c7e33948db740f1ab", "shasum": "" }, "require": { @@ -146,7 +146,7 @@ "source": "https://github.com/moodlehq/moodle-cs", "wiki": "https://github.com/moodlehq/moodle-cs/wiki" }, - "time": "2023-09-26T14:48:47+00:00" + "time": "2023-10-20T07:20:45+00:00" }, { "name": "phpcompatibility/php-compatibility", diff --git a/composer.phar b/composer.phar deleted file mode 100644 index fab56798..00000000 Binary files a/composer.phar and /dev/null differ diff --git a/tests/locallib_test.php b/tests/locallib_test.php index 22ef76a9..3cbf7b14 100644 --- a/tests/locallib_test.php +++ b/tests/locallib_test.php @@ -111,7 +111,7 @@ public static function local_codechecker_find_other_files_provider(): array { * @covers ::local_codechecker_find_other_files */ public function test_local_codechecker_find_other_files(string $path, array $ignores, - array $extensions, array $matches, array $nomatches) { + array $extensions, array $matches, array $nomatches): void { global $CFG; require_once(__DIR__ . '/../locallib.php'); @@ -149,7 +149,7 @@ public function test_local_codechecker_find_other_files(string $path, array $ign * * @covers ::local_codechecker_check_other_file */ - public function test_local_codechecker_check_other_file() { + public function test_local_codechecker_check_other_file(): void { require_once(__DIR__ . '/../locallib.php'); diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index a5ecca52..bc3538b0 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -83,17 +83,17 @@ }, { "name": "moodlehq/moodle-cs", - "version": "v3.3.9", - "version_normalized": "3.3.9.0", + "version": "v3.3.10", + "version_normalized": "3.3.10.0", "source": { "type": "git", "url": "https://github.com/moodlehq/moodle-cs.git", - "reference": "5503396df563cbe3244b67625246664ba5b663f1" + "reference": "13192ea4f0e7ac0c94571a6c7e33948db740f1ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/moodlehq/moodle-cs/zipball/5503396df563cbe3244b67625246664ba5b663f1", - "reference": "5503396df563cbe3244b67625246664ba5b663f1", + "url": "https://api.github.com/repos/moodlehq/moodle-cs/zipball/13192ea4f0e7ac0c94571a6c7e33948db740f1ab", + "reference": "13192ea4f0e7ac0c94571a6c7e33948db740f1ab", "shasum": "" }, "require": { @@ -113,7 +113,7 @@ "sebastian/phpcpd": "^6.0", "thor-juhasz/phpunit-coverage-check": "^0.3.0" }, - "time": "2023-09-26T14:48:47+00:00", + "time": "2023-10-20T07:20:45+00:00", "type": "phpcodesniffer-standard", "installation-source": "dist", "autoload": { diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 686b7e99..5347b921 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'moodlehq/local_codechecker', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '3a7e2dfb86d7f08b3ba48a2007c015b0f40e27b9', + 'reference' => '94709634e38007ebb30a695dbdf3f6f8fe7d1fb6', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -22,16 +22,16 @@ 'moodlehq/local_codechecker' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '3a7e2dfb86d7f08b3ba48a2007c015b0f40e27b9', + 'reference' => '94709634e38007ebb30a695dbdf3f6f8fe7d1fb6', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false, ), 'moodlehq/moodle-cs' => array( - 'pretty_version' => 'v3.3.9', - 'version' => '3.3.9.0', - 'reference' => '5503396df563cbe3244b67625246664ba5b663f1', + 'pretty_version' => 'v3.3.10', + 'version' => '3.3.10.0', + 'reference' => '13192ea4f0e7ac0c94571a6c7e33948db740f1ab', 'type' => 'phpcodesniffer-standard', 'install_path' => __DIR__ . '/../moodlehq/moodle-cs', 'aliases' => array(), diff --git a/vendor/moodlehq/moodle-cs/moodle/Sniffs/PHPUnit/TestReturnTypeSniff.php b/vendor/moodlehq/moodle-cs/moodle/Sniffs/PHPUnit/TestReturnTypeSniff.php new file mode 100644 index 00000000..af5776a4 --- /dev/null +++ b/vendor/moodlehq/moodle-cs/moodle/Sniffs/PHPUnit/TestReturnTypeSniff.php @@ -0,0 +1,156 @@ +. + +namespace MoodleHQ\MoodleCS\moodle\Sniffs\PHPUnit; + +// phpcs:disable moodle.NamingConventions + +use MoodleHQ\MoodleCS\moodle\Util\MoodleUtil; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; +use PHPCSUtils\Utils\FunctionDeclarations; +use PHPCSUtils\Utils\ObjectDeclarations; + +/** + * Checks that a test file has the @coversxxx annotations properly defined. + * + * @package local_codechecker + * @copyright 2022 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class TestReturnTypeSniff 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 $file The file being scanned. + * @param int $pointer The position in the stack. + */ + public function process(File $file, $pointer) + { + // Before starting any check, let's look for various things. + + // If we aren't checking Moodle 4.4dev (404) and up, nothing to check. + // Make and exception for codechecker phpunit tests, so they are run always. + if (!MoodleUtil::meetsMinimumMoodleVersion($file, 404) && !MoodleUtil::isUnitTestRunning()) { + return; // @codeCoverageIgnore + } + + // If the file is not a unit test file, nothing to check. + if (!MoodleUtil::isUnitTest($file) && !MoodleUtil::isUnitTestRunning()) { + return; // @codeCoverageIgnore + } + + // We have all we need from core, let's start processing the file. + + // Get the file tokens, for ease of use. + $tokens = $file->getTokens(); + + // We only want to do this once per file. + $prevopentag = $file->findPrevious(T_OPEN_TAG, + $pointer - 1 + ); + if ($prevopentag !== false) { + return; // @codeCoverageIgnore + } + + // Iterate over all the classes (hopefully only one, but that's not this sniff problem). + $cStart = $pointer; + while ($cStart = $file->findNext(T_CLASS, $cStart + 1)) { + if (MoodleUtil::isUnitTestCaseClass($file, $cStart) === false) { + // This class does not related to a uit test. + continue; + } + + $classInfo = ObjectDeclarations::getClassProperties($file, $cStart); + + // Iterate over all the methods in the class. + $mStart = $cStart; + while ($mStart = $file->findNext(T_FUNCTION, $mStart + 1, $tokens[$cStart]['scope_closer'])) { + $method = $file->getDeclarationName($mStart); + + // Ignore non test_xxxx() methods. + if (strpos($method, 'test_') !== 0) { + continue; + } + + // Get the function declaration. + $functionInfo = FunctionDeclarations::getProperties($file, $mStart); + + // 'return_type_token' => int|false, // The stack pointer to the start of the return type. + if ($functionInfo['return_type_token'] !== false) { + // This method has a return type. + // In most cases that will be void, but it could be anything in the + // case of chained or dependant tests. + // TODO: Detect all cases of chained tests and dependant tests. + continue; + } + + $methodNamePtr = $file->findNext(T_STRING, $mStart + 1, $tokens[$mStart]['parenthesis_opener']); + $methodEnd = $file->findNext(T_CLOSE_PARENTHESIS, $mStart + 1); + + // Detect if the method has a return statement. + // If it does, then see if it has a value or not. + $hasReturn = $file->findNext(T_RETURN, $mStart + 1, $tokens[$mStart]['scope_closer']); + $probablyVoid = !$hasReturn; + if ($hasReturn) { + $next = $file->findNext( + T_WHITESPACE, + $hasReturn + 1, + $tokens[$mStart]['scope_closer'], + true + ); + if ($tokens[$next]['code'] === T_SEMICOLON) { + $probablyVoid = true; + } + } + + $fix = false; + if ($probablyVoid) { + $fix = $file->addFixableWarning( + 'Test method %s() is missing a return type', + $methodNamePtr, + 'MissingReturnType', + [$method] + ); + } else { + $file->addWarning( + 'Test method %s() is missing a return type', + $methodNamePtr, + 'MissingReturnType', + [$method] + ); + } + + if ($fix) { + $file->fixer->beginChangeset(); + $file->fixer->addContent($methodEnd, ': void'); + $file->fixer->endChangeset(); + } + } + } + } +} diff --git a/vendor/moodlehq/moodle-cs/moodle/Util/MoodleUtil.php b/vendor/moodlehq/moodle-cs/moodle/Util/MoodleUtil.php index c091b00f..61a0daa2 100644 --- a/vendor/moodlehq/moodle-cs/moodle/Util/MoodleUtil.php +++ b/vendor/moodlehq/moodle-cs/moodle/Util/MoodleUtil.php @@ -18,11 +18,9 @@ use PHP_CodeSniffer\Config; use PHP_CodeSniffer\Exceptions\DeepExitException; -use PHP_CodeSniffer\Exceptions\RuntimeException; use PHP_CodeSniffer\Files\DummyFile; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Ruleset; -use PHP_CodeSniffer\Tokenizers\PHP; // phpcs:disable moodle.NamingConventions @@ -424,6 +422,35 @@ public static function isUnitTestRunning(): bool return defined('PHPUNIT_TEST') && PHPUNIT_TEST; } + /** + * Whether the class is a unit test case class. + * + * @param File $file + * @param int $classPtr + * @return bool + */ + public static function isUnitTestCaseClass( + File $file, + int $classPtr + ): bool { + $tokens = $file->getTokens(); + $class = $file->getDeclarationName($classPtr); + + // Only if the class is extending something. + // TODO: We could add a list of valid classes once we have a class-map available. + if (!$file->findNext(T_EXTENDS, $classPtr + 1, $tokens[$classPtr]['scope_opener'])) { + return false; + } + + // Ignore non ended "_test|_testcase" classes. + if (substr($class, -5) !== '_test' && substr($class, -9) != '_testcase') { + return false; + } + + // This is a class, which extends another class, and whose name ends in _test. + return true; + } + /** * Whether the file belongs to a version of Moodle meeting the specifeid minimum version. * diff --git a/vendor/moodlehq/moodle-cs/moodle/ruleset.xml b/vendor/moodlehq/moodle-cs/moodle/ruleset.xml index 983f8936..5a810143 100644 --- a/vendor/moodlehq/moodle-cs/moodle/ruleset.xml +++ b/vendor/moodlehq/moodle-cs/moodle/ruleset.xml @@ -110,6 +110,11 @@ --> + + + 0 @@ -124,4 +129,9 @@ + + +