Skip to content

Commit

Permalink
Avoid infinite loop with mixed EOL (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
VincentLanglet authored Feb 25, 2024
1 parent 2dccc7a commit b66e89b
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 23 deletions.
21 changes: 11 additions & 10 deletions src/Report/Reporter/TextReporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TwigCsFixer\File\FileHelper;
use TwigCsFixer\Report\Report;
use TwigCsFixer\Report\Violation;

Expand Down Expand Up @@ -47,16 +46,18 @@ public function display(
}

$content = @file_get_contents($file);
$lines = false !== $content ? preg_split("/\r\n?|\n/", $content) : false;

$rows = [];
foreach ($fileViolations as $violation) {
$formattedText = [];
$line = $violation->getLine();

if (null === $line || false === $content) {
if (null === $line || false === $lines) {
$formattedText[] = $this->formatErrorMessage($violation, $debug);
} else {
$lines = $this->getContext($content, $line);
foreach ($lines as $no => $code) {
$context = $this->getContext($lines, $line);
foreach ($context as $no => $code) {
$formattedText[] = sprintf(
self::ERROR_LINE_FORMAT,
$no,
Expand Down Expand Up @@ -103,22 +104,22 @@ public function display(
}

/**
* @param array<string> $templatesLines
*
* @return array<int, string>
*/
private function getContext(string $template, int $line): array
private function getContext(array $templatesLines, int $line): array
{
$eol = FileHelper::detectEOL($template);
$lines = explode($eol, $template);
$position = max(0, $line - 2);
$max = min(\count($lines), $line + 1);
$max = min(\count($templatesLines), $line + 1);

$result = [];
$indents = [];

do {
preg_match('/^[\s\t]+/', $lines[$position], $match);
preg_match('/^[\s\t]+/', $templatesLines[$position], $match);
$indents[] = \strlen($match[0] ?? '');
$result[$position + 1] = $lines[$position];
$result[$position + 1] = $templatesLines[$position];
++$position;
} while ($position < $max);

Expand Down
12 changes: 5 additions & 7 deletions src/Token/Tokenizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Twig\Environment;
use Twig\Source;
use TwigCsFixer\Exception\CannotTokenizeException;
use TwigCsFixer\File\FileHelper;
use TwigCsFixer\Report\ViolationId;
use Webmozart\Assert\Assert;

Expand Down Expand Up @@ -47,8 +46,6 @@ final class Tokenizer implements TokenizerInterface

private int $cursor = 0;

private string $eol = \PHP_EOL;

private int $lastEOL = 0;

private ?int $end = null;
Expand Down Expand Up @@ -177,7 +174,6 @@ private function resetState(Source $source): void
$this->code = $source->getCode();
$this->end = \strlen($this->code);
$this->filename = $source->getName();
$this->eol = FileHelper::detectEOL($this->code);
}

/**
Expand Down Expand Up @@ -297,7 +293,9 @@ private function pushToken(int|string $type, string $value = '', ?Token $related
$this->tokens[] = $token;

$this->cursor += \strlen($value);
$this->line += substr_count($value, $this->eol);

$eolNb = preg_match_all("/\r\n?|\n/", $value);
$this->line += false !== $eolNb ? $eolNb : 0;

return $token;
}
Expand All @@ -315,7 +313,7 @@ private function lexExpression(): void
$this->lexTab();
} elseif (' ' === $currentCode) {
$this->lexWhitespace();
} elseif (1 === preg_match("/^{$this->eol}/", $currentCode.$nextToken, $match)) {
} elseif (1 === preg_match("/^\r\n?|^\n/", $currentCode.$nextToken, $match)) {
$this->lexEOL($match[0]);
} elseif ('.' === $currentCode && '.' === $nextToken && '.' === $next2Token) {
$this->lexSpread();
Expand Down Expand Up @@ -450,7 +448,7 @@ private function lexData(int $limit = 0): void
$this->lexTab();
} elseif (' ' === $currentCode) {
$this->lexWhitespace();
} elseif (1 === preg_match("/^{$this->eol}/", $currentCode.$nextToken, $match)) {
} elseif (1 === preg_match("/^\r\n?|^\n/", $currentCode.$nextToken, $match)) {
$this->lexEOL($match[0]);
} elseif (1 === preg_match('/\S+/', $this->code, $match, 0, $this->cursor)) {
$value = $match[0];
Expand Down
14 changes: 8 additions & 6 deletions tests/Token/Tokenizer/TokenizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,22 @@ public function testTokenize(): void
);
}

public function testTokenizeWindowsEOL(): void
public function testTokenizeMixedEOL(): void
{
$env = new StubbedEnvironment();
$tokenizer = new Tokenizer($env);
$source = new Source("\r\n{#\r\n#}", 'path');
$source = new Source("{#\r\n\n#}\r\n\n", 'path');

static::assertEquals(
[
[
new Token(Token::EOL_TYPE, 1, 1, 'path', "\r\n"),
new Token(Token::COMMENT_START_TYPE, 2, 1, 'path', '{#'),
new Token(Token::COMMENT_EOL_TYPE, 2, 3, 'path', "\r\n"),
new Token(Token::COMMENT_START_TYPE, 1, 1, 'path', '{#'),
new Token(Token::COMMENT_EOL_TYPE, 1, 3, 'path', "\r\n"),
new Token(Token::COMMENT_EOL_TYPE, 2, 1, 'path', "\n"),
new Token(Token::COMMENT_END_TYPE, 3, 1, 'path', '#}'),
new Token(Token::EOF_TYPE, 3, 3, 'path'),
new Token(Token::EOL_TYPE, 3, 3, 'path', "\r\n"),
new Token(Token::EOL_TYPE, 4, 1, 'path', "\n"),
new Token(Token::EOF_TYPE, 5, 1, 'path'),
],
[],
],
Expand Down

0 comments on commit b66e89b

Please sign in to comment.