NoExtraBlankLinesFixerTest.php 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291
  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. return [
  262. [
  263. <<<'EOF'
  264. <?php
  265. //class Test
  266. $a; //
  267. $b;
  268. /***/
  269. $c;
  270. //
  271. $d;
  272. EOF
  273. ,
  274. <<<'EOF'
  275. <?php
  276. //class Test
  277. $a; //
  278. $b;
  279. /***/
  280. $c;
  281. //
  282. $d;
  283. EOF
  284. ],
  285. [
  286. "<?php\n//a\n\n\$a =1;",
  287. "<?php\n//a\n\n\n\n\$a =1;",
  288. ],
  289. ];
  290. }
  291. /**
  292. * @dataProvider provideFixWithLineBreaksCases
  293. */
  294. public function testFixWithLineBreaks(string $expected, ?string $input = null): void
  295. {
  296. $this->doTest($expected, $input);
  297. }
  298. public static function provideFixWithLineBreaksCases(): iterable
  299. {
  300. $input = '<?php //
  301. $a = 1;
  302. $b = 1;
  303. ';
  304. $expected = '<?php //
  305. $a = 1;
  306. $b = 1;
  307. ';
  308. yield [
  309. "<?php\r\n//a\r\n\r\n\$a =1;",
  310. "<?php\r\n//a\r\n\r\n\r\n\r\n\$a =1;",
  311. ];
  312. yield [
  313. $expected,
  314. $input,
  315. ];
  316. yield [
  317. str_replace("\n", "\r\n", $expected),
  318. str_replace("\n", "\r\n", $input),
  319. ];
  320. yield [
  321. str_replace("\n", "\r", $input),
  322. ];
  323. yield [
  324. str_replace("\n", "\r", $expected),
  325. ];
  326. }
  327. public function testWrongConfig(): void
  328. {
  329. $this->expectException(InvalidFixerConfigurationException::class);
  330. $this->expectExceptionMessageMatches('/^\[no_extra_blank_lines\] Invalid configuration: The option "tokens" .*\.$/');
  331. $this->fixer->configure(['tokens' => ['__TEST__']]);
  332. }
  333. /**
  334. * @dataProvider provideBetweenUseCases
  335. */
  336. public function testBetweenUse(string $expected, ?string $input = null): void
  337. {
  338. $this->fixer->configure(['tokens' => ['use']]);
  339. $this->doTest($expected, $input);
  340. }
  341. public static function provideBetweenUseCases(): iterable
  342. {
  343. yield ['<?php use A\B;'];
  344. yield ['<?php use A\B?>'];
  345. yield ['<?php use A\B;use A\D; return 1;'];
  346. yield ["<?php use A\\B?>\n\n<?php use D\\E\\F?>"];
  347. yield ['<?php use Y\B;use A\D; return 1;'];
  348. yield [
  349. '<?php
  350. use A\B;
  351. use A\C;',
  352. '<?php
  353. use A\B;
  354. use A\C;',
  355. ];
  356. yield [
  357. '<?php use A\E;use A\Z;
  358. use C;
  359. return 1;
  360. ',
  361. '<?php use A\E;use A\Z;
  362. use C;
  363. return 1;
  364. ',
  365. ];
  366. yield [
  367. '<?php
  368. class Test {
  369. use A;
  370. use B;
  371. }',
  372. ];
  373. yield [
  374. '<?php
  375. $example = function () use ($message) { var_dump($message); };
  376. $example = function () use ($message) { var_dump($message); };
  377. ',
  378. ];
  379. yield [
  380. '<?php
  381. use function A; use function B;
  382. echo 1;',
  383. ];
  384. yield [
  385. '<?php
  386. use some\a\{ClassA, ClassB, ClassC as C,};
  387. use function some\a\{fn_a, fn_b, fn_c,};
  388. use const some\a\{ConstA,ConstB,ConstC
  389. ,
  390. };
  391. use const some\Z\{ConstX,ConstY,ConstZ,};
  392. ',
  393. '<?php
  394. use some\a\{ClassA, ClassB, ClassC as C,};
  395. use function some\a\{fn_a, fn_b, fn_c,};
  396. use const some\a\{ConstA,ConstB,ConstC
  397. ,
  398. };
  399. '.'
  400. use const some\Z\{ConstX,ConstY,ConstZ,};
  401. ',
  402. ];
  403. }
  404. /**
  405. * @dataProvider provideRemoveLinesBetweenUseStatementsCases
  406. */
  407. public function testRemoveLinesBetweenUseStatements(string $expected, ?string $input = null): void
  408. {
  409. $this->fixer->configure(['tokens' => ['use']]);
  410. $this->doTest($expected, $input);
  411. }
  412. public static function provideRemoveLinesBetweenUseStatementsCases(): iterable
  413. {
  414. return [
  415. [
  416. <<<'EOF'
  417. <?php
  418. use Zxy\Qux;
  419. use Zoo\Bar as Bar2;
  420. use Foo\Bar as Bar1;
  421. use Foo\Zar\Baz;
  422. $c = 1;
  423. use Foo\Quxx as Quxx1;
  424. use Foo\Zar\Quxx;
  425. $a = new Bar1();
  426. $a = new Bar2();
  427. $a = new Baz();
  428. $a = new Qux();
  429. EOF
  430. ,
  431. <<<'EOF'
  432. <?php
  433. use Zxy\Qux;
  434. use Zoo\Bar as Bar2;
  435. use Foo\Bar as Bar1;
  436. use Foo\Zar\Baz;
  437. $c = 1;
  438. use Foo\Quxx as Quxx1;
  439. use Foo\Zar\Quxx;
  440. $a = new Bar1();
  441. $a = new Bar2();
  442. $a = new Baz();
  443. $a = new Qux();
  444. EOF
  445. ,
  446. ],
  447. [
  448. '<?php
  449. use some\a\{ClassA, ClassB, ClassC as C};
  450. use function some\a\{fn_a, fn_b, fn_c};
  451. use const some\a\{ConstA, ConstB, ConstC};
  452. ',
  453. '<?php
  454. use some\a\{ClassA, ClassB, ClassC as C};
  455. use function some\a\{fn_a, fn_b, fn_c};
  456. use const some\a\{ConstA, ConstB, ConstC};
  457. ',
  458. ],
  459. ];
  460. }
  461. /**
  462. * @dataProvider provideWithoutUsesCases
  463. */
  464. public function testWithoutUses(string $expected): void
  465. {
  466. $this->fixer->configure(['tokens' => ['use']]);
  467. $this->doTest($expected);
  468. }
  469. public static function provideWithoutUsesCases(): iterable
  470. {
  471. yield [
  472. '<?php
  473. $c = 1;
  474. $a = new Baz();
  475. $a = new Qux();',
  476. ];
  477. yield [
  478. '<?php use A\B;',
  479. ];
  480. yield [
  481. '<?php use A\B?>',
  482. ];
  483. yield [
  484. '<?php use A\B;?>',
  485. ];
  486. }
  487. /**
  488. * @dataProvider provideRemoveBetweenUseTraitsCases
  489. *
  490. * @group legacy
  491. */
  492. public function testRemoveBetweenUseTraits(string $expected, string $input): void
  493. {
  494. $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.');
  495. $this->fixer->configure(['tokens' => ['use_trait']]);
  496. $this->doTest($expected, $input);
  497. }
  498. public static function provideRemoveBetweenUseTraitsCases(): iterable
  499. {
  500. yield [
  501. '<?php
  502. class Foo
  503. {
  504. use Z; // 123
  505. use Bar;/* */use Baz;
  506. public function baz() {}
  507. use Bar1; use Baz1;
  508. public function baz1() {}
  509. }
  510. ',
  511. '<?php
  512. class Foo
  513. {
  514. use Z; // 123
  515. use Bar;/* */use Baz;
  516. public function baz() {}
  517. use Bar1; use Baz1;
  518. public function baz1() {}
  519. }
  520. ',
  521. ];
  522. yield [
  523. '<?php
  524. class Foo
  525. {
  526. use Bar;use Baz;
  527. use Bar1;use Baz1;
  528. public function baz() {}
  529. }
  530. ',
  531. '<?php
  532. class Foo
  533. {
  534. use Bar;use Baz;
  535. use Bar1;use Baz1;
  536. public function baz() {}
  537. }
  538. ',
  539. ];
  540. yield [
  541. '<?php
  542. namespace T\A;
  543. use V;
  544. use W;
  545. class Test {
  546. use A;
  547. use B;
  548. private function test($b) {
  549. $a = function() use ($b) { echo $b;};
  550. $b = function() use ($b) { echo $b;};
  551. }
  552. }',
  553. '<?php
  554. namespace T\A;
  555. use V;
  556. use W;
  557. class Test {
  558. use A;
  559. use B;
  560. private function test($b) {
  561. $a = function() use ($b) { echo $b;};
  562. $b = function() use ($b) { echo $b;};
  563. }
  564. }',
  565. ];
  566. }
  567. /**
  568. * @dataProvider provideOneOrInLineCases
  569. */
  570. public function testOneOrInLine(string $expected, ?string $input = null): void
  571. {
  572. $this->fixer->configure(['tokens' => [
  573. 'break',
  574. 'continue',
  575. 'return',
  576. 'throw',
  577. 'curly_brace_block',
  578. 'square_brace_block',
  579. 'parenthesis_brace_block',
  580. ]]);
  581. $this->doTest($expected, $input);
  582. }
  583. public static function provideOneOrInLineCases(): iterable
  584. {
  585. yield [
  586. "<?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",
  587. ];
  588. yield [
  589. "<?php throw new \\Exception('do not import.');\n",
  590. "<?php throw new \\Exception('do not import.');\n\n",
  591. ];
  592. yield [
  593. "<?php\n\n\$a = \$b[0];\n\n",
  594. ];
  595. yield [
  596. "<?php\n\n\$a->{'Test'};\nfunction test(){}\n",
  597. ];
  598. yield [
  599. "<?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",
  600. ];
  601. if (\PHP_VERSION_ID < 8_00_00) {
  602. yield [
  603. "<?php\n\n\$a = \$b{0};\n\n",
  604. ];
  605. }
  606. }
  607. /**
  608. * @param array<string, mixed> $config
  609. *
  610. * @dataProvider provideBracesCases
  611. */
  612. public function testBraces(array $config, string $expected, ?string $input = null): void
  613. {
  614. $this->fixer->configure($config);
  615. $this->doTest($expected, $input);
  616. }
  617. public static function provideBracesCases(): iterable
  618. {
  619. return [
  620. [
  621. ['tokens' => ['curly_brace_block']],
  622. "<?php function test()\n\n{}\n\necho 789;",
  623. ],
  624. [
  625. ['tokens' => ['curly_brace_block']],
  626. "<?php switch(\$a){\ncase 1:echo 789;}",
  627. "<?php switch(\$a){\n \ncase 1:echo 789;}",
  628. ],
  629. [
  630. ['tokens' => ['parenthesis_brace_block']],
  631. '<?php
  632. is_int(
  633. 1);
  634. function test(
  635. $a,
  636. $b,
  637. $c
  638. )
  639. {
  640. }',
  641. '<?php
  642. is_int(
  643. 1);
  644. function test(
  645. $a,
  646. $b,
  647. $c
  648. )
  649. {
  650. }',
  651. ],
  652. [
  653. ['tokens' => ['parenthesis_brace_block']],
  654. "<?php array(\n1,\n2,\n3,\n);",
  655. "<?php array(\n \n1,\n2,\n3,\n\n\n);",
  656. ],
  657. [
  658. ['tokens' => ['parenthesis_brace_block']],
  659. '<?php
  660. function a()
  661. {
  662. $b->d(e(
  663. ));
  664. foreach ($a as $x) {
  665. }
  666. }',
  667. ],
  668. [
  669. ['tokens' => ['return']],
  670. '<?php
  671. class Foo
  672. {
  673. public function bar() {return 1;}
  674. public function baz() {return 2;
  675. }
  676. }',
  677. '<?php
  678. class Foo
  679. {
  680. public function bar() {return 1;}
  681. public function baz() {return 2;
  682. }
  683. }',
  684. ],
  685. [
  686. ['tokens' => ['square_brace_block']],
  687. "<?php \$c = \$b[0];\n\n\n\$a = [\n 1,\n2];\necho 1;\n\$b = [];\n\n\n//a\n",
  688. "<?php \$c = \$b[0];\n\n\n\$a = [\n\n 1,\n2];\necho 1;\n\$b = [];\n\n\n//a\n",
  689. ],
  690. ];
  691. }
  692. /**
  693. * @param array<string, mixed> $config
  694. *
  695. * @dataProvider provideMessyWhitespacesCases
  696. */
  697. public function testMessyWhitespaces(array $config, string $expected, ?string $input = null): void
  698. {
  699. $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n"));
  700. $this->fixer->configure($config);
  701. $this->doTest($expected, $input);
  702. }
  703. public static function provideMessyWhitespacesCases(): iterable
  704. {
  705. yield [
  706. [],
  707. "<?php\r\nuse AAA;\r\n\r\nuse BBB;\r\n\r\n",
  708. "<?php\r\nuse AAA;\r\n\r\n\r\n\r\nuse BBB;\r\n\r\n",
  709. ];
  710. yield [
  711. ['tokens' => ['parenthesis_brace_block']],
  712. "<?php is_int(\r\n1);",
  713. "<?php is_int(\r\n\r\n\r\n\r\n1);",
  714. ];
  715. yield [
  716. ['tokens' => ['square_brace_block']],
  717. "<?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",
  718. "<?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",
  719. ];
  720. yield [
  721. ['tokens' => ['square_brace_block']],
  722. "<?php \$c = \$b[0];\r\n\r\n\r\n\$a = [\r\n\t1,\r\n2];",
  723. "<?php \$c = \$b[0];\r\n\r\n\r\n\$a = [\r\n\r\n\t1,\r\n2];",
  724. ];
  725. }
  726. /**
  727. * @param list<string> $config
  728. *
  729. * @dataProvider provideInSwitchStatementCases
  730. */
  731. public function testInSwitchStatement(array $config, string $expected, ?string $input = null): void
  732. {
  733. $this->fixer->configure(['tokens' => $config]);
  734. $this->doTest($expected, $input);
  735. }
  736. public static function provideInSwitchStatementCases(): iterable
  737. {
  738. return [
  739. [
  740. [
  741. 'break',
  742. 'continue',
  743. 'extra',
  744. 'return',
  745. 'throw',
  746. ],
  747. '<?php
  748. /** a */
  749. switch ($a) {
  750. case 1:
  751. break;
  752. case 2:
  753. continue;
  754. case 3:
  755. return 1;
  756. case 4:
  757. throw $e;
  758. case 5:
  759. throw new \Exception();
  760. case Token::STRING_TYPE:
  761. echo 123;
  762. return new ConstantNode($token->getValue());
  763. case 7:
  764. return new ConstantNode($token->getValue());
  765. case 8:
  766. return 8;
  767. default:
  768. echo 1;
  769. }',
  770. '<?php
  771. /** a */
  772. switch ($a) {
  773. case 1:
  774. break;
  775. case 2:
  776. continue;
  777. case 3:
  778. return 1;
  779. case 4:
  780. throw $e;
  781. case 5:
  782. throw new \Exception();
  783. case Token::STRING_TYPE:
  784. echo 123;
  785. return new ConstantNode($token->getValue());
  786. case 7:
  787. return new ConstantNode($token->getValue());
  788. '.'
  789. case 8:
  790. return 8;
  791. '.'
  792. default:
  793. echo 1;
  794. }',
  795. ],
  796. [
  797. [
  798. 'switch',
  799. 'case',
  800. 'default',
  801. ],
  802. '<?php
  803. switch($a) {
  804. case 0:
  805. case 1:
  806. default:
  807. return 1;
  808. }',
  809. '<?php
  810. switch($a) {
  811. case 0:
  812. case 1:
  813. default:
  814. return 1;
  815. }',
  816. ],
  817. [
  818. [
  819. 'switch',
  820. 'case',
  821. 'default',
  822. ],
  823. '<?php
  824. switch($a) { case 2: echo 3;
  825. default: return 1;}
  826. // above stays empty',
  827. '<?php
  828. switch($a) { case 2: echo 3;
  829. default: return 1;}
  830. // above stays empty',
  831. ],
  832. ];
  833. }
  834. public function testRemovingEmptyLinesAfterOpenTag(): void
  835. {
  836. $this->doTest(
  837. '<?php
  838. class Foo {}',
  839. '<?php
  840. class Foo {}'
  841. );
  842. }
  843. /**
  844. * @param array<string, mixed> $config
  845. *
  846. * @dataProvider provideFix80Cases
  847. *
  848. * @requires PHP 8.0
  849. */
  850. public function testFix80(array $config, string $expected, string $input = null): void
  851. {
  852. $this->fixer->configure($config);
  853. $this->doTest($expected, $input);
  854. }
  855. public static function provideFix80Cases(): iterable
  856. {
  857. yield [
  858. ['tokens' => ['throw']],
  859. '<?php
  860. $a = $bar ?? throw new \Exception();
  861. $a = $bar ?? throw new \Exception();
  862. $a = $bar ?? throw new \Exception();
  863. ',
  864. ];
  865. yield [
  866. ['tokens' => ['throw']],
  867. '<?php
  868. $a = $bar ?? throw new \Exception();
  869. // Now, we are going to use it!
  870. var_dump($a);
  871. ',
  872. ];
  873. yield [
  874. ['tokens' => ['attribute']],
  875. '<?php
  876. #[Attr]
  877. #[AttrFoo1]
  878. #[AttrFoo2]
  879. function foo(){}
  880. ',
  881. '<?php
  882. #[Attr]
  883. #[AttrFoo1]
  884. #[AttrFoo2]
  885. function foo(){}
  886. ',
  887. ];
  888. yield [
  889. ['tokens' => ['attribute']],
  890. '<?php class Foo
  891. {
  892. protected function f1(string $x): string { return "r1"; }
  893. protected function f2(string $x): string { return "r1"; }
  894. protected function f3(#[Attr] string $x): string { return "r1"; }
  895. protected function f4(string $x): string { return "r1"; }
  896. }',
  897. ];
  898. yield [
  899. ['tokens' => ['attribute']],
  900. '<?php abstract class Foo
  901. {
  902. abstract protected function f1(string $x): string;
  903. abstract protected function f2(string $x): string;
  904. abstract protected function f3(#[Attr] string $x): string;
  905. abstract protected function f4(string $x): string;
  906. }',
  907. ];
  908. }
  909. /**
  910. * @dataProvider provideFix81Cases
  911. *
  912. * @requires PHP 8.1
  913. */
  914. public function testFix81(string $expected, string $input = null): void
  915. {
  916. $this->fixer->configure(['tokens' => ['case']]);
  917. $this->doTest($expected, $input);
  918. }
  919. public static function provideFix81Cases(): iterable
  920. {
  921. yield [
  922. '<?php
  923. enum test
  924. {
  925. case Baz;
  926. public function foo() {
  927. switch (bar()) {
  928. case 1: echo 1; break;
  929. case 2: echo 2; break;
  930. }
  931. }
  932. case Bad;
  933. }
  934. ',
  935. '<?php
  936. enum test
  937. {
  938. case Baz;
  939. public function foo() {
  940. switch (bar()) {
  941. case 1: echo 1; break;
  942. case 2: echo 2; break;
  943. }
  944. }
  945. case Bad;
  946. }
  947. ',
  948. ];
  949. $expectedTemplate = '<?php
  950. enum Foo
  951. {
  952. case CASE_1;
  953. %s
  954. }';
  955. $enumAttributes = [
  956. 'case CASE_2;',
  957. 'const CONST_1 = self::CASE_1;',
  958. 'private const CONST_1 = self::CASE_1;',
  959. 'public function bar(): void {}',
  960. 'protected function bar(): void {}',
  961. 'private function bar(): void {}',
  962. 'static function bar(): void {}',
  963. 'final function bar(): void {}',
  964. ];
  965. foreach ($enumAttributes as $enumAttribute) {
  966. yield [
  967. sprintf($expectedTemplate, $enumAttribute),
  968. ];
  969. }
  970. }
  971. /**
  972. * @param list<int> $lineNumbers
  973. */
  974. private function removeLinesFromString(string $input, array $lineNumbers): string
  975. {
  976. sort($lineNumbers);
  977. $lines = explode("\n", $input);
  978. foreach ($lineNumbers as $lineNumber) {
  979. --$lineNumber;
  980. unset($lines[$lineNumber]);
  981. }
  982. return implode("\n", $lines);
  983. }
  984. }