AlternativeSyntaxAnalyzerTest.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Tests\Tokenizer\Analyzer;
  13. use PhpCsFixer\Tests\TestCase;
  14. use PhpCsFixer\Tokenizer\Analyzer\AlternativeSyntaxAnalyzer;
  15. use PhpCsFixer\Tokenizer\Tokens;
  16. /**
  17. * @internal
  18. *
  19. * @covers \PhpCsFixer\Tokenizer\Analyzer\AlternativeSyntaxAnalyzer
  20. */
  21. final class AlternativeSyntaxAnalyzerTest extends TestCase
  22. {
  23. /**
  24. * @param list<int> $expectedPositives
  25. *
  26. * @dataProvider provideBelongsToAlternativeSyntaxCases
  27. */
  28. public function testBelongsToAlternativeSyntax(array $expectedPositives, string $source): void
  29. {
  30. $tokens = Tokens::fromCode($source);
  31. for ($index = $tokens->count() - 1; $index >= 0; --$index) {
  32. self::assertSame(
  33. \in_array($index, $expectedPositives, true),
  34. (new AlternativeSyntaxAnalyzer())->belongsToAlternativeSyntax($tokens, $index),
  35. '@ index: '.$index
  36. );
  37. }
  38. }
  39. public static function provideBelongsToAlternativeSyntaxCases(): iterable
  40. {
  41. yield 'declare' => [
  42. [7],
  43. '<?php declare(ticks=1):enddeclare;',
  44. ];
  45. yield 'for' => [
  46. [20],
  47. '<?php for($i = 0; $i < 10; $i++): echo $i; endfor;',
  48. ];
  49. yield 'foreach' => [
  50. [17],
  51. '<?php foreach([1, 2, 3] as $i): echo $i; endforeach;',
  52. ];
  53. yield 'if, elseif, else' => [
  54. [6, 17, 25],
  55. '<?php if ($condition): echo 1; elseif($a): echo 2; else: echo 3; endif;',
  56. ];
  57. yield 'switch' => [
  58. [6],
  59. '<?php switch ($value): default: echo 4; endswitch;',
  60. ];
  61. yield 'while' => [
  62. [5],
  63. '<?php while(true): echo "na"; endwhile;',
  64. ];
  65. yield 'multiple expressions' => [
  66. [7, 15, 51],
  67. '<?php
  68. if ($condition1): echo 1; else: echo 2; endif;
  69. somelabel: echo 3;
  70. echo $condition2 ? 4 : 5;
  71. if ($condition3): echo 6; endif;
  72. ',
  73. ];
  74. }
  75. /**
  76. * @dataProvider provideItFindsTheEndOfAnAlternativeSyntaxBlockCases
  77. */
  78. public function testItFindsTheEndOfAnAlternativeSyntaxBlock(string $code, int $startIndex, int $expectedResult): void
  79. {
  80. $analyzer = new AlternativeSyntaxAnalyzer();
  81. self::assertSame(
  82. $expectedResult,
  83. $analyzer->findAlternativeSyntaxBlockEnd(
  84. Tokens::fromCode($code),
  85. $startIndex
  86. )
  87. );
  88. }
  89. /**
  90. * @return iterable<array{string, int, int}>
  91. */
  92. public static function provideItFindsTheEndOfAnAlternativeSyntaxBlockCases(): iterable
  93. {
  94. yield ['<?php if ($foo): foo(); endif;', 1, 13];
  95. yield ['<?php if ($foo): foo(); else: bar(); endif;', 1, 13];
  96. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); endif;', 1, 13];
  97. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); endif;', 13, 25];
  98. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); else: baz(); endif;', 13, 25];
  99. yield ['<?php if ($foo): foo(); else: bar(); endif;', 13, 21];
  100. yield ['<?php for (;;): foo(); endfor;', 1, 14];
  101. yield ['<?php foreach ($foo as $bar): foo(); endforeach;', 1, 17];
  102. yield ['<?php while ($foo): foo(); endwhile;', 1, 13];
  103. yield ['<?php switch ($foo): case 1: foo(); endswitch;', 1, 18];
  104. $nested = <<<'PHP'
  105. <?php
  106. switch (foo()):
  107. case 1:
  108. switch (foo2()):
  109. case 2:
  110. if (bar()) {
  111. }
  112. switch (foo2()):
  113. case 4:
  114. {
  115. switch (foo3()) {
  116. case 4:
  117. {
  118. }
  119. }
  120. }
  121. endswitch;
  122. endswitch;
  123. case 2:
  124. switch (foo5()) {
  125. case 4:
  126. echo 1;
  127. }
  128. endswitch;
  129. PHP;
  130. yield [$nested, 1, 113];
  131. yield [$nested, 15, 83];
  132. yield [$nested, 41, 80];
  133. $nestedWithHtml = <<<'PHP'
  134. <?php if (1): ?>
  135. <div></div>
  136. <?php else: ?>
  137. <?php if (2): ?>
  138. <div></div>
  139. <?php else: ?>
  140. <div></div>
  141. <?php endif; ?>
  142. <?php endif; ?>
  143. PHP;
  144. yield [$nestedWithHtml, 1, 11];
  145. yield [$nestedWithHtml, 11, 38];
  146. yield [$nestedWithHtml, 17, 27];
  147. yield [$nestedWithHtml, 27, 33];
  148. }
  149. /**
  150. * @dataProvider provideItThrowsOnInvalidAlternativeSyntaxBlockStartIndexCases
  151. */
  152. public function testItThrowsOnInvalidAlternativeSyntaxBlockStartIndex(string $code, int $startIndex, string $expectedMessage): void
  153. {
  154. $tokens = Tokens::fromCode($code);
  155. $analyzer = new AlternativeSyntaxAnalyzer();
  156. $this->expectException(\InvalidArgumentException::class);
  157. $this->expectExceptionMessage($expectedMessage);
  158. $analyzer->findAlternativeSyntaxBlockEnd($tokens, $startIndex);
  159. }
  160. /**
  161. * @return iterable<array{string, int, string}>
  162. */
  163. public static function provideItThrowsOnInvalidAlternativeSyntaxBlockStartIndexCases(): iterable
  164. {
  165. yield ['<?php if ($foo): foo(); endif;', 0, 'Token at index 0 is not the start of an alternative syntax block.'];
  166. yield ['<?php if ($foo): foo(); endif;', 2, 'Token at index 2 is not the start of an alternative syntax block.'];
  167. yield ['<?php if ($foo): foo(); endif;', 999, 'There is no token at index 999.'];
  168. yield ['<?php if ($foo): foo(); else: bar(); endif;', 0, 'Token at index 0 is not the start of an alternative syntax block.'];
  169. yield ['<?php if ($foo): foo(); else: bar(); endif;', 2, 'Token at index 2 is not the start of an alternative syntax block.'];
  170. yield ['<?php if ($foo): foo(); else: bar(); endif;', 999, 'There is no token at index 999.'];
  171. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); endif;', 0, 'Token at index 0 is not the start of an alternative syntax block.'];
  172. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); endif;', 2, 'Token at index 2 is not the start of an alternative syntax block.'];
  173. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); endif;', 999, 'There is no token at index 999.'];
  174. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); else: baz(); endif;', 0, 'Token at index 0 is not the start of an alternative syntax block.'];
  175. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); else: baz(); endif;', 2, 'Token at index 2 is not the start of an alternative syntax block.'];
  176. yield ['<?php if ($foo): foo(); elseif ($bar): bar(); else: baz(); endif;', 999, 'There is no token at index 999.'];
  177. yield ['<?php for (;;): foo(); endfor;', 0, 'Token at index 0 is not the start of an alternative syntax block.'];
  178. yield ['<?php for (;;): foo(); endfor;', 2, 'Token at index 2 is not the start of an alternative syntax block.'];
  179. yield ['<?php for (;;): foo(); endfor;', 999, 'There is no token at index 999.'];
  180. yield ['<?php foreach ($foo as $bar): foo(); endforeach;', 0, 'Token at index 0 is not the start of an alternative syntax block.'];
  181. yield ['<?php foreach ($foo as $bar): foo(); endforeach;', 2, 'Token at index 2 is not the start of an alternative syntax block.'];
  182. yield ['<?php foreach ($foo as $bar): foo(); endforeach;', 999, 'There is no token at index 999.'];
  183. yield ['<?php while ($foo): foo(); endwhile;', 0, 'Token at index 0 is not the start of an alternative syntax block.'];
  184. yield ['<?php while ($foo): foo(); endwhile;', 2, 'Token at index 2 is not the start of an alternative syntax block.'];
  185. yield ['<?php while ($foo): foo(); endwhile;', 999, 'There is no token at index 999.'];
  186. yield ['<?php switch ($foo): case 1: foo(); endswitch;', 0, 'Token at index 0 is not the start of an alternative syntax block.'];
  187. yield ['<?php switch ($foo): case 1: foo(); endswitch;', 2, 'Token at index 2 is not the start of an alternative syntax block.'];
  188. yield ['<?php switch ($foo): case 1: foo(); endswitch;', 999, 'There is no token at index 999.'];
  189. }
  190. }