Skip to content

Add rule suppresion for a line or scope #1211

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

Closed
wants to merge 3 commits into from
Closed
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
76 changes: 75 additions & 1 deletion CodeSniffer/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ class PHP_CodeSniffer_File
*/
private $_ignoredCodes = array();

/**
* An array of rules to ignore for a given token range
*
* @var array()
*/
private $_ignoredRulesForLines = array();

/**
* The total number of errors raised.
*
Expand Down Expand Up @@ -513,6 +520,8 @@ public function start($contents=null)
$listenerClass = $this->phpcs->sniffCodes[$listenerCode];
$this->phpcs->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
}
} else if (strpos($token['content'], '@codingStandardsIgnoreRule') !== false) {
$this->_addIgnoreRule($token, $stackPtr);
}//end if
}//end if
}//end if
Expand Down Expand Up @@ -993,6 +1002,15 @@ private function _addError($error, $line, $column, $code, $data, $severity, $fix
return false;
}

if (isset($this->_ignoredRulesForLines[$this->_activeListener]) === true) {
foreach ($this->_ignoredRulesForLines[$this->_activeListener] as $ignoredLinesRange) {
list($lineFrom, $lineTo) = $ignoredLinesRange;
if ($line >= $lineFrom && $line <= $lineTo) {
return false;
}
}
}

// Work out which sniff generated the error.
if (substr($code, 0, 9) === 'Internal.') {
// Any internal message.
Expand Down Expand Up @@ -1140,6 +1158,15 @@ private function _addWarning($warning, $line, $column, $code, $data, $severity,
return false;
}

if (isset($this->_ignoredRulesForLines[$this->_activeListener]) === true) {
foreach ($this->_ignoredRulesForLines[$this->_activeListener] as $ignoredLinesRange) {
list($lineFrom, $lineTo) = $ignoredLinesRange;
if ($line >= $lineFrom && $line <= $lineTo) {
return false;
}
}
}

// Work out which sniff generated the warning.
if (substr($code, 0, 9) === 'Internal.') {
// Any internal message.
Expand Down Expand Up @@ -1632,8 +1659,12 @@ private static function _createPositionMap(&$tokens, $tokenizer, $eolChar, $enco
self::$_ignoredLines[($tokens[$i]['line'] + 1)] = true;
// Ignore this comment too.
self::$_ignoredLines[$tokens[$i]['line']] = true;
} else if ($ignoring === false
&& strpos($tokens[$i]['content'], '@codingStandardsIgnoreRule') !== false
) {
self::$_ignoredLines[$tokens[$i]['line']] = true;
}
}
}//end if
}//end if

if ($ignoring === true) {
Expand Down Expand Up @@ -2644,6 +2675,49 @@ private static function _createLevelMap(&$tokens, $tokenizer, $eolChar)
}//end _createLevelMap()


/**
* Adds a rule to be ignored if the given token is followed by a function declaration
*
* @param array $token The document tag token for the ignore rule
* @param int $stackPtr The stack position where the token was found
*
* @return void
*/
private function _addIgnoreRule($token, $stackPtr)
{
$ignoredTokens = PHP_CodeSniffer_Tokens::$emptyTokens;
$nextTokenPtr = $this->findNext($ignoredTokens, ($stackPtr + 1), null, true, null, true);
$ruleNameRegex = '/@codingStandardsIgnoreRule\s*\(\s*([^)\s]++)\s*\)/i';

if ($nextTokenPtr === false || preg_match($ruleNameRegex, $token['content'], $matches) === 0) {
return;
}

$rule = $matches[1];
if (strpos($rule, '.') !== false) {
// Convert rule name to class name.
$rule = explode('.', $rule);
array_splice($rule, 1, 0, array('Sniffs'));
$rule = implode('_', $rule).'Sniff';
}

$scopeOpenerPtr = $this->findFirstOnLine(PHP_CodeSniffer_Tokens::$scopeOpeners, $nextTokenPtr);
if ($scopeOpenerPtr === false) {
$token = $this->_tokens[$nextTokenPtr];
$range = array_fill(0, 2, $token['line']);
} else {
$token = $this->_tokens[$scopeOpenerPtr];
$range = array(
$token['line'],
$this->_tokens[$token['scope_closer']]['line'],
);
}

$this->_ignoredRulesForLines[$rule][] = $range;

}//end _addIgnoreRule()


/**
* Returns the declaration names for T_CLASS, T_INTERFACE and T_FUNCTION tokens.
*
Expand Down
37 changes: 37 additions & 0 deletions tests/Core/ErrorSuppressionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,43 @@ public function testSuppressFile()

}//end testSuppressFile()

public function testSuppressRule()
{
$phpcs = new PHP_CodeSniffer();
$phpcs->initStandard('Generic', array('Generic.Commenting.Todo'));

// Process without suppression.
$content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'function foo() {'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'}'.PHP_EOL.'}';
$file = $phpcs->processFile('ruleNonSuppressionTest.php', $content);
$warnings = $file->getWarnings();
$numWarnings = $file->getWarningCount();
$this->assertEquals(1, $numWarnings);
$this->assertEquals(1, count($warnings));

// Process with inline comment suppression
$content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'// @codingStandardsIgnoreRule(Generic.Commenting.Todo)'.PHP_EOL.'function foo() {'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'}'.PHP_EOL.'}';
$file = $phpcs->processFile('ruleSuppressionTest.php', $content);
$warnings = $file->getWarnings();
$numWarnings = $file->getWarningCount();
$this->assertEquals(0, $numWarnings);
$this->assertEquals(0, count($warnings));

// Process with multiline comment suppression
$content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'/*'.PHP_EOL.'* @codingStandardsIgnoreRule(Generic.Commenting.Todo)'.PHP_EOL.'*/'.PHP_EOL.'function foo() {'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'}'.PHP_EOL.'}';
$file = $phpcs->processFile('ruleSuppressionTest.php', $content);
$warnings = $file->getWarnings();
$numWarnings = $file->getWarningCount();
$this->assertEquals(0, $numWarnings);
$this->assertEquals(0, count($warnings));

// Process with docblock suppression
$content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'/**'.PHP_EOL.'* @codingStandardsIgnoreRule(Generic.Commenting.Todo)'.PHP_EOL.'*/'.PHP_EOL.'function foo() {'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'}'.PHP_EOL.'}';
$file = $phpcs->processFile('ruleSuppressionTest.php', $content);
$warnings = $file->getWarnings();
$numWarnings = $file->getWarningCount();
$this->assertEquals(0, $numWarnings);
$this->assertEquals(0, count($warnings));
}//end testSuppressRule()

}//end class

Expand Down