Skip to content

Commit

Permalink
Add sniff to check for first line descriptions (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Mar 27, 2024
1 parent 5af7ee0 commit ad8be34
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 0 deletions.
102 changes: 102 additions & 0 deletions moodle/Sniffs/Commenting/DocblockDescriptionSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?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 WARRANdTY; 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\Docblocks;
use MoodleHQ\MoodleCS\moodle\Util\TokenUtil;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;

/**
* Checks that all docblocks for a main scope have a simple description.
*
* This is typically a one-line description.
*
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class DocblockDescriptionSniff 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();
$toCheck = [];

$docblockPtr = Docblocks::getDocBlockPointer($phpcsFile, $stackPtr);
if ($docblockPtr !== null) {
$toCheck[$stackPtr] = $docblockPtr;
}
$find = Tokens::$ooScopeTokens;
$find[T_FUNCTION] = T_FUNCTION;

$typePtr = $stackPtr + 1;
while ($typePtr = $phpcsFile->findNext($find, $typePtr + 1)) {
$docblockPtr = Docblocks::getDocBlockPointer($phpcsFile, $typePtr);
if ($docblockPtr === null) {
// Other sniffs check for missing blocks. Not my job.
continue;
}

$toCheck[$typePtr] = $docblockPtr;
}

foreach ($toCheck as $typePtr => $docblockPtr) {
$docblock = $tokens[$docblockPtr];
if (count($docblock['comment_tags'])) {
$stopAt = reset($docblock['comment_tags']);
} else {
$stopAt = $docblock['comment_closer'];
}
$faultAtLine = $tokens[$stopAt]['line'];

// Skip to the next T_DOC_COMMENT_STAR line. We do not accept single line docblocks.
$docblockLinePtr = $docblockPtr;
while ($docblockLinePtr = $phpcsFile->findNext(T_DOC_COMMENT_STAR, $docblockLinePtr + 1, $stopAt)) {
if ($tokens[$docblockLinePtr]['line'] !== $faultAtLine) {
continue 2;
}
break;
}

$objectName = TokenUtil::getObjectName($phpcsFile, $typePtr);
$objectType = TokenUtil::getObjectType($phpcsFile, $typePtr);

$phpcsFile->addError(
'No one-line description found in phpdocs for docblock of %s %s',
$typePtr,
'Missing',
[$objectType, $objectName]
);
}
}
}
90 changes: 90 additions & 0 deletions moodle/Tests/Sniffs/Commenting/DocblockDescriptionSniffTest.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;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\DummyFile;
use PHP_CodeSniffer\Ruleset;

/**
* Test the MissingDocblockSniff 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\DocblockDescriptionSniff
*/
class DocblockDescriptionSniffTest extends MoodleCSBaseTestCase
{
/**
* @dataProvider fixtureProvider
*/
public function testFixtures(
string $fixture,
?string $fixtureFilename,
array $errors,
array $warnings
): void {
$this->setStandard('moodle');
$this->setSniff('moodle.Commenting.DocblockDescription');
$this->setFixture(sprintf("%s/fixtures/DocblockDescription/%s.php", __DIR__, $fixture), $fixtureFilename);
$this->setWarnings($warnings);
$this->setErrors($errors);

$this->verifyCsResults();
}

public static function fixtureProvider(): array {
$cases = [
'Standard tests' => [
'fixture' => 'standard',
'fixtureFilename' => null,
'errors' => [
22 => 'No one-line description found in phpdocs for docblock of function method_with_param_docblock',
47 => 'No one-line description found in phpdocs for docblock of class class_with_docblock_but_no_description',
51 => 'No one-line description found in phpdocs for docblock of interface int_with_docblock_but_no_description',
55 => 'No one-line description found in phpdocs for docblock of trait trait_with_docblock_but_no_description',
60 => 'No one-line description found in phpdocs for docblock of '
. 'function function_with_docblock_but_no_description',
],
'warnings' => [
],
],
'No file docblock' => [
'fixture' => 'no_file_docblock',
'fixtureFilename' => null,
'errors' => [
],
'warnings' => [
],
],
'No description for file docblock' => [
'fixture' => 'no_description_in_file',
'fixtureFilename' => null,
'errors' => [
1 => 'No one-line description found in phpdocs for docblock of file no_description_in_file',
],
'warnings' => [
],
],
];

return $cases;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\PHPUnit;

/**
* @license
*/

/**
* Class level docblock.
*/
class class_with_docblock {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\PHPUnit;

class no_docblock {}

/**
* Class level docblock.
*/
class class_with_docblock {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\PHPUnit;

/**
* File level docblock.
*/

/**
* Class level docblock.
*/
class class_with_docblock {
/**
* Method level docblock.
*/
public function method_with_docblock() {}
public function method_no_docblock() {}

/**
* @param string $example
*/
public function method_with_param_docblock($example) {}
}
class no_docblock {}

/**
* Description of the interface.
*/
interface int_with_docblock {}
interface int_no_docblock {}

/**
* Description of the Trait.
*/
trait trait_with_docblock {}
trait trait_no_docblock {}

/**
* Description of the function.
*/
function function_with_docblock() {}
function function_no_docblock() {}

/**
* @license
*/
class class_with_docblock_but_no_description {}
/**
* @license
*/
interface int_with_docblock_but_no_description {}
/**
* @license
*/
trait trait_with_docblock_but_no_description {}

/**
* @license
*/
function function_with_docblock_but_no_description() {}

0 comments on commit ad8be34

Please sign in to comment.