TrailingCommaInMultilineFixerTest.php 16 KB

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