TrailingCommaInMultilineFixerTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  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\ControlStructure;
  13. use PhpCsFixer\Fixer\ControlStructure\TrailingCommaInMultilineFixer;
  14. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  15. /**
  16. * @author Sebastiaan Stok <s.stok@rollerscapes.net>
  17. * @author Kuba Werłos <werlos@gmail.com>
  18. *
  19. * @internal
  20. *
  21. * @covers \PhpCsFixer\Fixer\ControlStructure\TrailingCommaInMultilineFixer
  22. *
  23. * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\ControlStructure\TrailingCommaInMultilineFixer>
  24. *
  25. * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\ControlStructure\TrailingCommaInMultilineFixer
  26. */
  27. final class TrailingCommaInMultilineFixerTest extends AbstractFixerTestCase
  28. {
  29. /**
  30. * @param _AutogeneratedInputConfiguration $config
  31. *
  32. * @dataProvider provideFixCases
  33. */
  34. public function testFix(string $expected, ?string $input = null, array $config = []): void
  35. {
  36. $this->fixer->configure($config);
  37. $this->doTest($expected, $input);
  38. }
  39. public static function provideFixCases(): iterable
  40. {
  41. // long syntax tests
  42. yield ['<?php $x = array();'];
  43. yield ['<?php $x = array("foo");'];
  44. yield ['<?php $x = array("foo", );'];
  45. yield ["<?php \$x = array(\n'foo',\n);", "<?php \$x = array(\n'foo'\n);"];
  46. yield ["<?php \$x = array('foo',\n);"];
  47. yield ["<?php \$x = array('foo',\n);", "<?php \$x = array('foo'\n);"];
  48. yield ["<?php \$x = array('foo', /* boo */\n);", "<?php \$x = array('foo' /* boo */\n);"];
  49. yield ["<?php \$x = array('foo',\n/* boo */\n);", "<?php \$x = array('foo'\n/* boo */\n);"];
  50. yield ["<?php \$x = array(\narray('foo',\n),\n);", "<?php \$x = array(\narray('foo'\n)\n);"];
  51. yield ["<?php \$x = array(\narray('foo'),\n);", "<?php \$x = array(\narray('foo')\n);"];
  52. yield ["<?php \$x = array(\n /* He */ \n);"];
  53. yield [
  54. "<?php \$x = array('a', 'b', 'c',\n 'd', 'q', 'z');",
  55. ];
  56. yield [
  57. "<?php \$x = array('a', 'b', 'c',\n'd', 'q', 'z');",
  58. ];
  59. yield [
  60. "<?php \$x = array('a', 'b', 'c',\n'd', 'q', 'z' );",
  61. ];
  62. yield [
  63. "<?php \$x = array('a', 'b', 'c',\n'd', 'q', 'z'\t);",
  64. ];
  65. yield ["<?php \$x = array(\n<<<EOT\noet\nEOT\n);"];
  66. yield ["<?php \$x = array(\n<<<'EOT'\noet\nEOT\n);"];
  67. yield [
  68. '<?php
  69. $foo = array(
  70. array(
  71. ),
  72. );',
  73. ];
  74. yield [
  75. '<?php
  76. $a = array(
  77. 1 => array(
  78. 2 => 3,
  79. ),
  80. );',
  81. '<?php
  82. $a = array(
  83. 1 => array(
  84. 2 => 3
  85. )
  86. );',
  87. ];
  88. yield [
  89. "<?php
  90. \$x = array(
  91. 'foo',
  92. 'bar',
  93. array(
  94. 'foo',
  95. 'bar',
  96. array(
  97. 'foo',
  98. 'bar',
  99. array(
  100. 'foo',
  101. ('bar' ? true : !false),
  102. ('bar' ? array(true) : !(false)),
  103. array(
  104. 'foo',
  105. 'bar',
  106. array(
  107. 'foo',
  108. ('bar'),
  109. ),
  110. ),
  111. ),
  112. ),
  113. ),
  114. );",
  115. "<?php
  116. \$x = array(
  117. 'foo',
  118. 'bar',
  119. array(
  120. 'foo',
  121. 'bar',
  122. array(
  123. 'foo',
  124. 'bar',
  125. array(
  126. 'foo',
  127. ('bar' ? true : !false),
  128. ('bar' ? array(true) : !(false)),
  129. array(
  130. 'foo',
  131. 'bar',
  132. array(
  133. 'foo',
  134. ('bar'),
  135. )
  136. )
  137. )
  138. )
  139. )
  140. );",
  141. ];
  142. yield [
  143. '<?php
  144. $a = array("foo" => function ($b) {
  145. return "bar".$b;
  146. });',
  147. ];
  148. yield [
  149. '<?php
  150. return array(
  151. "a" => 1,
  152. "b" => 2,
  153. );',
  154. '<?php
  155. return array(
  156. "a" => 1,
  157. "b" => 2
  158. );',
  159. ];
  160. yield [
  161. '<?php
  162. $test = array("foo", <<<TWIG
  163. foo
  164. bar
  165. baz
  166. TWIG
  167. , $twig);',
  168. ];
  169. yield [
  170. '<?php
  171. $test = array("foo", <<<\'TWIG\'
  172. foo
  173. bar
  174. baz
  175. TWIG
  176. , $twig);',
  177. ];
  178. // short syntax tests
  179. yield ['<?php $x = array([]);'];
  180. yield ['<?php $x = [[]];'];
  181. yield ['<?php $x = ["foo",];'];
  182. yield ['<?php $x = bar(["foo",]);'];
  183. yield ["<?php \$x = bar(['foo',\n]);", "<?php \$x = bar(['foo'\n]);"];
  184. yield ["<?php \$x = ['foo', \n];"];
  185. yield ['<?php $x = array([],);'];
  186. yield ['<?php $x = [[],];'];
  187. yield ['<?php $x = [$y,];'];
  188. yield ["<?php \$x = [\n /* He */ \n];"];
  189. yield [
  190. '<?php
  191. $foo = [
  192. [
  193. ],
  194. ];',
  195. ];
  196. yield [
  197. '<?php
  198. $a = ["foo" => function ($b) {
  199. return "bar".$b;
  200. }];',
  201. ];
  202. yield [
  203. '<?php
  204. return [
  205. "a" => 1,
  206. "b" => 2,
  207. ];',
  208. '<?php
  209. return [
  210. "a" => 1,
  211. "b" => 2
  212. ];',
  213. ];
  214. yield [
  215. '<?php
  216. $test = ["foo", <<<TWIG
  217. foo
  218. bar
  219. baz
  220. TWIG
  221. , $twig];',
  222. ];
  223. yield [
  224. '<?php
  225. $test = ["foo", <<<\'TWIG\'
  226. foo
  227. bar
  228. baz
  229. TWIG
  230. , $twig];',
  231. ];
  232. // no array tests
  233. yield [
  234. "<?php
  235. throw new BadMethodCallException(
  236. sprintf(
  237. 'Method \"%s\" not implemented',
  238. __METHOD__
  239. )
  240. );",
  241. ];
  242. yield [
  243. "<?php
  244. throw new BadMethodCallException(sprintf(
  245. 'Method \"%s\" not implemented',
  246. __METHOD__
  247. ));",
  248. ];
  249. yield [
  250. "<?php
  251. namespace FOS\\RestBundle\\Controller;
  252. class ExceptionController extends ContainerAware
  253. {
  254. public function showAction(Request \$request, \$exception, DebugLoggerInterface \$logger = null, \$format = 'html')
  255. {
  256. if (!\$exception instanceof DebugFlattenException && !\$exception instanceof HttpFlattenException) {
  257. throw new \\InvalidArgumentException(sprintf(
  258. 'ExceptionController::showAction can only accept some exceptions (%s, %s), \"%s\" given',
  259. 'Symfony\\Component\\HttpKernel\\Exception\\FlattenException',
  260. 'Symfony\\Component\\Debug\\Exception\\FlattenException',
  261. get_class(\$exception)
  262. ));
  263. }
  264. }
  265. }",
  266. ];
  267. yield [
  268. '<?php
  269. function foo(array $a)
  270. {
  271. bar(
  272. baz(
  273. 1
  274. )
  275. );
  276. }',
  277. ];
  278. yield [
  279. '<?php
  280. $var = array(
  281. "string",
  282. //comment
  283. );',
  284. '<?php
  285. $var = array(
  286. "string"
  287. //comment
  288. );',
  289. ];
  290. yield [
  291. '<?php
  292. $var = array(
  293. "string",
  294. /* foo */);',
  295. '<?php
  296. $var = array(
  297. "string"
  298. /* foo */);',
  299. ];
  300. yield [
  301. '<?php
  302. $var = [
  303. "string",
  304. /* foo */];',
  305. '<?php
  306. $var = [
  307. "string"
  308. /* foo */];',
  309. ];
  310. yield [
  311. '<?php
  312. function a()
  313. {
  314. yield array(
  315. "a" => 1,
  316. "b" => 2,
  317. );
  318. }',
  319. '<?php
  320. function a()
  321. {
  322. yield array(
  323. "a" => 1,
  324. "b" => 2
  325. );
  326. }',
  327. ];
  328. yield [
  329. '<?php
  330. function a()
  331. {
  332. yield [
  333. "a" => 1,
  334. "b" => 2,
  335. ];
  336. }',
  337. '<?php
  338. function a()
  339. {
  340. yield [
  341. "a" => 1,
  342. "b" => 2
  343. ];
  344. }',
  345. ];
  346. yield ['<?php
  347. while(
  348. (
  349. (
  350. $a
  351. )
  352. )
  353. ) {}',
  354. ];
  355. yield [
  356. "<?php foo('a', 'b', 'c', 'd', 'q', 'z');",
  357. null,
  358. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  359. ];
  360. yield [
  361. "<?php function foo(\$a,\n\$b\n) {};",
  362. null,
  363. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  364. ];
  365. yield [
  366. '<?php foo(1, 2, [
  367. ARRAY_ELEMENT_1,
  368. ARRAY_ELEMENT_2
  369. ], 3, 4);',
  370. null,
  371. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  372. ];
  373. yield [
  374. "<?php \$var = array('a', 'b',\n 'c', 'd');",
  375. null,
  376. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  377. ];
  378. yield [
  379. "<?php \$var = list(\$a, \$b,\n \$c, \$d) = [1, 2, 3, 4];",
  380. null,
  381. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  382. ];
  383. yield [
  384. "<?php if (true || \n false) {}",
  385. null,
  386. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  387. ];
  388. yield [
  389. "<?php \$var = foo('a', 'b', 'c',\n 'd', 'q', 'z');",
  390. null, // do not fix if not configured
  391. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARRAYS]],
  392. ];
  393. yield [
  394. "<?php \$var = foo('a', 'b', 'c',\n 'd', 'q', 'z');",
  395. null,
  396. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  397. ];
  398. yield [
  399. "<?php \$var = foo('a', 'b', 'c',\n 'd', 'q', 'z',\n);",
  400. "<?php \$var = foo('a', 'b', 'c',\n 'd', 'q', 'z'\n);",
  401. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  402. ];
  403. yield [
  404. "<?php \$var = \$foonction('a', 'b', 'c',\n 'd', 'q', 'z');",
  405. null,
  406. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  407. ];
  408. yield [
  409. "<?php \$var = \$fMap[100]('a', 'b', 'c',\n 'd', 'q', 'z');",
  410. null,
  411. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  412. ];
  413. yield [
  414. "<?php \$var = new Foo('a', 'b', 'c',\n 'd', 'q', 'z',\n);",
  415. "<?php \$var = new Foo('a', 'b', 'c',\n 'd', 'q', 'z'\n);",
  416. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  417. ];
  418. yield [
  419. "<?php \$var = new class('a', 'b', 'c',\n 'd', 'q', 'z',\n) extends Foo {};",
  420. "<?php \$var = new class('a', 'b', 'c',\n 'd', 'q', 'z'\n) extends Foo {};",
  421. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  422. ];
  423. yield [
  424. '<?php
  425. $obj->method(
  426. 1,
  427. 2,
  428. );
  429. ',
  430. '<?php
  431. $obj->method(
  432. 1,
  433. 2
  434. );
  435. ',
  436. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  437. ];
  438. yield 'function-like language constructs' => [
  439. '<?php
  440. isset(
  441. $a,
  442. $b,
  443. );
  444. unset(
  445. $a,
  446. $b,
  447. );
  448. list(
  449. $a,
  450. $b,
  451. ) = $foo;
  452. ',
  453. '<?php
  454. isset(
  455. $a,
  456. $b
  457. );
  458. unset(
  459. $a,
  460. $b
  461. );
  462. list(
  463. $a,
  464. $b
  465. ) = $foo;
  466. ',
  467. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  468. ];
  469. yield [
  470. '<?php
  471. array(
  472. 1,
  473. 2,
  474. );
  475. [
  476. 3,
  477. 4,
  478. ];
  479. foo(
  480. 5,
  481. 6,
  482. );
  483. ',
  484. '<?php
  485. array(
  486. 1,
  487. 2
  488. );
  489. [
  490. 3,
  491. 4
  492. ];
  493. foo(
  494. 5,
  495. 6
  496. );
  497. ',
  498. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARRAYS, TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  499. ];
  500. yield [
  501. '<?php
  502. while(
  503. (
  504. (
  505. $a
  506. )
  507. )
  508. ) {}',
  509. null,
  510. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARRAYS, TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  511. ];
  512. yield [
  513. <<<'EXPECTED'
  514. <?php
  515. $a = [
  516. <<<'EOD'
  517. foo
  518. EOD,
  519. ];
  520. EXPECTED,
  521. <<<'INPUT'
  522. <?php
  523. $a = [
  524. <<<'EOD'
  525. foo
  526. EOD
  527. ];
  528. INPUT,
  529. ['after_heredoc' => true],
  530. ];
  531. yield [
  532. '<?php $a = new class() {function A() { return new static(
  533. 1,
  534. 2,
  535. ); }};',
  536. '<?php $a = new class() {function A() { return new static(
  537. 1,
  538. 2
  539. ); }};',
  540. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  541. ];
  542. yield [
  543. '<?php
  544. $a = [11,2,3];
  545. [
  546. $c,
  547. $d,
  548. ] = $a;
  549. ',
  550. '<?php
  551. $a = [11,2,3];
  552. [
  553. $c,
  554. $d
  555. ] = $a;
  556. ',
  557. ['elements' => ['array_destructuring']],
  558. ];
  559. }
  560. /**
  561. * @requires PHP <8.0
  562. */
  563. public function testFixPre80(): void
  564. {
  565. $this->fixer->configure([
  566. 'elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARRAYS, TrailingCommaInMultilineFixer::ELEMENTS_PARAMETERS],
  567. ]);
  568. // ELEMENTS_PARAMETERS got unconfigured, as not valid <8.0
  569. $this->doTest(
  570. "<?php function foo(\$a\n) { return [1,\n]; }",
  571. "<?php function foo(\$a\n) { return [1\n]; }",
  572. );
  573. }
  574. /**
  575. * @param _AutogeneratedInputConfiguration $config
  576. *
  577. * @dataProvider provideFix80Cases
  578. *
  579. * @requires PHP 8.0
  580. */
  581. public function testFix80(string $expected, ?string $input = null, array $config = []): void
  582. {
  583. $this->fixer->configure($config);
  584. $this->doTest($expected, $input);
  585. }
  586. public static function provideFix80Cases(): iterable
  587. {
  588. yield [
  589. '<?php function foo($x, $y) {}',
  590. null,
  591. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_PARAMETERS]],
  592. ];
  593. yield [
  594. '<?php function foo(
  595. $x,
  596. $y
  597. ) {}',
  598. null, // do not fix if not configured
  599. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARRAYS, TrailingCommaInMultilineFixer::ELEMENTS_ARGUMENTS]],
  600. ];
  601. yield [
  602. '<?php function foo(
  603. $x,
  604. $y,
  605. ) {}',
  606. '<?php function foo(
  607. $x,
  608. $y
  609. ) {}',
  610. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_PARAMETERS]],
  611. ];
  612. yield [
  613. '<?php $x = function(
  614. $x,
  615. $y,
  616. ) {};',
  617. '<?php $x = function(
  618. $x,
  619. $y
  620. ) {};',
  621. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_PARAMETERS]],
  622. ];
  623. yield [
  624. '<?php $x = fn(
  625. $x,
  626. $y,
  627. ) => $x + $y;',
  628. '<?php $x = fn(
  629. $x,
  630. $y
  631. ) => $x + $y;',
  632. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_PARAMETERS]],
  633. ];
  634. yield 'match' => [
  635. '<?php
  636. $m = match ($a) {
  637. 200, 300 => null,
  638. 400 => 1,
  639. 500 => function() {return 2;},
  640. 600 => static function() {return 4;},
  641. default => 3,
  642. };
  643. $z = match ($a) {
  644. 1 => 0,
  645. 2 => 1,
  646. };
  647. $b = match($c) {19 => 28, default => 333};
  648. ',
  649. '<?php
  650. $m = match ($a) {
  651. 200, 300 => null,
  652. 400 => 1,
  653. 500 => function() {return 2;},
  654. 600 => static function() {return 4;},
  655. default => 3
  656. };
  657. $z = match ($a) {
  658. 1 => 0,
  659. 2 => 1
  660. };
  661. $b = match($c) {19 => 28, default => 333};
  662. ',
  663. ['elements' => ['match']],
  664. ];
  665. yield 'match with last comma in the same line as closing brace' => [
  666. '<?php
  667. $x = match ($a) { 1 => 0,
  668. 2 => 1 };
  669. ',
  670. null,
  671. ['elements' => ['match']],
  672. ];
  673. yield 'match and parameters' => [
  674. '<?php function foo(
  675. $a,
  676. ) {
  677. return [
  678. 1,
  679. ];
  680. }',
  681. '<?php function foo(
  682. $a
  683. ) {
  684. return [
  685. 1
  686. ];
  687. }',
  688. ['elements' => [TrailingCommaInMultilineFixer::ELEMENTS_ARRAYS, TrailingCommaInMultilineFixer::ELEMENTS_PARAMETERS]],
  689. ];
  690. }
  691. }