TrailingCommaInMultilineFixerTest.php 17 KB

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