Skip to content

Commit

Permalink
Add new sniffs to test validity of phpdoc tags
Browse files Browse the repository at this point in the history
Replaces the following sniffs from moodlecheck:
- phpdocsinvalidtag
- phpdocsnotrecommendedtag
- phpdocsinvalidpathtag
  • Loading branch information
andrewnicols committed Mar 17, 2024
1 parent 623544c commit 9c7b243
Show file tree
Hide file tree
Showing 10 changed files with 1,220 additions and 8 deletions.
135 changes: 135 additions & 0 deletions moodle/Sniffs/Commenting/ValidTagsSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?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\Sniffs\Commenting;

use MoodleHQ\MoodleCS\moodle\Util\MoodleUtil;
use MoodleHQ\MoodleCS\moodle\Util\Docblocks;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;

/**
* Checks that valid docblock tags are in use.
*
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class ValidTagsSniff 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();

while ($docPtr = $phpcsFile->findNext(T_DOC_COMMENT_OPEN_TAG, $stackPtr)) {
$docblock = Docblocks::getDocBlock($phpcsFile, $docPtr);
foreach ($docblock['comment_tags'] as $tagPtr) {
$tagName = ltrim($tokens[$tagPtr]['content'], '@');
if (!Docblocks::isValidTag($phpcsFile, $tagPtr)) {
if (Docblocks::shouldRemoveTag($tagName)) {
$fix = $phpcsFile->addFixableError(
'Invalid docblock tag "@%s" is not supported.',
$tagPtr,
'Invalid',
[$tagName]
);
if ($fix) {
$phpcsFile->fixer->beginChangeset();
foreach ($this->getTokensOnTokenLine($phpcsFile, $tagPtr) as $tokenPtr) {
$phpcsFile->fixer->replaceToken($tokenPtr, '');
}
$phpcsFile->fixer->endChangeset();
}
} elseif ($renameTo = Docblocks::getRenameTag($tagName)) {
$fix = $phpcsFile->addFixableError(
'Incorrect docblock tag "@%s". Should be "@%s".',
$tagPtr,
'Invalid',
[$tagName, $renameTo]
);
if ($fix) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($tagPtr, "@{$renameTo}");
$phpcsFile->fixer->endChangeset();
}
} else {
$phpcsFile->addError(
'Invalid docblock tag "@%s".',
$tagPtr,
'Invalid',
[$tagName]
);
}
} elseif (!Docblocks::isRecommendedTag($tagName)) {
// The tag is valid, but not recommended.
$phpcsFile->addWarning(
'Docblock tag "@%s" is not recommended.',
$tagPtr,
'Invalid',
[$tagName]
);
}
}
$stackPtr = $docPtr + 1;
}
}

/**
* Get the tokens on the same line as the given token.
*
* @param File $phpcsFile
* @param int $ptr
* @return int[]
*/
protected function getTokensOnTokenLine(File $phpcsFile, int $ptr): array {
$tokens = $phpcsFile->getTokens();
$line = $tokens[$ptr]['line'];
$lineTokens = [];
for ($i = $ptr; $i >= 0; $i--) {
if ($tokens[$i]['line'] === $line) {
array_unshift($lineTokens, $i);
continue;
}
break;
}

$lineTokens[] = $ptr;

for ($i = $ptr; $i < count($tokens); $i++) {
if ($tokens[$i]['line'] === $line) {
$lineTokens[] = $i;
continue;
}
break;
}

return $lineTokens;
}
}
3 changes: 0 additions & 3 deletions moodle/Tests/MoodleCSBaseTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,6 @@ protected function setSniff($sniff) {
* @param string $fixture full path to the file used as input (fixture).
*/
protected function setFixture($fixture) {
if ($this->fixtureContent !== null) {
$this->fail('Fixture file content already set, cannot set it again.');
}
if (!is_readable($fixture)) {
$this->fail('Unreadable fixture passed: ' . $fixture);
}
Expand Down
90 changes: 90 additions & 0 deletions moodle/Tests/Sniffs/Commenting/ValidTagsSniffTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?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;

/**
* Test the CategorySniff sniff.
*
* @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\ValidTagsSniff
*/
class ValidTagsSniffTest extends MoodleCSBaseTestCase
{
/**
* @dataProvider provider
*/
public function testValidTags(
string $fixturePath,
string $fixtureSource,
array $errors,
array $warnings
): void {
$fixtureSource = sprintf("%s/fixtures/ValidTags/%s.php", __DIR__, $fixtureSource);
$fixtureContent = file_get_contents($fixtureSource);

$this->setStandard('moodle');
$this->setSniff('moodle.Commenting.ValidTags');
$this->setFixtureFileContent(<<<EOF
phpcs_input_file: {$fixturePath}
{$fixtureContent}
EOF);
$this->setFixture($fixtureSource);
$this->setWarnings($warnings);
$this->setErrors($errors);

$this->verifyCsResults();
}

public static function provider(): array {
return [
'Unit test file' => [
'fixturePath' => 'lib/tests/example_test.php',
'fixtureSource' => 'unit_test',
'errors' => [
11 => 'Incorrect docblock tag "@returns". Should be "@return"',
12 => 'Invalid docblock tag "@void"',
55 => 'Invalid docblock tag "@small"',
56 => 'Invalid docblock tag "@zzzing"',
57 => 'Invalid docblock tag "@inheritdoc"',
],
'warnings' => [],
],
'Standard file' => [
'fixturePath' => 'lib/classes/example.php',
'fixtureSource' => 'general',
'errors' => [
28 => 'Invalid docblock tag "@covers"',
29 => 'Invalid docblock tag "@dataProvider"',
30 => 'Invalid docblock tag "@group"',
31 => 'Invalid docblock tag "@small"',
32 => 'Invalid docblock tag "@zzzing"',
33 => 'Invalid docblock tag "@inheritdoc"',
238 => 'Invalid docblock tag "@void" is not supported.',
247 => 'Incorrect docblock tag "@returns". Should be "@return"',
],
'warnings' => [
248 => 'Docblock tag "@version" is not recommended.'
],
],
];
}
}
Loading

0 comments on commit 9c7b243

Please sign in to comment.