packages/sql-semantics/tests/Unit/Core/Analysis/TriviaReaderTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Core\Analysis;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Small;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlParser\Lexer\Token;
12use SqlParser\Parser\Node;
13use SqlSemantics\Core\Analysis\SourceComments;
14use SqlSemantics\Core\Analysis\TriviaReader;
15
16#[CoversClass(TriviaReader::class)]
17#[UsesClass(SourceComments::class)]
18#[Small]
19final class TriviaReaderTest extends TestCase
20{
21 public function testReadSeparatesLeadingTrailingAndTokenCommentsAndSkipsTokensThatSpellNothing(): void
22 {
23 $select = new Token(1, 'SELECT', 'SELECT', 11, '/* lead */ ');
24 $one = new Token(2, 'NUM', '1', 26, ' /* value */ ');
25 $end = new Token(0, 'END_OF_INPUT', '', 33, " -- after\n");
26 $tree = new Node('root', 0, [new Node('stmt', 0, [$select, new Node('expr', 0, [$one])]), $end], "-- more\n");
27 $comments = (new TriviaReader())->read($tree);
28 self::assertSame(['/* lead */'], $comments->leading);
29 self::assertSame(['/* value */'], $comments->before($one));
30 self::assertSame([], $comments->before($select));
31 self::assertSame([], $comments->before($end));
32 self::assertSame(['-- after', '-- more'], $comments->trailing);
33 }
34
35 public function testReadStartsEachTreeOutsideAnExecutableComment(): void
36 {
37 $reader = new TriviaReader(executableVersion: 80000);
38 $opened = new Node('root', 0, [new Token(1, 'SELECT', 'SELECT', 0), new Token(2, 'NUM', '1', 16, ' /*!50700 ')]);
39 $reader->read($opened);
40 $plain = new Node('root', 0, [new Token(1, 'SELECT', 'SELECT', 0), new Token(2, 'NUM', '1', 14, ' /* a */ ')]);
41 self::assertSame(['/* a */'], $reader->read($plain)->before($plain->tokens()[1]));
42 }
43
44 public function testCommentsSplitsBlockAndLineCommentsWithoutTheirWhitespace(): void
45 {
46 $reader = new TriviaReader();
47 self::assertSame([], $reader->comments(''));
48 self::assertSame([], $reader->comments(" \n\t"));
49 self::assertSame(['/* a */', '-- b', '#c', '/* d'], $reader->comments(" /* a */\n-- b \r\n#c\n /* d"));
50 self::assertSame(['/* a /* b */'], $reader->comments('/* a /* b */'));
51 }
52
53 public function testCommentsNestsBlockCommentsWhenTheLanguageDoes(): void
54 {
55 self::assertSame(['/* a /* b */ c */', '-- d'], (new TriviaReader(nestedBlocks: true))->comments("/* a /* b */ c */ -- d\n"));
56 }
57
58 public function testCommentsKeepsExecutableCommentDelimitersApartAndOrdinaryVersionCommentsWhole(): void
59 {
60 $reader = new TriviaReader(executableVersion: 80000);
61 self::assertSame(['/*!50700'], $reader->comments(' /*!50700 '));
62 self::assertSame(['/* inner */', '*/'], $reader->comments(' /* inner */ */ '));
63 self::assertSame(['/*!99999 skipped */', '/*!'], $reader->comments(' /*!99999 skipped */ /*! '));
64 self::assertSame(['*/'], $reader->comments(' */ '));
65 self::assertSame(['/* a /*! b */ c */'], $reader->comments('/* a /*! b */ c */'));
66 }
67
68
69 public function testCommentEndFindsTheEndOfEachCommentKind(): void
70 {
71 $reader = new TriviaReader();
72 self::assertSame(4, $reader->commentEnd("-- a\nb", 0));
73 self::assertSame(2, $reader->commentEnd('#a', 0));
74 self::assertSame(8, $reader->commentEnd(' /* a */ ', 1));
75 }
76
77 public function testBlockEndStepsOverPairsOnlyInLanguagesWithExecutableComments(): void
78 {
79 self::assertSame(5, (new TriviaReader())->blockEnd('/*/*/ x */', 0));
80 self::assertSame(10, (new TriviaReader(executableVersion: 80000))->blockEnd('/*/*/ x */', 0));
81 self::assertSame(10, (new TriviaReader(nestedBlocks: true))->blockEnd('/* /* */*/', 0));
82 self::assertSame(7, (new TriviaReader())->blockEnd('/* open', 0));
83 }
84}
85