Skip to content

Commit 23ee080

Browse files
committed
Add new sniffs to test validity of phpdoc tags
Replaces the following sniffs from moodlecheck: - phpdocsinvalidtag - phpdocsnotrecommendedtag - phpdocsinvalidpathtag
1 parent a0012c2 commit 23ee080

File tree

8 files changed

+768
-5
lines changed

8 files changed

+768
-5
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
// This file is part of Moodle - https://moodle.org/
4+
//
5+
// Moodle is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// Moodle is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
17+
18+
namespace MoodleHQ\MoodleCS\moodle\Sniffs\Commenting;
19+
20+
use MoodleHQ\MoodleCS\moodle\Util\MoodleUtil;
21+
use MoodleHQ\MoodleCS\moodle\Util\Docblocks;
22+
use PHP_CodeSniffer\Sniffs\Sniff;
23+
use PHP_CodeSniffer\Files\File;
24+
25+
/**
26+
* Checks that valid docblock tags are in use.
27+
*
28+
* @copyright 2024 Andrew Lyons <[email protected]>
29+
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30+
*/
31+
class ValidTagsSniff implements Sniff
32+
{
33+
/**
34+
* Register for open tag (only process once per file).
35+
*/
36+
public function register() {
37+
return [
38+
T_OPEN_TAG,
39+
];
40+
}
41+
42+
/**
43+
* Processes php files and perform various checks with file.
44+
*
45+
* @param File $phpcsFile The file being scanned.
46+
* @param int $stackPtr The position in the stack.
47+
*/
48+
public function process(File $phpcsFile, $stackPtr) {
49+
$tokens = $phpcsFile->getTokens();
50+
51+
while ($docPtr = $phpcsFile->findNext(T_DOC_COMMENT_OPEN_TAG, $stackPtr)) {
52+
$docblock = Docblocks::getDocBlock($phpcsFile, $docPtr);
53+
foreach ($docblock['comment_tags'] as $tagPtr) {
54+
$tagName = ltrim($tokens[$tagPtr]['content'], '@');
55+
if (!Docblocks::isValidTag($phpcsFile, $tagPtr)) {
56+
if (Docblocks::shouldRemoveTag($tagName)) {
57+
$fix = $phpcsFile->addFixableError(
58+
'Invalid docblock tag "@%s" is not supported.',
59+
$tagPtr,
60+
'Invalid',
61+
[$tagName]
62+
);
63+
if ($fix) {
64+
$phpcsFile->fixer->beginChangeset();
65+
foreach ($this->getTokensOnTokenLine($phpcsFile, $tagPtr) as $tokenPtr) {
66+
$phpcsFile->fixer->replaceToken($tokenPtr, '');
67+
}
68+
$phpcsFile->fixer->endChangeset();
69+
}
70+
} elseif ($renameTo = Docblocks::getRenameTag($tagName)) {
71+
$fix = $phpcsFile->addFixableError(
72+
'Incorrect docblock tag "@%s". Should be "@%s".',
73+
$tagPtr,
74+
'Invalid',
75+
[$tagName, $renameTo]
76+
);
77+
if ($fix) {
78+
$phpcsFile->fixer->beginChangeset();
79+
$phpcsFile->fixer->replaceToken($tagPtr, "@{$renameTo}");
80+
$phpcsFile->fixer->endChangeset();
81+
}
82+
} else {
83+
$phpcsFile->addError(
84+
'Invalid docblock tag "@%s".',
85+
$tagPtr,
86+
'Invalid',
87+
[$tagName]
88+
);
89+
}
90+
} elseif (!Docblocks::isRecommendedTag($tagName)) {
91+
// The tag is valid, but not recommended.
92+
$phpcsFile->addWarning(
93+
'Docblock tag "@%s" is not recommended.',
94+
$tagPtr,
95+
'Invalid',
96+
[$tagName]
97+
);
98+
}
99+
}
100+
$stackPtr = $docPtr + 1;
101+
}
102+
}
103+
104+
/**
105+
* Get the tokens on the same line as the given token.
106+
*
107+
* @param File $phpcsFile
108+
* @param int $ptr
109+
* @return int[]
110+
*/
111+
protected function getTokensOnTokenLine(File $phpcsFile, int $ptr): array {
112+
$tokens = $phpcsFile->getTokens();
113+
$line = $tokens[$ptr]['line'];
114+
$lineTokens = [];
115+
for ($i = $ptr; $i >= 0; $i--) {
116+
if ($tokens[$i]['line'] === $line) {
117+
array_unshift($lineTokens, $i);
118+
continue;
119+
}
120+
break;
121+
}
122+
123+
$lineTokens[] = $ptr;
124+
125+
for ($i = $ptr; $i < count($tokens); $i++) {
126+
if ($tokens[$i]['line'] === $line) {
127+
$lineTokens[] = $i;
128+
continue;
129+
}
130+
break;
131+
}
132+
133+
return $lineTokens;
134+
}
135+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
// This file is part of Moodle - https://moodle.org/
4+
//
5+
// Moodle is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// Moodle is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
17+
18+
namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Commenting;
19+
20+
use MoodleHQ\MoodleCS\moodle\Tests\MoodleCSBaseTestCase;
21+
22+
/**
23+
* Test the CategorySniff sniff.
24+
*
25+
* @copyright 2024 onwards Andrew Lyons <[email protected]>
26+
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27+
*
28+
* @covers \MoodleHQ\MoodleCS\moodle\Sniffs\Commenting\ValidTagsSniff
29+
*/
30+
class ValidTagsSniffTest extends MoodleCSBaseTestCase
31+
{
32+
/**
33+
* @dataProvider provider
34+
*/
35+
public function testValidTags(
36+
string $fixturePath,
37+
string $fixtureSource,
38+
array $errors,
39+
array $warnings
40+
): void {
41+
$fixtureSource = sprintf("%s/fixtures/ValidTags/%s.php", __DIR__, $fixtureSource);
42+
43+
$this->setStandard('moodle');
44+
$this->setSniff('moodle.Commenting.ValidTags');
45+
$this->setFixture($fixtureSource, $fixturePath);
46+
$this->setWarnings($warnings);
47+
$this->setErrors($errors);
48+
49+
$this->verifyCsResults();
50+
}
51+
52+
public static function provider(): array {
53+
return [
54+
'Unit test file' => [
55+
'fixturePath' => 'lib/tests/example_test.php',
56+
'fixtureSource' => 'unit_test',
57+
'errors' => [
58+
11 => 'Incorrect docblock tag "@returns". Should be "@return"',
59+
12 => 'Invalid docblock tag "@void"',
60+
55 => 'Invalid docblock tag "@small"',
61+
56 => 'Invalid docblock tag "@zzzing"',
62+
57 => 'Invalid docblock tag "@inheritdoc"',
63+
],
64+
'warnings' => [],
65+
],
66+
'Standard file' => [
67+
'fixturePath' => 'lib/classes/example.php',
68+
'fixtureSource' => 'general',
69+
'errors' => [
70+
28 => 'Invalid docblock tag "@covers"',
71+
29 => 'Invalid docblock tag "@dataProvider"',
72+
30 => 'Invalid docblock tag "@group"',
73+
31 => 'Invalid docblock tag "@small"',
74+
32 => 'Invalid docblock tag "@zzzing"',
75+
33 => 'Invalid docblock tag "@inheritdoc"',
76+
42 => 'Invalid docblock tag "@void" is not supported.',
77+
51 => 'Incorrect docblock tag "@returns". Should be "@return"',
78+
],
79+
'warnings' => [
80+
52 => 'Docblock tag "@version" is not recommended.',
81+
],
82+
],
83+
];
84+
}
85+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
/**
4+
* A fixture to verify various phpdoc tags in a general location.
5+
*
6+
* @package local_moodlecheck
7+
* @copyright 2018 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
8+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9+
*/
10+
class some_class extends \advanced_testcase {
11+
/**
12+
* Some valid tags, to verify they are ok.
13+
*
14+
* @license
15+
* @throws
16+
* @deprecated
17+
* @author
18+
* @todo
19+
*/
20+
public function all_valid_tags() {
21+
echo "yay!";
22+
}
23+
24+
/**
25+
* Some invalid tags, to verify they are detected.
26+
*
27+
* @codingStandardsIgnoreLine
28+
* @covers
29+
* @dataProvider
30+
* @group
31+
* @small
32+
* @zzzing
33+
* @inheritdoc
34+
*/
35+
public function all_invalid_tags() {
36+
echo "yoy!";
37+
}
38+
39+
/**
40+
* Incorrect tag which can be removed.
41+
*
42+
* @void
43+
*/
44+
public function incorrect_tag() {
45+
echo "yoy!";
46+
}
47+
48+
/**
49+
* Incorrect tag which can be renamed.
50+
*
51+
* @returns string
52+
* @version 4.1.0
53+
*/
54+
public function renamable_tag() {
55+
echo "yoy!";
56+
}
57+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/**
4+
* A fixture to verify various phpdoc tags in a general location.
5+
*
6+
* @package local_moodlecheck
7+
* @copyright 2018 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
8+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9+
*/
10+
class some_class extends \advanced_testcase {
11+
/**
12+
* Some valid tags, to verify they are ok.
13+
*
14+
* @license
15+
* @throws
16+
* @deprecated
17+
* @author
18+
* @todo
19+
*/
20+
public function all_valid_tags() {
21+
echo "yay!";
22+
}
23+
24+
/**
25+
* Some invalid tags, to verify they are detected.
26+
*
27+
* @codingStandardsIgnoreLine
28+
* @covers
29+
* @dataProvider
30+
* @group
31+
* @small
32+
* @zzzing
33+
* @inheritdoc
34+
*/
35+
public function all_invalid_tags() {
36+
echo "yoy!";
37+
}
38+
39+
/**
40+
* Incorrect tag which can be removed.
41+
*
42+
*/
43+
public function incorrect_tag() {
44+
echo "yoy!";
45+
}
46+
47+
/**
48+
* Incorrect tag which can be renamed.
49+
*
50+
* @return string
51+
* @version 4.1.0
52+
*/
53+
public function renamable_tag() {
54+
echo "yoy!";
55+
}
56+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/**
4+
* @covers \some_class
5+
*/
6+
class some_test extends \advanced_testcase {
7+
/**
8+
* @dataProvider provider
9+
* @covers \some_class::some_method
10+
* @param string $param1
11+
* @returns string
12+
* @void
13+
*/
14+
public function test_something() {
15+
$this->assertTrue(true);
16+
}
17+
18+
/**
19+
* Some valid tags, to verify they are ok.
20+
*
21+
* @license
22+
* @throws
23+
* @deprecated
24+
* @author
25+
* @todo
26+
*/
27+
public function all_valid_tags() {
28+
echo "yay!";
29+
}
30+
31+
/**
32+
* Some more valid tags, because we are under tests area.
33+
*
34+
* @covers
35+
* @dataProvider
36+
* @group
37+
* @runTestsInSeparateProcesses
38+
*/
39+
public function also_all_valid_tags() {
40+
echo "reyay!";
41+
}
42+
43+
/**
44+
* Some more valid tags, because we are under tests area.
45+
*/
46+
public function valid_inline_tags() {
47+
// @codeCoverageIgnoreStart
48+
echo "reyay!";
49+
// @codeCoverageIgnoreEnd
50+
}
51+
52+
/**
53+
* Some invalid tags.
54+
*
55+
* @small
56+
* @zzzing
57+
* @inheritdoc
58+
*/
59+
public function all_invalid_tags() {
60+
echo "yoy!";
61+
}
62+
}

0 commit comments

Comments
 (0)