Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Commenting\DisallowEmptyComment sniff #49

Merged
merged 1 commit into from
Nov 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?php

declare(strict_types=1);

namespace WebimpressCodingStandard\Sniffs\Commenting;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Common;

use function preg_match;
use function preg_replace;
use function strpos;
use function trim;

use const T_COMMENT;
use const T_DOC_COMMENT_CLOSE_TAG;
use const T_DOC_COMMENT_OPEN_TAG;
use const T_DOC_COMMENT_STAR;
use const T_DOC_COMMENT_WHITESPACE;
use const T_WHITESPACE;

class DisallowEmptyCommentSniff implements Sniff
{
/**
* @return int[]
*/
public function register() : array
{
return [
T_DOC_COMMENT_OPEN_TAG,
T_COMMENT,
];
}

/**
* @param int $stackPtr
*/
public function process(File $phpcsFile, $stackPtr) : int
{
$tokens = $phpcsFile->getTokens();

if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_OPEN_TAG) {
$next = $phpcsFile->findNext([T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR], $stackPtr + 1, null, true);

if ($tokens[$next]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
$error = 'Empty doc-block comment';

$fix = $phpcsFile->addFixableError($error, $stackPtr, 'DocBlock');
if ($fix) {
$after = $phpcsFile->findNext(T_WHITESPACE, $next + 1, null, true);
$phpcsFile->fixer->beginChangeset();
for ($i = $stackPtr; $i < $after; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}

return $stackPtr + 1;
}

$line = $tokens[$stackPtr]['line'];
$comment = trim($tokens[$stackPtr]['content']);
if (strpos($comment, '/*') === 0) {
$i = $stackPtr;
$content = '';
do {
$content .= $tokens[$i]['content'];
} while ($tokens[++$i]['code'] === T_COMMENT);

$end = $i;

if (preg_replace('#[\s\*/]#', '', $content) === '') {
$fix = $phpcsFile->addFixableError('Empty comment', $stackPtr, 'Multiline');
if ($fix) {
$next = $phpcsFile->findNext(T_WHITESPACE, $i, null, true) ?: $phpcsFile->numTokens;
$phpcsFile->fixer->beginChangeset();
for ($i = $stackPtr; $i < $next; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}

return $end;
}

$newContent = preg_replace('#(^/\*\s*?$)(?:\n^[\s\*]*$)+#m', '\\1', $content);
$newContent = preg_replace('#(?:^[\s\*]*$\n)+(\s*?\*/)#m', '\\1', $newContent);
$newContent = preg_replace('#(^[\s\*]*$\n){2,}#m', '\\1', $newContent);

if ($newContent !== $content) {
$error = 'Redundant empty lines in comment; found: %s but expected %s';
$data = [
Common::prepareForOutput($content),
Common::prepareForOutput($newContent),
];

$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Lines', $data);
if ($fix) {
$phpcsFile->fixer->beginChangeset();
while (--$i >= $stackPtr) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->addContent($stackPtr, $newContent);
$phpcsFile->fixer->endChangeset();
}
}

return $end;
}

if ($this->isEmptyComment($comment)) {
$before = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true);
$after = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true);

$hasBefore = false;
$hasAfter = false;

if ($tokens[$before]['code'] === T_COMMENT
&& $tokens[$before]['line'] === $line - 1
&& ! $this->isEmptyComment($tokens[$before]['content'])
) {
$hasBefore = true;
}

if ($tokens[$after]['code'] === T_COMMENT
&& $tokens[$after]['line'] === $line + 1
&& ! $this->isEmptyComment($tokens[$after]['content'])
) {
$hasAfter = true;
}

if (! $hasBefore || ! $hasAfter) {
$fix = $phpcsFile->addFixableError('Empty inline comment ' . $comment, $stackPtr, 'Inline');
if ($fix) {
$phpcsFile->fixer->replaceToken($stackPtr, '');
}
}
}

return $stackPtr + 1;
}

private function isEmptyComment(string $comment) : bool
{
return preg_match('@^(//|#)[\s/#*-]*$@', $comment) === 1;
}
}
101 changes: 101 additions & 0 deletions test/Sniffs/Commenting/DisallowEmptyCommentUnitTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php
/**
* non empty doc-block comment
*/

/**
*
*/

/**

*/

/** */

/* */

/* * */

/*
*/

/*
*
*/

/*

*/

/*
*
*
*/

//

#

// Hello
//
// Above line can be empty

# The same
#
# here

/*
* Below line cannot be empty
*
*/

/*
*
* Above line cannot be empty
*/

//
// Above line cannot be empty

// Below line cannot be empty
//

// Mixed types: Below line cannot be empty.
//
#
# Above and below lines cannot be empty.
#
/* something */

/****
*
*/

/*

Remove empty lines above and below

*/

/*
*
*
* Multi-line
*
*
* comment
*
*
*/

// / / / / / / / / / / / /

//////////////////////////

///
///

# # # #

#######
46 changes: 46 additions & 0 deletions test/Sniffs/Commenting/DisallowEmptyCommentUnitTest.inc.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
/**
* non empty doc-block comment
*/



// Hello
//
// Above line can be empty

# The same
#
# here

/*
* Below line cannot be empty
*/

/*
* Above line cannot be empty
*/

// Above line cannot be empty

// Below line cannot be empty

// Mixed types: Below line cannot be empty.
# Above and below lines cannot be empty.
#
/* something */

/*
Remove empty lines above and below
*/

/*
* Multi-line
*
* comment
*/





47 changes: 47 additions & 0 deletions test/Sniffs/Commenting/DisallowEmptyCommentUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace WebimpressCodingStandardTest\Sniffs\Commenting;

use WebimpressCodingStandardTest\Sniffs\AbstractTestCase;

class DisallowEmptyCommentUnitTest extends AbstractTestCase
{
protected function getErrorList(string $testFile = '') : array
{
return [
6 => 1,
10 => 1,
14 => 1,
16 => 1,
18 => 1,
20 => 1,
23 => 1,
27 => 1,
31 => 1,
36 => 1,
38 => 1,
48 => 1,
53 => 1,
58 => 1,
62 => 1,
65 => 1,
66 => 1,
71 => 1,
75 => 1,
81 => 1,
92 => 1,
94 => 1,
96 => 1,
97 => 1,
99 => 1,
101 => 1,
];
}

protected function getWarningList(string $testFile = '') : array
{
return [];
}
}