diff --git a/src/Report/ViolationId.php b/src/Report/ViolationId.php index 846e7985..c449af0a 100644 --- a/src/Report/ViolationId.php +++ b/src/Report/ViolationId.php @@ -52,10 +52,15 @@ public function toString(): string public function match(self $violationId): bool { - return $this->ruleShortName === $violationId->ruleShortName - && (null === $this->identifier || $this->identifier === $violationId->identifier) - && (null === $this->tokenName || $this->tokenName === $violationId->tokenName) - && (null === $this->line || $this->line === $violationId->line) - && (null === $this->linePosition || $this->linePosition === $violationId->linePosition); + return $this->matchValue($this->ruleShortName, $violationId->ruleShortName) + && $this->matchValue($this->identifier, $violationId->identifier) + && $this->matchValue($this->tokenName, $violationId->tokenName) + && $this->matchValue($this->line, $violationId->line) + && $this->matchValue($this->linePosition, $violationId->linePosition); + } + + private function matchValue(string|int|null $self, string|int|null $other): bool + { + return null === $self || strtolower((string) $self) === strtolower((string) $other); } } diff --git a/src/Runner/Fixer.php b/src/Runner/Fixer.php index 96061e26..701316d1 100644 --- a/src/Runner/Fixer.php +++ b/src/Runner/Fixer.php @@ -7,8 +7,6 @@ use BadMethodCallException; use Twig\Source; use TwigCsFixer\Exception\CannotFixFileException; -use TwigCsFixer\Report\Violation; -use TwigCsFixer\Report\ViolationId; use TwigCsFixer\Ruleset\Ruleset; use TwigCsFixer\Token\Token; use TwigCsFixer\Token\TokenizerInterface; diff --git a/src/Token/Tokenizer.php b/src/Token/Tokenizer.php index 80dfb779..d8caeb0a 100644 --- a/src/Token/Tokenizer.php +++ b/src/Token/Tokenizer.php @@ -159,7 +159,7 @@ public function tokenize(Source $source): array $this->pushToken(Token::EOF_TYPE); - return [$this->tokens, []]; + return [$this->tokens, $this->ignoredViolations]; } private function resetState(Source $source): void @@ -689,12 +689,12 @@ private function getOperatorRegex(Environment $env): string private function extractIgnoredViolations(string $comment): void { $comment = trim($comment); - if (1 === preg_match('/^twig-cs-fixer-disable(|-line|-next-line) ([\s\w,.:]+)/', $comment, $match)) { + if (1 === preg_match('/^twig-cs-fixer-disable(|-line|-next-line) ([\s\w,.:]+)/i', $comment, $match)) { $this->setStateParam('ignoredViolations', preg_replace('/\s+/', ',', $match[2]) ?? ''); - $this->setStateParam('ignoreType', trim('-', $match[1])); + $this->setStateParam('ignoredType', trim($match[1], '-')); + } else { + $this->setStateParam('ignoredViolations', ''); } - - $this->setStateParam('ignoredViolations', ''); } private function processIgnoredViolations(): void @@ -704,8 +704,8 @@ private function processIgnoredViolations(): void return; } - $line = match ($this->getStateParam('ignoreType')) { - 'line' => $this->getStateParam('startLine'), + $line = match ($this->getStateParam('ignoredType')) { + 'line' => (int) $this->getStateParam('startLine'), 'next-line' => $this->line + 1, default => null, }; diff --git a/tests/Rules/Fixtures/FakeRule.php b/tests/Rules/Fixtures/FakeRule.php index e5238b77..a2b1b0f6 100644 --- a/tests/Rules/Fixtures/FakeRule.php +++ b/tests/Rules/Fixtures/FakeRule.php @@ -6,9 +6,16 @@ use TwigCsFixer\Rules\AbstractRule; +/** + * This rule reports an error for the first token of every line. + */ class FakeRule extends AbstractRule { public function process(int $tokenPosition, array $tokens): void { + $token = $tokens[$tokenPosition]; + if (1 === $token->getPosition()) { + $this->addError('First token of the line', $token); + } } } diff --git a/tests/Rules/Fixtures/disable0.twig b/tests/Rules/Fixtures/disable0.twig new file mode 100644 index 00000000..e69de29b diff --git a/tests/Rules/Fixtures/disable1.twig b/tests/Rules/Fixtures/disable1.twig new file mode 100644 index 00000000..de37cd9f --- /dev/null +++ b/tests/Rules/Fixtures/disable1.twig @@ -0,0 +1,2 @@ +{# twig-cs-fixer-disable Fake.Error #} +This comment disable for the whole file. diff --git a/tests/Rules/Fixtures/disable2.twig b/tests/Rules/Fixtures/disable2.twig new file mode 100644 index 00000000..b2a17b79 --- /dev/null +++ b/tests/Rules/Fixtures/disable2.twig @@ -0,0 +1,11 @@ +{# twig-cs-fixer-disable Fake.Error:1 #} This comment disable for the first line. +{# twig-cs-fixer-disable Fake.Error:2:1 #} This comment disable for the first token of the line. +{# twig-cs-fixer-disable Fake.Error:3:2 #} This comment disable for the second token of the line. => ERROR ON THIS LINE +{# twig-cs-fixer-disable-line Fake.Error #} This comment disable for the line. +{# +twig-cs-fixer-disable-line Fake.Error #} This comment disable for the line with "{ #" => ERROR ON THIS LINE +{# twig-cs-fixer-disable-line Fake.Error::1 #} This comment disable for the first token of the line. +{# twig-cs-fixer-disable-line Fake.Error::2 #} This comment disable for the second token of the line. => ERROR ON THIS LINE +{# twig-cs-fixer-disable-next-line Fake.Error #} This comment disable for the first token of the next-line. => ERROR ON THIS LINE +{# twig-cs-fixer-disable-next-line Fake.Error +#} This comment disable for the next line. => ERROR ON THIS LINE diff --git a/tests/Rules/RuleTest.php b/tests/Rules/RuleTest.php index 2d51bc3e..e7be141b 100644 --- a/tests/Rules/RuleTest.php +++ b/tests/Rules/RuleTest.php @@ -4,12 +4,18 @@ namespace TwigCsFixer\Tests\Rules; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SplFileInfo; +use TwigCsFixer\Environment\StubbedEnvironment; use TwigCsFixer\Report\Report; +use TwigCsFixer\Report\Violation; use TwigCsFixer\Rules\AbstractRule; +use TwigCsFixer\Ruleset\Ruleset; +use TwigCsFixer\Runner\Linter; use TwigCsFixer\Tests\Rules\Fixtures\FakeRule; use TwigCsFixer\Token\Token; +use TwigCsFixer\Token\Tokenizer; final class RuleTest extends TestCase { @@ -106,4 +112,48 @@ protected function process(int $tokenPosition, array $tokens): void static::assertSame(0, $report->getTotalWarnings()); static::assertSame(2, $report->getTotalErrors()); } + + /** + * @param array $expectedLines + * + * @dataProvider ignoredViolationsDataProvider + */ + public function testIgnoredViolations(string $filePath, array $expectedLines): void + { + $env = new StubbedEnvironment(); + $tokenizer = new Tokenizer($env); + $linter = new Linter($env, $tokenizer); + $ruleset = new Ruleset(); + + $ruleset->addRule(new FakeRule()); + $report = $linter->run([new SplFileInfo($filePath)], $ruleset); + $messages = $report->getFileViolations($filePath); + + static::assertSame( + $expectedLines, + array_map( + static fn (Violation $violation) => $violation->getLine(), + $messages, + ), + ); + } + + /** + * @return iterable}> + */ + public static function ignoredViolationsDataProvider(): iterable + { + yield [ + __DIR__.'/Fixtures/disable0.twig', + [1], + ]; + yield [ + __DIR__.'/Fixtures/disable1.twig', + [], + ]; + yield [ + __DIR__.'/Fixtures/disable2.twig', + [3, 6, 8, 9, 11], + ]; + } } diff --git a/tests/Runner/LinterTest.php b/tests/Runner/LinterTest.php index 620329b9..72ea82b5 100644 --- a/tests/Runner/LinterTest.php +++ b/tests/Runner/LinterTest.php @@ -94,6 +94,7 @@ public function testUntokenizableFilesAreReported(): void $call = 0; $tokenizer->method('tokenize')->willReturnCallback( static function () use (&$call): array { + /** @psalm-suppress RedundantCondition */ if (0 === $call) { $call++; throw CannotTokenizeException::unknownError(); @@ -128,7 +129,6 @@ public function testUserDeprecationAreReported(): void { $deprecations = 0; set_error_handler(static function () use (&$deprecations): bool { - /** @psalm-suppress MixedOperand,MixedAssignment https://github.com/vimeo/psalm/issues/9155 */ $deprecations++; return true; @@ -198,6 +198,7 @@ public function testBuggyFixesAreReported( $fixer = self::createStub(FixerInterface::class); $fixer->method('fixFile')->willReturnCallback( static function () use (&$call, $exception): string { + /** @psalm-suppress RedundantCondition */ if (0 === $call) { $call++; throw $exception; diff --git a/tests/Token/TokenTest.php b/tests/Token/TokenTest.php index 5383c804..aea9a8ef 100644 --- a/tests/Token/TokenTest.php +++ b/tests/Token/TokenTest.php @@ -29,7 +29,9 @@ public function testNullValue(): void static::assertSame('', $token->getValue()); } - #[DataProvider('tokenNameDataProvider')] + /** + * @dataProvider tokenNameDataProvider + */ public function testTokenName(int|string $type, string $expectedName): void { $token = new Token($type, 1, 1, 'file.twig'); @@ -37,7 +39,7 @@ public function testTokenName(int|string $type, string $expectedName): void } /** - * @return iterable + * @return iterable */ public static function tokenNameDataProvider(): iterable { diff --git a/tests/Token/Tokenizer/Fixtures/ignored_violations.twig b/tests/Token/Tokenizer/Fixtures/ignored_violations.twig new file mode 100644 index 00000000..cae3d675 --- /dev/null +++ b/tests/Token/Tokenizer/Fixtures/ignored_violations.twig @@ -0,0 +1,4 @@ +{# twig-cs-fixer-disable Foo.Bar.Baz #} +{# Twig-CS-Fixer-disable Foo.Bar.BazInsensitive #} +{# twig-cs-fixer-disable-line Foo.Bar #} +{# twig-cs-fixer-disable-next-line Foo.Bar Bar.Foo #} diff --git a/tests/Token/Tokenizer/TokenizerTest.php b/tests/Token/Tokenizer/TokenizerTest.php index 477fc104..90545789 100644 --- a/tests/Token/Tokenizer/TokenizerTest.php +++ b/tests/Token/Tokenizer/TokenizerTest.php @@ -8,6 +8,7 @@ use Twig\Source; use TwigCsFixer\Environment\StubbedEnvironment; use TwigCsFixer\Exception\CannotTokenizeException; +use TwigCsFixer\Report\ViolationId; use TwigCsFixer\Tests\TestHelper; use TwigCsFixer\Tests\Token\Tokenizer\Fixtures\CustomTwigExtension; use TwigCsFixer\Token\Token; @@ -71,6 +72,31 @@ public function testTokenizeWithCustomOperators(): void ); } + public function testTokenizeIgnoredViolations(): void + { + $filePath = __DIR__.'/Fixtures/ignored_violations.twig'; + $content = file_get_contents($filePath); + static::assertNotFalse($content); + + $env = new StubbedEnvironment([new CustomTwigExtension()]); + $tokenizer = new Tokenizer($env); + $source = new Source($content, $filePath); + + static::assertEquals( + [ + 'Foo.Bar.Baz', + 'Foo.Bar.BazInsensitive', + 'Foo.Bar:3', + 'Foo.Bar:5', + 'Bar.Foo:5', + ], + array_map( + static fn (ViolationId $validationId) => $validationId->toString(), + $tokenizer->tokenize($source)[1] + ) + ); + } + /** * @param array $expectedTokenTypes *