Skip to content

Commit

Permalink
The phpcs:disable and phpcs:ignore comments can now selectively ignor…
Browse files Browse the repository at this point in the history
…e specific sniffs (request #604)
  • Loading branch information
gsherwood committed Oct 30, 2017
1 parent ac151a8 commit 1f07a69
Show file tree
Hide file tree
Showing 4 changed files with 433 additions and 62 deletions.
7 changes: 7 additions & 0 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
--- phpcs:ignore has the token T_PHPCS_IGNORE
--- phpcs:set has the token T_PHPCS_SET

- The phpcs:disable and phpcs:ignore comments can now selectively ignore specific sniffs (request #604)
-- E.g., phpcs:disable Generic.Commenting.Todo.Found for a specific message
-- E.g., phpcs:disable Generic.Commenting.Todo for a whole sniff
-- E.g., phpcs:disable Generic.Commenting for a whole category of sniffs
-- E.g., phpcs:disable Generic for a whole standard
-- Multiple sniff codes can be specified by comma seperating them
--- E.g., phpcs:disable Generic.Commenting.Todo,PSR1.Files
- @codingStandardsIgnoreLine comments now only ignore the following line if they are on a line by themselves
-- If they are at the end of an existing line, they will only ignore the line they are on
-- Stops some lines from accidently being ignored
Expand Down
24 changes: 16 additions & 8 deletions src/Files/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -804,17 +804,11 @@ public function addFixableWarning(
*/
protected function addMessage($error, $message, $line, $column, $code, $data, $severity, $fixable)
{
if (isset($this->tokenizer->ignoredLines[$line]) === true) {
// Check if this line is ignoring all message codes.
if (isset($this->tokenizer->ignoredLines[$line]['all']) === true) {
return false;
}

$includeAll = true;
if ($this->configCache['cache'] === false
|| $this->configCache['recordErrors'] === false
) {
$includeAll = false;
}

// Work out which sniff generated the message.
$parts = explode('.', $code);
if ($parts[0] === 'Internal') {
Expand All @@ -841,6 +835,20 @@ protected function addMessage($error, $message, $line, $column, $code, $data, $s
);
}//end if

// Check if this line is ignoring this specific message.
foreach ($checkCodes as $checkCode) {
if (isset($this->tokenizer->ignoredLines[$line][$checkCode]) === true) {
return false;
}
}

$includeAll = true;
if ($this->configCache['cache'] === false
|| $this->configCache['recordErrors'] === false
) {
$includeAll = false;
}

// Filter out any messages for sniffs that shouldn't have run
// due to the use of the --sniffs command line argument.
if ($includeAll === false
Expand Down
187 changes: 139 additions & 48 deletions src/Tokenizers/Tokenizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private function createPositionMap()
$currColumn = 1;
$lineNumber = 1;
$eolLen = (strlen($this->eolChar) * -1);
$ignoring = false;
$ignoring = null;
$inTests = defined('PHP_CODESNIFFER_IN_TESTS');

$checkEncoding = false;
Expand Down Expand Up @@ -225,38 +225,72 @@ private function createPositionMap()
|| ($inTests === true && $this->tokens[$i]['code'] === T_INLINE_HTML))
) {
$commentText = ltrim($this->tokens[$i]['content'], ' /*');
$commentText = rtrim($commentText, " */\r\n");
$commentTextLower = strtolower($commentText);
if (strpos($commentText, '@codingStandards') !== false) {
if ($ignoring === false
// If this comment is the only thing on the line, it tells us
// to ignore the following line. If the line contains other content
// then we are just ignoring this one single line.
$ownLine = false;
for ($prev = ($i - 1); $prev >= 0; $prev--) {
if ($this->tokens[$prev]['code'] === T_WHITESPACE) {
continue;
}

break;
}

if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {

This comment has been minimized.

Copy link
@jrfnl

jrfnl Oct 31, 2017

Contributor

This change causes an Undefined offset -1 error when a @codingStandardsChangeSetting command is at the very start of a (mixed HTML/PHP) file. Either the for() should be wrapped in a if ($i !== 0) {} or some other defensive coding check is needed.
See: https://travis-ci.org/WordPress-Coding-Standards/WordPress-Coding-Standards/jobs/295128540

Came across this with test case files for WPCS where properties need to be set before the T_OPEN_TAG, like for a filename sniff.

This comment has been minimized.

Copy link
@jrfnl

jrfnl Oct 31, 2017

Contributor

Argh.. just noticed you've already fixed this after that Travis run had run its course 👍

Sorry for the confusion.

$ownLine = true;
}

if ($ignoring === null
&& strpos($commentText, '@codingStandardsIgnoreStart') !== false
) {
$ignoring = true;
} else if ($ignoring === true
$ignoring = array('all' => true);
if ($ownLine === true) {
$this->ignoredLines[$this->tokens[$i]['line']] = array('all' => true);
}
} else if ($ignoring !== null
&& strpos($commentText, '@codingStandardsIgnoreEnd') !== false
) {
$ignoring = false;
// Ignore this comment too.
$this->ignoredLines[$this->tokens[$i]['line']] = true;
} else if ($ignoring === false
if ($ownLine === true) {
$this->ignoredLines[$this->tokens[$i]['line']] = array('all' => true);
} else {
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}

$ignoring = null;
} else if ($ignoring === null
&& strpos($commentText, '@codingStandardsIgnoreLine') !== false
) {
// If this comment is the only thing on the line, it tells us
// to ignore the following line. If the line contains other content
// then we are just ignoring this one single line.
$this->ignoredLines[$this->tokens[$i]['line']] = true;
for ($prev = ($i - 1); $prev >= 0; $prev--) {
if ($this->tokens[$prev]['code'] === T_WHITESPACE) {
continue;
}

break;
$ignoring = array('all' => true);
if ($ownLine === true) {
$this->ignoredLines[$this->tokens[$i]['line']] = array('all' => true);
$this->ignoredLines[($this->tokens[$i]['line'] + 1)] = $ignoring;
} else {
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}

if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
$this->ignoredLines[($this->tokens[$i]['line'] + 1)] = true;
}
$ignoring = null;
}//end if
} else if (substr($commentTextLower, 0, 6) === 'phpcs:') {
// If this comment is the only thing on the line, it tells us
// to ignore the following line. If the line contains other content
// then we are just ignoring this one single line.
$ownLine = false;
for ($prev = ($i - 1); $prev >= 0; $prev--) {
if ($this->tokens[$prev]['code'] === T_WHITESPACE) {
continue;
}

break;
}

if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
$ownLine = true;
}

if (substr($commentTextLower, 0, 9) === 'phpcs:set') {
// Ignore standards for lines that change sniff settings.
$this->ignoredLines[$this->tokens[$i]['line']] = true;
Expand All @@ -266,46 +300,103 @@ private function createPositionMap()
// The whole file will be ignored, but at least set the correct token.
$this->tokens[$i]['code'] = T_PHPCS_IGNORE_FILE;
$this->tokens[$i]['type'] = 'T_PHPCS_IGNORE_FILE';
} else if ($ignoring === false
&& substr($commentTextLower, 0, 13) === 'phpcs:disable'
) {
$ignoring = true;
$this->tokens[$i]['code'] = T_PHPCS_DISABLE;
$this->tokens[$i]['type'] = 'T_PHPCS_DISABLE';
} else if ($ignoring === true
} else if (substr($commentTextLower, 0, 13) === 'phpcs:disable') {
if ($ownLine === true) {
// Completely ignore the comment line.
$this->ignoredLines[$this->tokens[$i]['line']] = array('all' => true);
}

if ($ignoring === null) {
$ignoring = array();
}

$disabledSniffs = array();

$additionalText = substr($commentText, 14);
if ($additionalText === false) {
$ignoring = array('all' => true);
} else {
$parts = explode(',', substr($commentText, 13));
foreach ($parts as $sniffCode) {
$sniffCode = trim($sniffCode);
$disabledSniffs[$sniffCode] = true;
$ignoring[$sniffCode] = true;
}
}

$this->tokens[$i]['code'] = T_PHPCS_DISABLE;
$this->tokens[$i]['type'] = 'T_PHPCS_DISABLE';
$this->tokens[$i]['sniffCodes'] = $disabledSniffs;
} else if ($ignoring !== null
&& substr($commentTextLower, 0, 12) === 'phpcs:enable'
) {
$ignoring = false;
$this->ignoredLines[$this->tokens[$i]['line']] = true;
$this->tokens[$i]['code'] = T_PHPCS_ENABLE;
$this->tokens[$i]['type'] = 'T_PHPCS_ENABLE';
} else if ($ignoring === false
$enabledSniffs = array();

$additionalText = substr($commentText, 13);
if ($additionalText === false) {
$ignoring = null;
} else {
$parts = explode(',', substr($commentText, 13));
foreach ($parts as $sniffCode) {
$sniffCode = trim($sniffCode);
$enabledSniffs[$sniffCode] = true;
if (isset($ignoring[$sniffCode]) === true) {
unset($ignoring[$sniffCode]);
}
}

if (empty($ignoring) === true) {
$ignoring = null;
}
}

if ($ownLine === true) {
// Completely ignore the comment line.
$this->ignoredLines[$this->tokens[$i]['line']] = array('all' => true);
} else {
// The comment is on the same line as the code it is ignoring,
// so respect the new ignore rules.
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}

$this->tokens[$i]['code'] = T_PHPCS_ENABLE;
$this->tokens[$i]['type'] = 'T_PHPCS_ENABLE';
$this->tokens[$i]['sniffCodes'] = $enabledSniffs;
} else if ($ignoring === null
&& substr($commentTextLower, 0, 12) === 'phpcs:ignore'
) {
// If this comment is the only thing on the line, it tells us
// to ignore the following line. If the line contains other content
// then we are just ignoring this one single line.
$this->ignoredLines[$this->tokens[$i]['line']] = true;
for ($prev = ($i - 1); $prev >= 0; $prev--) {
if ($this->tokens[$prev]['code'] === T_WHITESPACE) {
continue;
}
$ignoreRules = array();

break;
$additionalText = substr($commentText, 13);
if ($additionalText === false) {
$ignoreRules = array('all' => true);
} else {
$parts = explode(',', substr($commentText, 13));
foreach ($parts as $sniffCode) {
$ignoreRules[trim($sniffCode)] = true;
}
}

if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
$this->ignoredLines[($this->tokens[$i]['line'] + 1)] = true;
if ($ownLine === true) {
// Completely ignore the comment line, and set the folllowing
// line to include the ignore rules we've set.
$this->ignoredLines[$this->tokens[$i]['line']] = array('all' => true);
$this->ignoredLines[($this->tokens[$i]['line'] + 1)] = $ignoreRules;
} else {
// The comment is on the same line as the code it is ignoring,
// so respect the ignore rules it set.
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoreRules;
}

$this->tokens[$i]['code'] = T_PHPCS_IGNORE;
$this->tokens[$i]['type'] = 'T_PHPCS_IGNORE';
$this->tokens[$i]['code'] = T_PHPCS_IGNORE;
$this->tokens[$i]['type'] = 'T_PHPCS_IGNORE';
$this->tokens[$i]['sniffCodes'] = $ignoreRules;
}//end if
}//end if
}//end if

if ($ignoring === true) {
$this->ignoredLines[$this->tokens[$i]['line']] = true;
if ($ignoring !== null && isset($this->ignoredLines[$this->tokens[$i]['line']]) === false) {
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}
}//end for

Expand Down
Loading

0 comments on commit 1f07a69

Please sign in to comment.