NoExtraBlankLinesFixerTest.php 25 KB

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