Skip to content

Commit 807c10c

Browse files
committed
Tokenizer: support hash comment for ignore annotations
Until now, for the PHPCS native ignore annotations, only `//` slash or `/* */` star-style comments were supported. This adds support for PHPCS native ignore annotations using `#` hash-style comments. Includes unit tests and syncing of the `ltrim()` used in `File::process()` with the `ltrim()` used in `Tokenizer::createPositionMap()`.
1 parent b84c541 commit 807c10c

File tree

3 files changed

+169
-5
lines changed

3 files changed

+169
-5
lines changed

src/Files/File.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ public function process()
356356
|| $token['code'] === T_DOC_COMMENT_TAG
357357
|| ($inTests === true && $token['code'] === T_INLINE_HTML))
358358
) {
359-
$commentText = ltrim($this->tokens[$stackPtr]['content'], ' /*');
359+
$commentText = ltrim($this->tokens[$stackPtr]['content'], " \t/*#");
360360
$commentTextLower = strtolower($commentText);
361361
if (strpos($commentText, '@codingStandards') !== false) {
362362
if (strpos($commentText, '@codingStandardsIgnoreFile') !== false) {

src/Tokenizers/Tokenizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ private function createPositionMap()
250250
|| $this->tokens[$i]['code'] === T_DOC_COMMENT_TAG
251251
|| ($inTests === true && $this->tokens[$i]['code'] === T_INLINE_HTML)
252252
) {
253-
$commentText = ltrim($this->tokens[$i]['content'], " \t/*");
253+
$commentText = ltrim($this->tokens[$i]['content'], " \t/*#");
254254
$commentText = rtrim($commentText, " */\t\r\n");
255255
$commentTextLower = strtolower($commentText);
256256
if (strpos($commentText, '@codingStandards') !== false) {

tests/Core/ErrorSuppressionTest.php

Lines changed: 167 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,46 @@ public function testSuppressError()
8181
$this->assertEquals(0, $numErrors);
8282
$this->assertCount(0, $errors);
8383

84+
// Process with inline hash comment suppression.
85+
$content = '<?php '.PHP_EOL.'# phpcs:disable'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'# phpcs:enable';
86+
$file = new DummyFile($content, $ruleset, $config);
87+
$file->process();
88+
89+
$errors = $file->getErrors();
90+
$numErrors = $file->getErrorCount();
91+
$this->assertEquals(0, $numErrors);
92+
$this->assertCount(0, $errors);
93+
94+
// Process with multi-line inline hash comment suppression, tab-indented.
95+
$content = '<?php '.PHP_EOL."\t".'# For reasons'.PHP_EOL."\t".'# phpcs:disable'.PHP_EOL."\t".'$var = FALSE;'.PHP_EOL."\t".'# phpcs:enable';
96+
$file = new DummyFile($content, $ruleset, $config);
97+
$file->process();
98+
99+
$errors = $file->getErrors();
100+
$numErrors = $file->getErrorCount();
101+
$this->assertEquals(0, $numErrors);
102+
$this->assertCount(0, $errors);
103+
104+
// Process with inline hash @ comment suppression.
105+
$content = '<?php '.PHP_EOL.'# @phpcs:disable'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'# @phpcs:enable';
106+
$file = new DummyFile($content, $ruleset, $config);
107+
$file->process();
108+
109+
$errors = $file->getErrors();
110+
$numErrors = $file->getErrorCount();
111+
$this->assertEquals(0, $numErrors);
112+
$this->assertCount(0, $errors);
113+
114+
// Process with inline hash comment suppression mixed case.
115+
$content = '<?php '.PHP_EOL.'# PHPCS:Disable'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'# pHPcs:enabLE';
116+
$file = new DummyFile($content, $ruleset, $config);
117+
$file->process();
118+
119+
$errors = $file->getErrors();
120+
$numErrors = $file->getErrorCount();
121+
$this->assertEquals(0, $numErrors);
122+
$this->assertCount(0, $errors);
123+
84124
// Process with inline comment suppression (deprecated syntax).
85125
$content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'// @codingStandardsIgnoreEnd';
86126
$file = new DummyFile($content, $ruleset, $config);
@@ -217,6 +257,26 @@ public function testSuppressSomeErrors()
217257
$this->assertEquals(1, $numErrors);
218258
$this->assertCount(1, $errors);
219259

260+
// Process with suppression (hash comment).
261+
$content = '<?php '.PHP_EOL.'# phpcs:disable'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'# phpcs:enable'.PHP_EOL.'$var = TRUE;';
262+
$file = new DummyFile($content, $ruleset, $config);
263+
$file->process();
264+
265+
$errors = $file->getErrors();
266+
$numErrors = $file->getErrorCount();
267+
$this->assertEquals(1, $numErrors);
268+
$this->assertCount(1, $errors);
269+
270+
// Process with @ suppression (hash comment).
271+
$content = '<?php '.PHP_EOL.'# @phpcs:disable'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'# @phpcs:enable'.PHP_EOL.'$var = TRUE;';
272+
$file = new DummyFile($content, $ruleset, $config);
273+
$file->process();
274+
275+
$errors = $file->getErrors();
276+
$numErrors = $file->getErrorCount();
277+
$this->assertEquals(1, $numErrors);
278+
$this->assertCount(1, $errors);
279+
220280
// Process with suppression (deprecated syntax).
221281
$content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'// @codingStandardsIgnoreEnd'.PHP_EOL.'$var = TRUE;';
222282
$file = new DummyFile($content, $ruleset, $config);
@@ -372,6 +432,26 @@ public function testSuppressLine()
372432
$this->assertEquals(1, $numErrors);
373433
$this->assertCount(1, $errors);
374434

435+
// Process with suppression on line before (hash comment).
436+
$content = '<?php '.PHP_EOL.'# phpcs:ignore'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = FALSE;';
437+
$file = new DummyFile($content, $ruleset, $config);
438+
$file->process();
439+
440+
$errors = $file->getErrors();
441+
$numErrors = $file->getErrorCount();
442+
$this->assertEquals(1, $numErrors);
443+
$this->assertCount(1, $errors);
444+
445+
// Process with @ suppression on line before (hash comment).
446+
$content = '<?php '.PHP_EOL.'# @phpcs:ignore'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = FALSE;';
447+
$file = new DummyFile($content, $ruleset, $config);
448+
$file->process();
449+
450+
$errors = $file->getErrors();
451+
$numErrors = $file->getErrorCount();
452+
$this->assertEquals(1, $numErrors);
453+
$this->assertCount(1, $errors);
454+
375455
// Process with suppression on line before.
376456
$content = '<?php '.PHP_EOL.'/* phpcs:ignore */'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = FALSE;';
377457
$file = new DummyFile($content, $ruleset, $config);
@@ -432,6 +512,26 @@ public function testSuppressLine()
432512
$this->assertEquals(1, $numErrors);
433513
$this->assertCount(1, $errors);
434514

515+
// Process with suppression on same line (hash comment).
516+
$content = '<?php '.PHP_EOL.'$var = FALSE; # phpcs:ignore'.PHP_EOL.'$var = FALSE;';
517+
$file = new DummyFile($content, $ruleset, $config);
518+
$file->process();
519+
520+
$errors = $file->getErrors();
521+
$numErrors = $file->getErrorCount();
522+
$this->assertEquals(1, $numErrors);
523+
$this->assertCount(1, $errors);
524+
525+
// Process with @ suppression on same line (hash comment).
526+
$content = '<?php '.PHP_EOL.'$var = FALSE; # @phpcs:ignore'.PHP_EOL.'$var = FALSE;';
527+
$file = new DummyFile($content, $ruleset, $config);
528+
$file->process();
529+
530+
$errors = $file->getErrors();
531+
$numErrors = $file->getErrorCount();
532+
$this->assertEquals(1, $numErrors);
533+
$this->assertCount(1, $errors);
534+
435535
// Process with suppression on same line (deprecated syntax).
436536
$content = '<?php '.PHP_EOL.'$var = FALSE; // @codingStandardsIgnoreLine'.PHP_EOL.'$var = FALSE;';
437537
$file = new DummyFile($content, $ruleset, $config);
@@ -478,6 +578,16 @@ public function testNestedSuppressLine()
478578
$this->assertEquals(0, $numErrors);
479579
$this->assertCount(0, $errors);
480580

581+
// Process with disable/enable suppression and no single line suppression (hash comment).
582+
$content = '<?php '.PHP_EOL.'# phpcs:disable'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = TRUE;'.PHP_EOL.'# phpcs:enable';
583+
$file = new DummyFile($content, $ruleset, $config);
584+
$file->process();
585+
586+
$errors = $file->getErrors();
587+
$numErrors = $file->getErrorCount();
588+
$this->assertEquals(0, $numErrors);
589+
$this->assertCount(0, $errors);
590+
481591
// Process with disable/enable suppression and no single line suppression (deprecated syntax).
482592
$content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = TRUE;'.PHP_EOL.'// @codingStandardsIgnoreEnd';
483593
$file = new DummyFile($content, $ruleset, $config);
@@ -508,6 +618,16 @@ public function testNestedSuppressLine()
508618
$this->assertEquals(0, $numErrors);
509619
$this->assertCount(0, $errors);
510620

621+
// Process with line @ suppression nested within disable/enable @ suppression (hash comment).
622+
$content = '<?php '.PHP_EOL.'# @phpcs:disable'.PHP_EOL.'# @phpcs:ignore'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = TRUE;'.PHP_EOL.'# @phpcs:enable';
623+
$file = new DummyFile($content, $ruleset, $config);
624+
$file->process();
625+
626+
$errors = $file->getErrors();
627+
$numErrors = $file->getErrorCount();
628+
$this->assertEquals(0, $numErrors);
629+
$this->assertCount(0, $errors);
630+
511631
// Process with line suppression nested within disable/enable suppression (deprecated syntax).
512632
$content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'// @codingStandardsIgnoreLine'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = TRUE;'.PHP_EOL.'// @codingStandardsIgnoreEnd';
513633
$file = new DummyFile($content, $ruleset, $config);
@@ -554,6 +674,16 @@ public function testSuppressScope()
554674
$this->assertEquals(0, $numErrors);
555675
$this->assertCount(0, $errors);
556676

677+
// Process with suppression (hash comment).
678+
$content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'#phpcs:disable'.PHP_EOL.'function myFunction() {'.PHP_EOL.'#phpcs:enable'.PHP_EOL.'$this->foo();'.PHP_EOL.'}'.PHP_EOL.'}';
679+
$file = new DummyFile($content, $ruleset, $config);
680+
$file->process();
681+
682+
$errors = $file->getErrors();
683+
$numErrors = $file->getErrorCount();
684+
$this->assertEquals(0, $numErrors);
685+
$this->assertCount(0, $errors);
686+
557687
// Process with suppression.
558688
$content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'//@phpcs:disable'.PHP_EOL.'function myFunction() {'.PHP_EOL.'//@phpcs:enable'.PHP_EOL.'$this->foo();'.PHP_EOL.'}'.PHP_EOL.'}';
559689
$file = new DummyFile($content, $ruleset, $config);
@@ -647,6 +777,26 @@ public function testSuppressFile()
647777
$this->assertEquals(0, $numWarnings);
648778
$this->assertCount(0, $warnings);
649779

780+
// Process with suppression (hash comment).
781+
$content = '<?php '.PHP_EOL.'# phpcs:ignoreFile'.PHP_EOL.'//TODO: write some code';
782+
$file = new DummyFile($content, $ruleset, $config);
783+
$file->process();
784+
785+
$warnings = $file->getWarnings();
786+
$numWarnings = $file->getWarningCount();
787+
$this->assertEquals(0, $numWarnings);
788+
$this->assertCount(0, $warnings);
789+
790+
// Process with @ suppression (hash comment).
791+
$content = '<?php '.PHP_EOL.'# @phpcs:ignoreFile'.PHP_EOL.'//TODO: write some code';
792+
$file = new DummyFile($content, $ruleset, $config);
793+
$file->process();
794+
795+
$warnings = $file->getWarnings();
796+
$numWarnings = $file->getWarningCount();
797+
$this->assertEquals(0, $numWarnings);
798+
$this->assertCount(0, $warnings);
799+
650800
// Process with suppression (deprecated syntax).
651801
$content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreFile'.PHP_EOL.'//TODO: write some code';
652802
$file = new DummyFile($content, $ruleset, $config);
@@ -780,6 +930,20 @@ public function testDisableSelected()
780930
$this->assertEquals(0, $numWarnings);
781931
$this->assertCount(0, $warnings);
782932

933+
// Suppress a single sniff with reason (hash comment).
934+
$content = '<?php '.PHP_EOL.'# phpcs:disable Generic.Commenting.Todo -- for reasons'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'//TODO: write some code';
935+
$file = new DummyFile($content, $ruleset, $config);
936+
$file->process();
937+
938+
$errors = $file->getErrors();
939+
$numErrors = $file->getErrorCount();
940+
$warnings = $file->getWarnings();
941+
$numWarnings = $file->getWarningCount();
942+
$this->assertEquals(1, $numErrors);
943+
$this->assertCount(1, $errors);
944+
$this->assertEquals(0, $numWarnings);
945+
$this->assertCount(0, $warnings);
946+
783947
// Suppress multiple sniffs.
784948
$content = '<?php '.PHP_EOL.'// phpcs:disable Generic.Commenting.Todo,Generic.PHP.LowerCaseConstant'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'//TODO: write some code';
785949
$file = new DummyFile($content, $ruleset, $config);
@@ -942,7 +1106,7 @@ public function testEnableSelected()
9421106
$this->assertCount(1, $warnings);
9431107

9441108
// Suppress multiple sniffs and re-enable one.
945-
$content = '<?php '.PHP_EOL.'// phpcs:disable Generic.Commenting.Todo,Generic.PHP.LowerCaseConstant'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'// phpcs:enable Generic.Commenting.Todo'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'$var = FALSE;';
1109+
$content = '<?php '.PHP_EOL.'# phpcs:disable Generic.Commenting.Todo,Generic.PHP.LowerCaseConstant'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'# phpcs:enable Generic.Commenting.Todo'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'$var = FALSE;';
9461110
$file = new DummyFile($content, $ruleset, $config);
9471111
$file->process();
9481112

@@ -998,7 +1162,7 @@ public function testEnableSelected()
9981162
$this->assertCount(1, $warnings);
9991163

10001164
// Suppress a category and re-enable a whole standard.
1001-
$content = '<?php '.PHP_EOL.'// phpcs:disable Generic.Commenting'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'// phpcs:enable Generic'.PHP_EOL.'//TODO: write some code';
1165+
$content = '<?php '.PHP_EOL.'# phpcs:disable Generic.Commenting'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'# phpcs:enable Generic'.PHP_EOL.'//TODO: write some code';
10021166
$file = new DummyFile($content, $ruleset, $config);
10031167
$file->process();
10041168

@@ -1143,7 +1307,7 @@ public function testIgnoreSelected()
11431307
$this->assertCount(0, $warnings);
11441308

11451309
// Suppress a category of sniffs.
1146-
$content = '<?php '.PHP_EOL.'// phpcs:ignore Generic.Commenting'.PHP_EOL.'$var = FALSE; //TODO: write some code'.PHP_EOL.'$var = FALSE; //TODO: write some code';
1310+
$content = '<?php '.PHP_EOL.'# phpcs:ignore Generic.Commenting'.PHP_EOL.'$var = FALSE; //TODO: write some code'.PHP_EOL.'$var = FALSE; //TODO: write some code';
11471311
$file = new DummyFile($content, $ruleset, $config);
11481312
$file->process();
11491313

0 commit comments

Comments
 (0)