NoExtraBlankLinesFixerTest.php 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316
  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\Fixer\Whitespace;
  13. use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
  14. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  15. use PhpCsFixer\WhitespacesFixerConfig;
  16. /**
  17. * @internal
  18. *
  19. * @covers \PhpCsFixer\Fixer\Whitespace\NoExtraBlankLinesFixer
  20. */
  21. final class NoExtraBlankLinesFixerTest extends AbstractFixerTestCase
  22. {
  23. private string $template = <<<'EOF'
  24. <?php
  25. use \DateTime;
  26. use \stdClass;
  27. use \InvalidArgumentException;
  28. class Test {
  29. public function testThrow($a)
  30. {
  31. if ($a) {
  32. throw new InvalidArgumentException('test.'); // test
  33. }
  34. $date = new DateTime();
  35. $class = new stdClass();
  36. $class = (string) $class;
  37. $e = new InvalidArgumentException($class.$date->format('Y'));
  38. throw $e;
  39. }
  40. public function testBreak($a)
  41. {
  42. switch($a) {
  43. case 1:
  44. echo $a;
  45. break;
  46. case 2:
  47. echo 'test';
  48. break;
  49. }
  50. }
  51. protected static function testContinueAndReturn($a, $b)
  52. {
  53. while($a < 100) {
  54. if ($b < time()) {
  55. continue;
  56. }
  57. return $b;
  58. }
  59. return $a;
  60. }
  61. private function test(){
  62. // comment
  63. }
  64. private function test123(){
  65. // comment
  66. }
  67. }
  68. EOF;
  69. /**
  70. * @param list<int> $lineNumberRemoved Line numbers expected to be removed after fixing
  71. * @param list<string> $config
  72. *
  73. * @dataProvider provideWithConfigCases
  74. */
  75. public function testWithConfig(array $lineNumberRemoved, array $config): void
  76. {
  77. $this->fixer->configure(['tokens' => $config]);
  78. $this->doTest($this->removeLinesFromString($this->template, $lineNumberRemoved), $this->template);
  79. }
  80. public static function provideWithConfigCases(): iterable
  81. {
  82. $tests = [
  83. [
  84. [9, 14, 21, 43, 45, 49, 53, 57],
  85. ['curly_brace_block'],
  86. ],
  87. [
  88. [3, 5],
  89. ['use'],
  90. ],
  91. [
  92. [23, 24],
  93. ['extra'],
  94. ],
  95. [
  96. [49, 53],
  97. ['return'],
  98. ],
  99. [
  100. [45],
  101. ['continue'],
  102. ],
  103. [
  104. [32],
  105. ['break'],
  106. ],
  107. [
  108. [14, 21],
  109. ['throw'],
  110. ],
  111. ];
  112. yield from $tests;
  113. $all = [[], []];
  114. foreach ($tests as $test) {
  115. $all[0] = array_merge($test[0], $all[0]);
  116. $all[1] = array_merge($test[1], $all[1]);
  117. }
  118. yield $all;
  119. }
  120. public function testFix(): void
  121. {
  122. $expected = <<<'EOF'
  123. <?php
  124. $a = new Bar();
  125. $a = new FooBaz();
  126. EOF;
  127. $input = <<<'EOF'
  128. <?php
  129. $a = new Bar();
  130. $a = new FooBaz();
  131. EOF;
  132. $this->doTest($expected, $input);
  133. }
  134. public function testFixWithManyEmptyLines(): void
  135. {
  136. $expected = <<<'EOF'
  137. <?php
  138. $a = new Bar();
  139. $a = new FooBaz();
  140. EOF;
  141. $input = <<<'EOF'
  142. <?php
  143. $a = new Bar();
  144. $a = new FooBaz();
  145. EOF;
  146. $this->doTest($expected, $input);
  147. }
  148. public function testFixWithHeredoc(): void
  149. {
  150. $expected = '
  151. <?php
  152. $b = <<<TEXT
  153. Foo TEXT
  154. Bar
  155. FooFoo
  156. TEXT;
  157. ';
  158. $this->doTest($expected);
  159. }
  160. public function testFixWithNowdoc(): void
  161. {
  162. $expected = '
  163. <?php
  164. $b = <<<\'TEXT\'
  165. Foo TEXT;
  166. Bar1}
  167. FooFoo
  168. TEXT;
  169. ';
  170. $this->doTest($expected);
  171. }
  172. public function testFixWithEncapsulatedNowdoc(): void
  173. {
  174. $expected = '
  175. <?php
  176. $b = <<<\'TEXT\'
  177. Foo TEXT
  178. Bar
  179. <<<\'TEMPLATE\'
  180. BarFooBar TEMPLATE
  181. TEMPLATE;
  182. FooFoo
  183. TEXT;
  184. ';
  185. $this->doTest($expected);
  186. }
  187. public function testFixWithMultilineString(): void
  188. {
  189. $expected = <<<'EOF'
  190. <?php
  191. $a = 'Foo
  192. Bar';
  193. EOF;
  194. $this->doTest($expected);
  195. }
  196. public function testFixWithTrickyMultilineStrings(): void
  197. {
  198. $expected = <<<'EOF'
  199. <?php
  200. $a = 'Foo';
  201. $b = 'Bar
  202. Here\'s an escaped quote '
  203. .
  204. '
  205. FooFoo';
  206. EOF;
  207. $input = <<<'EOF'
  208. <?php
  209. $a = 'Foo';
  210. $b = 'Bar
  211. Here\'s an escaped quote '
  212. .
  213. '
  214. FooFoo';
  215. EOF;
  216. $this->doTest($expected, $input);
  217. }
  218. public function testFixWithCommentWithQuote(): void
  219. {
  220. $expected = <<<'EOF'
  221. <?php
  222. $a = 'foo';
  223. // my comment's must have a quote
  224. $b = 'foobar';
  225. $c = 'bar';
  226. EOF;
  227. $input = <<<'EOF'
  228. <?php
  229. $a = 'foo';
  230. // my comment's must have a quote
  231. $b = 'foobar';
  232. $c = 'bar';
  233. EOF;
  234. $this->doTest($expected, $input);
  235. }
  236. public function testFixWithTrailingInlineBlock(): void
  237. {
  238. $expected = "
  239. <?php
  240. echo 'hello';
  241. ?>
  242. \$a = 0;
  243. //a
  244. <?php
  245. \$a = 0;
  246. \$b = 1;
  247. //a
  248. ?>
  249. ";
  250. $this->doTest($expected);
  251. }
  252. /**
  253. * @dataProvider provideFixWithCommentsCases
  254. */
  255. public function testFixWithComments(string $expected, string $input): void
  256. {
  257. $this->doTest($expected, $input);
  258. }
  259. public static function provideFixWithCommentsCases(): iterable
  260. {
  261. yield [
  262. <<<'EOF'
  263. <?php
  264. //class Test
  265. $a; //
  266. $b;
  267. /***/
  268. $c;
  269. //
  270. $d;
  271. EOF,
  272. <<<'EOF'
  273. <?php
  274. //class Test
  275. $a; //
  276. $b;
  277. /***/
  278. $c;
  279. //
  280. $d;
  281. EOF
  282. ];
  283. yield [
  284. "<?php\n//a\n\n\$a =1;",
  285. "<?php\n//a\n\n\n\n\$a =1;",
  286. ];
  287. }
  288. /**
  289. * @dataProvider provideFixWithLineBreaksCases
  290. */
  291. public function testFixWithLineBreaks(string $expected, ?string $input = null): void
  292. {
  293. $this->doTest($expected, $input);
  294. }
  295. public static function provideFixWithLineBreaksCases(): iterable
  296. {
  297. $input = '<?php //
  298. $a = 1;
  299. $b = 1;
  300. ';
  301. $expected = '<?php //
  302. $a = 1;
  303. $b = 1;
  304. ';
  305. yield [
  306. "<?php\r\n//a\r\n\r\n\$a =1;",
  307. "<?php\r\n//a\r\n\r\n\r\n\r\n\$a =1;",
  308. ];
  309. yield [
  310. $expected,
  311. $input,
  312. ];
  313. yield [
  314. str_replace("\n", "\r\n", $expected),
  315. str_replace("\n", "\r\n", $input),
  316. ];
  317. yield [
  318. str_replace("\n", "\r", $input),
  319. ];
  320. yield [
  321. str_replace("\n", "\r", $expected),
  322. ];
  323. }
  324. public function testWrongConfig(): void
  325. {
  326. $this->expectException(InvalidFixerConfigurationException::class);
  327. $this->expectExceptionMessageMatches('/^\[no_extra_blank_lines\] Invalid configuration: The option "tokens" .*\.$/');
  328. $this->fixer->configure(['tokens' => ['__TEST__']]);
  329. }
  330. /**
  331. * @dataProvider provideBetweenUseCases
  332. */
  333. public function testBetweenUse(string $expected, ?string $input = null): void
  334. {
  335. $this->fixer->configure(['tokens' => ['use']]);
  336. $this->doTest($expected, $input);
  337. }
  338. public static function provideBetweenUseCases(): iterable
  339. {
  340. yield ['<?php use A\B;'];
  341. yield ['<?php use A\B?>'];
  342. yield ['<?php use A\B;use A\D; return 1;'];
  343. yield ["<?php use A\\B?>\n\n<?php use D\\E\\F?>"];
  344. yield ['<?php use Y\B;use A\D; return 1;'];
  345. yield [
  346. '<?php
  347. use A\B;
  348. use A\C;',
  349. '<?php
  350. use A\B;
  351. use A\C;',
  352. ];
  353. yield [
  354. '<?php use A\E;use A\Z;
  355. use C;
  356. return 1;
  357. ',
  358. '<?php use A\E;use A\Z;
  359. use C;
  360. return 1;
  361. ',
  362. ];
  363. yield [
  364. '<?php
  365. class Test {
  366. use A;
  367. use B;
  368. }',
  369. ];
  370. yield [
  371. '<?php
  372. $example = function () use ($message) { var_dump($message); };
  373. $example = function () use ($message) { var_dump($message); };
  374. ',
  375. ];
  376. yield [
  377. '<?php
  378. use function A; use function B;
  379. echo 1;',
  380. ];
  381. yield [
  382. '<?php
  383. use some\a\{ClassA, ClassB, ClassC as C,};
  384. use function some\a\{fn_a, fn_b, fn_c,};
  385. use const some\a\{ConstA,ConstB,ConstC
  386. ,
  387. };
  388. use const some\Z\{ConstX,ConstY,ConstZ,};
  389. ',
  390. '<?php
  391. use some\a\{ClassA, ClassB, ClassC as C,};
  392. use function some\a\{fn_a, fn_b, fn_c,};
  393. use const some\a\{ConstA,ConstB,ConstC
  394. ,
  395. };
  396. '.'
  397. use const some\Z\{ConstX,ConstY,ConstZ,};
  398. ',
  399. ];
  400. }
  401. /**
  402. * @dataProvider provideRemoveLinesBetweenUseStatementsCases
  403. */
  404. public function testRemoveLinesBetweenUseStatements(string $expected, ?string $input = null): void
  405. {
  406. $this->fixer->configure(['tokens' => ['use']]);
  407. $this->doTest($expected, $input);
  408. }
  409. public static function provideRemoveLinesBetweenUseStatementsCases(): iterable
  410. {
  411. yield [
  412. <<<'EOF'
  413. <?php
  414. use Zxy\Qux;
  415. use Zoo\Bar as Bar2;
  416. use Foo\Bar as Bar1;
  417. use Foo\Zar\Baz;
  418. $c = 1;
  419. use Foo\Quxx as Quxx1;
  420. use Foo\Zar\Quxx;
  421. $a = new Bar1();
  422. $a = new Bar2();
  423. $a = new Baz();
  424. $a = new Qux();
  425. EOF,
  426. <<<'EOF'
  427. <?php
  428. use Zxy\Qux;
  429. use Zoo\Bar as Bar2;
  430. use Foo\Bar as Bar1;
  431. use Foo\Zar\Baz;
  432. $c = 1;
  433. use Foo\Quxx as Quxx1;
  434. use Foo\Zar\Quxx;
  435. $a = new Bar1();
  436. $a = new Bar2();
  437. $a = new Baz();
  438. $a = new Qux();
  439. EOF,
  440. ];
  441. yield [
  442. '<?php
  443. use some\a\{ClassA, ClassB, ClassC as C};
  444. use function some\a\{fn_a, fn_b, fn_c};
  445. use const some\a\{ConstA, ConstB, ConstC};
  446. ',
  447. '<?php
  448. use some\a\{ClassA, ClassB, ClassC as C};
  449. use function some\a\{fn_a, fn_b, fn_c};
  450. use const some\a\{ConstA, ConstB, ConstC};
  451. ',
  452. ];
  453. }
  454. /**
  455. * @dataProvider provideWithoutUsesCases
  456. */
  457. public function testWithoutUses(string $expected): void
  458. {
  459. $this->fixer->configure(['tokens' => ['use']]);
  460. $this->doTest($expected);
  461. }
  462. public static function provideWithoutUsesCases(): iterable
  463. {
  464. yield [
  465. '<?php
  466. $c = 1;
  467. $a = new Baz();
  468. $a = new Qux();',
  469. ];
  470. yield [
  471. '<?php use A\B;',
  472. ];
  473. yield [
  474. '<?php use A\B?>',
  475. ];
  476. yield [
  477. '<?php use A\B;?>',
  478. ];
  479. }
  480. /**
  481. * @dataProvider provideRemoveBetweenUseTraitsCases
  482. *
  483. * @group legacy
  484. */
  485. public function testRemoveBetweenUseTraits(string $expected, string $input): void
  486. {
  487. $this->expectDeprecation('Option "tokens: use_trait" used in `no_extra_blank_lines` rule is deprecated, use the rule `class_attributes_separation` with `elements: trait_import` instead.');
  488. $this->fixer->configure(['tokens' => ['use_trait']]);
  489. $this->doTest($expected, $input);
  490. }
  491. public static function provideRemoveBetweenUseTraitsCases(): iterable
  492. {
  493. yield [
  494. '<?php
  495. class Foo
  496. {
  497. use Z; // 123
  498. use Bar;/* */use Baz;
  499. public function baz() {}
  500. use Bar1; use Baz1;
  501. public function baz1() {}
  502. }
  503. ',
  504. '<?php
  505. class Foo
  506. {
  507. use Z; // 123
  508. use Bar;/* */use Baz;
  509. public function baz() {}
  510. use Bar1; use Baz1;
  511. public function baz1() {}
  512. }
  513. ',
  514. ];
  515. yield [
  516. '<?php
  517. class Foo
  518. {
  519. use Bar;use Baz;
  520. use Bar1;use Baz1;
  521. public function baz() {}
  522. }
  523. ',
  524. '<?php
  525. class Foo
  526. {
  527. use Bar;use Baz;
  528. use Bar1;use Baz1;
  529. public function baz() {}
  530. }
  531. ',
  532. ];
  533. yield [
  534. '<?php
  535. namespace T\A;
  536. use V;
  537. use W;
  538. class Test {
  539. use A;
  540. use B;
  541. private function test($b) {
  542. $a = function() use ($b) { echo $b;};
  543. $b = function() use ($b) { echo $b;};
  544. }
  545. }',
  546. '<?php
  547. namespace T\A;
  548. use V;
  549. use W;
  550. class Test {
  551. use A;
  552. use B;
  553. private function test($b) {
  554. $a = function() use ($b) { echo $b;};
  555. $b = function() use ($b) { echo $b;};
  556. }
  557. }',
  558. ];
  559. }
  560. /**
  561. * @dataProvider provideOneOrInLineCases
  562. */
  563. public function testOneOrInLine(string $expected, ?string $input = null): void
  564. {
  565. $this->fixer->configure(['tokens' => [
  566. 'break',
  567. 'continue',
  568. 'return',
  569. 'throw',
  570. 'curly_brace_block',
  571. 'square_brace_block',
  572. 'parenthesis_brace_block',
  573. ]]);
  574. $this->doTest($expected, $input);
  575. }
  576. public static function provideOneOrInLineCases(): iterable
  577. {
  578. yield [
  579. "<?php\n\n\$a = function() use (\$b) { while(3<1)break; \$c = \$b[1]; while(\$b<1)continue; if (true) throw \$e; return 1; };\n\n",
  580. ];
  581. yield [
  582. "<?php throw new \\Exception('do not import.');\n",
  583. "<?php throw new \\Exception('do not import.');\n\n",
  584. ];
  585. yield [
  586. "<?php\n\n\$a = \$b[0];\n\n",
  587. ];
  588. yield [
  589. "<?php\n\n\$a->{'Test'};\nfunction test(){}\n",
  590. ];
  591. yield [
  592. "<?php\n\n\$a = new class { public function a () { while(4<1)break; while(3<1)continue; if (true) throw \$e; return 1; }};\n\n",
  593. ];
  594. }
  595. /**
  596. * @param array<string, mixed> $config
  597. *
  598. * @dataProvider provideBracesCases
  599. */
  600. public function testBraces(array $config, string $expected, ?string $input = null): void
  601. {
  602. $this->fixer->configure($config);
  603. $this->doTest($expected, $input);
  604. }
  605. public static function provideBracesCases(): iterable
  606. {
  607. yield [
  608. ['tokens' => ['curly_brace_block']],
  609. "<?php function test()\n\n{}\n\necho 789;",
  610. ];
  611. yield [
  612. ['tokens' => ['curly_brace_block']],
  613. "<?php switch(\$a){\ncase 1:echo 789;}",
  614. "<?php switch(\$a){\n \ncase 1:echo 789;}",
  615. ];
  616. yield [
  617. ['tokens' => ['parenthesis_brace_block']],
  618. '<?php
  619. is_int(
  620. 1);
  621. function test(
  622. $a,
  623. $b,
  624. $c
  625. )
  626. {
  627. }',
  628. '<?php
  629. is_int(
  630. 1);
  631. function test(
  632. $a,
  633. $b,
  634. $c
  635. )
  636. {
  637. }',
  638. ];
  639. yield [
  640. ['tokens' => ['parenthesis_brace_block']],
  641. "<?php array(\n1,\n2,\n3,\n);",
  642. "<?php array(\n \n1,\n2,\n3,\n\n\n);",
  643. ];
  644. yield [
  645. ['tokens' => ['parenthesis_brace_block']],
  646. '<?php
  647. function a()
  648. {
  649. $b->d(e(
  650. ));
  651. foreach ($a as $x) {
  652. }
  653. }',
  654. ];
  655. yield [
  656. ['tokens' => ['return']],
  657. '<?php
  658. class Foo
  659. {
  660. public function bar() {return 1;}
  661. public function baz() {return 2;
  662. }
  663. }',
  664. '<?php
  665. class Foo
  666. {
  667. public function bar() {return 1;}
  668. public function baz() {return 2;
  669. }
  670. }',
  671. ];
  672. yield [
  673. ['tokens' => ['square_brace_block']],
  674. "<?php \$c = \$b[0];\n\n\n\$a = [\n 1,\n2];\necho 1;\n\$b = [];\n\n\n//a\n",
  675. "<?php \$c = \$b[0];\n\n\n\$a = [\n\n 1,\n2];\necho 1;\n\$b = [];\n\n\n//a\n",
  676. ];
  677. }
  678. /**
  679. * @dataProvider provideFixPre80Cases
  680. *
  681. * @requires PHP <8.0
  682. */
  683. public function testFixPre80(string $expected, ?string $input = null): void
  684. {
  685. $this->doTest($expected, $input);
  686. }
  687. /**
  688. * @return iterable<array{string, 1?: string}>
  689. */
  690. public static function provideFixPre80Cases(): iterable
  691. {
  692. yield [
  693. "<?php\n\n\$a = \$b{0};\n\n",
  694. ];
  695. }
  696. /**
  697. * @param array<string, mixed> $config
  698. *
  699. * @dataProvider provideMessyWhitespacesCases
  700. */
  701. public function testMessyWhitespaces(array $config, string $expected, ?string $input = null): void
  702. {
  703. $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n"));
  704. $this->fixer->configure($config);
  705. $this->doTest($expected, $input);
  706. }
  707. public static function provideMessyWhitespacesCases(): iterable
  708. {
  709. yield [
  710. [],
  711. "<?php\r\nuse AAA;\r\n\r\nuse BBB;\r\n\r\n",
  712. "<?php\r\nuse AAA;\r\n\r\n\r\n\r\nuse BBB;\r\n\r\n",
  713. ];
  714. yield [
  715. ['tokens' => ['parenthesis_brace_block']],
  716. "<?php is_int(\r\n1);",
  717. "<?php is_int(\r\n\r\n\r\n\r\n1);",
  718. ];
  719. yield [
  720. ['tokens' => ['square_brace_block']],
  721. "<?php \$c = \$b[0];\r\n\r\n\r\n\$a = [\r\n 1,\r\n2];\r\necho 1;\r\n\$b = [];\r\n\r\n\r\n//a\r\n",
  722. "<?php \$c = \$b[0];\r\n\r\n\r\n\$a = [\r\n\r\n 1,\r\n2];\r\necho 1;\r\n\$b = [];\r\n\r\n\r\n//a\r\n",
  723. ];
  724. yield [
  725. ['tokens' => ['square_brace_block']],
  726. "<?php \$c = \$b[0];\r\n\r\n\r\n\$a = [\r\n\t1,\r\n2];",
  727. "<?php \$c = \$b[0];\r\n\r\n\r\n\$a = [\r\n\r\n\t1,\r\n2];",
  728. ];
  729. }
  730. /**
  731. * @param list<string> $config
  732. *
  733. * @dataProvider provideInSwitchStatementCases
  734. */
  735. public function testInSwitchStatement(array $config, string $expected, ?string $input = null): void
  736. {
  737. $this->fixer->configure(['tokens' => $config]);
  738. $this->doTest($expected, $input);
  739. }
  740. public static function provideInSwitchStatementCases(): iterable
  741. {
  742. yield [
  743. [
  744. 'break',
  745. 'continue',
  746. 'extra',
  747. 'return',
  748. 'throw',
  749. ],
  750. '<?php
  751. /** a */
  752. switch ($a) {
  753. case 1:
  754. break;
  755. case 2:
  756. continue;
  757. case 3:
  758. return 1;
  759. case 4:
  760. throw $e;
  761. case 5:
  762. throw new \Exception();
  763. case Token::STRING_TYPE:
  764. echo 123;
  765. return new ConstantNode($token->getValue());
  766. case 7:
  767. return new ConstantNode($token->getValue());
  768. case 8:
  769. return 8;
  770. default:
  771. echo 1;
  772. }',
  773. '<?php
  774. /** a */
  775. switch ($a) {
  776. case 1:
  777. break;
  778. case 2:
  779. continue;
  780. case 3:
  781. return 1;
  782. case 4:
  783. throw $e;
  784. case 5:
  785. throw new \Exception();
  786. case Token::STRING_TYPE:
  787. echo 123;
  788. return new ConstantNode($token->getValue());
  789. case 7:
  790. return new ConstantNode($token->getValue());
  791. '.'
  792. case 8:
  793. return 8;
  794. '.'
  795. default:
  796. echo 1;
  797. }',
  798. ];
  799. yield [
  800. [
  801. 'switch',
  802. 'case',
  803. 'default',
  804. ],
  805. '<?php
  806. switch($a) {
  807. case 0:
  808. case 1:
  809. default:
  810. return 1;
  811. }',
  812. '<?php
  813. switch($a) {
  814. case 0:
  815. case 1:
  816. default:
  817. return 1;
  818. }',
  819. ];
  820. yield [
  821. [
  822. 'switch',
  823. 'case',
  824. 'default',
  825. ],
  826. '<?php
  827. switch($a) { case 2: echo 3;
  828. default: return 1;}
  829. // above stays empty',
  830. '<?php
  831. switch($a) { case 2: echo 3;
  832. default: return 1;}
  833. // above stays empty',
  834. ];
  835. }
  836. public function testRemovingEmptyLinesAfterOpenTag(): void
  837. {
  838. $this->doTest(
  839. '<?php
  840. class Foo {}',
  841. '<?php
  842. class Foo {}'
  843. );
  844. }
  845. /**
  846. * @param array<string, mixed> $config
  847. *
  848. * @dataProvider provideFix80Cases
  849. *
  850. * @requires PHP 8.0
  851. */
  852. public function testFix80(array $config, string $expected, ?string $input = null): void
  853. {
  854. $this->fixer->configure($config);
  855. $this->doTest($expected, $input);
  856. }
  857. public static function provideFix80Cases(): iterable
  858. {
  859. yield [
  860. ['tokens' => ['throw']],
  861. '<?php
  862. $nullCoalescingOperator1 = $bar ?? throw new \Exception();
  863. $nullCoalescingOperator2 = $bar ?? throw new \Exception();
  864. $nullCoalescingOperator3 = $bar ?? throw new \Exception();
  865. $ternaryOperator1 = $bar ? 42 : throw new \Exception();
  866. $ternaryOperator2 = $bar ? 42 : throw new \Exception();
  867. $ternaryOperator3 = $bar ? 42 : throw new \Exception();
  868. $orOperator1 = $bar || throw new \Exception();
  869. $orOperator2 = $bar || throw new \Exception();
  870. $orOperator3 = $bar || throw new \Exception();
  871. ',
  872. ];
  873. yield [
  874. ['tokens' => ['throw']],
  875. '<?php
  876. $a = $bar ?? throw new \Exception();
  877. // Now, we are going to use it!
  878. var_dump($a);
  879. ',
  880. ];
  881. yield [
  882. ['tokens' => ['attribute']],
  883. '<?php
  884. #[Attr]
  885. #[AttrFoo1]
  886. #[AttrFoo2]
  887. function foo(){}
  888. ',
  889. '<?php
  890. #[Attr]
  891. #[AttrFoo1]
  892. #[AttrFoo2]
  893. function foo(){}
  894. ',
  895. ];
  896. yield [
  897. ['tokens' => ['attribute']],
  898. '<?php class Foo
  899. {
  900. protected function f1(string $x): string { return "r1"; }
  901. protected function f2(string $x): string { return "r1"; }
  902. protected function f3(#[Attr] string $x): string { return "r1"; }
  903. protected function f4(string $x): string { return "r1"; }
  904. }',
  905. ];
  906. yield [
  907. ['tokens' => ['attribute']],
  908. '<?php abstract class Foo
  909. {
  910. abstract protected function f1(string $x): string;
  911. abstract protected function f2(string $x): string;
  912. abstract protected function f3(#[Attr] string $x): string;
  913. abstract protected function f4(string $x): string;
  914. }',
  915. ];
  916. }
  917. /**
  918. * @dataProvider provideFix81Cases
  919. *
  920. * @requires PHP 8.1
  921. */
  922. public function testFix81(string $expected, ?string $input = null): void
  923. {
  924. $this->fixer->configure(['tokens' => ['case']]);
  925. $this->doTest($expected, $input);
  926. }
  927. public static function provideFix81Cases(): iterable
  928. {
  929. yield [
  930. '<?php
  931. enum test
  932. {
  933. case Baz;
  934. public function foo() {
  935. switch (bar()) {
  936. case 1: echo 1; break;
  937. case 2: echo 2; break;
  938. }
  939. }
  940. case Bad;
  941. }
  942. ',
  943. '<?php
  944. enum test
  945. {
  946. case Baz;
  947. public function foo() {
  948. switch (bar()) {
  949. case 1: echo 1; break;
  950. case 2: echo 2; break;
  951. }
  952. }
  953. case Bad;
  954. }
  955. ',
  956. ];
  957. $expectedTemplate = '<?php
  958. enum Foo
  959. {
  960. case CASE_1;
  961. %s
  962. }';
  963. $enumAttributes = [
  964. 'case CASE_2;',
  965. 'const CONST_1 = self::CASE_1;',
  966. 'private const CONST_1 = self::CASE_1;',
  967. 'public function bar(): void {}',
  968. 'protected function bar(): void {}',
  969. 'private function bar(): void {}',
  970. 'static function bar(): void {}',
  971. 'final function bar(): void {}',
  972. ];
  973. foreach ($enumAttributes as $enumAttribute) {
  974. yield [
  975. sprintf($expectedTemplate, $enumAttribute),
  976. ];
  977. }
  978. }
  979. /**
  980. * @param list<int> $lineNumbers
  981. */
  982. private function removeLinesFromString(string $input, array $lineNumbers): string
  983. {
  984. sort($lineNumbers);
  985. $lines = explode("\n", $input);
  986. foreach ($lineNumbers as $lineNumber) {
  987. --$lineNumber;
  988. unset($lines[$lineNumber]);
  989. }
  990. return implode("\n", $lines);
  991. }
  992. }