PhpdocLineSpanFixerTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  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\Phpdoc;
  13. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  14. /**
  15. * @author Gert de Pagter <BackEndTea@gmail.com>
  16. *
  17. * @internal
  18. *
  19. * @covers \PhpCsFixer\Fixer\Phpdoc\PhpdocLineSpanFixer
  20. */
  21. final class PhpdocLineSpanFixerTest extends AbstractFixerTestCase
  22. {
  23. /**
  24. * @dataProvider provideFixCases
  25. *
  26. * @param array<string, mixed> $config
  27. */
  28. public function testFix(string $expected, ?string $input = null, array $config = []): void
  29. {
  30. $this->fixer->configure($config);
  31. $this->doTest($expected, $input);
  32. }
  33. public static function provideFixCases(): iterable
  34. {
  35. yield 'It does not change doc blocks if not needed' => [
  36. '<?php
  37. class Foo
  38. {
  39. /**
  40. * Important
  41. */
  42. const FOO_BAR = "foobar";
  43. /**
  44. * @var bool
  45. */
  46. public $variable = true;
  47. /**
  48. * @var bool
  49. */
  50. private $var = false;
  51. /**
  52. * @return void
  53. */
  54. public function hello() {}
  55. }
  56. ',
  57. ];
  58. yield 'It does change doc blocks to multi by default' => [
  59. '<?php
  60. class Foo
  61. {
  62. /**
  63. * Important
  64. */
  65. const FOO_BAR = "foobar";
  66. /**
  67. * @var bool
  68. */
  69. public $variable = true;
  70. /**
  71. * @var bool
  72. */
  73. private $var = false;
  74. /**
  75. * @return void
  76. */
  77. public function hello() {}
  78. }
  79. ',
  80. '<?php
  81. class Foo
  82. {
  83. /** Important */
  84. const FOO_BAR = "foobar";
  85. /** @var bool */
  86. public $variable = true;
  87. /** @var bool */
  88. private $var = false;
  89. /** @return void */
  90. public function hello() {}
  91. }
  92. ',
  93. ];
  94. yield 'It does change doc blocks to single if configured to do so' => [
  95. '<?php
  96. class Foo
  97. {
  98. /** Important */
  99. const FOO_BAR = "foobar";
  100. /** @var bool */
  101. public $variable = true;
  102. /** @var bool */
  103. private $var = false;
  104. /** @return void */
  105. public function hello() {}
  106. }
  107. ',
  108. '<?php
  109. class Foo
  110. {
  111. /**
  112. * Important
  113. */
  114. const FOO_BAR = "foobar";
  115. /**
  116. * @var bool
  117. */
  118. public $variable = true;
  119. /**
  120. * @var bool
  121. */
  122. private $var = false;
  123. /**
  124. * @return void
  125. */
  126. public function hello() {}
  127. }
  128. ',
  129. [
  130. 'property' => 'single',
  131. 'const' => 'single',
  132. 'method' => 'single',
  133. ],
  134. ];
  135. yield 'It does change complicated doc blocks to single if configured to do so' => [
  136. '<?php
  137. class Foo
  138. {
  139. /** @var bool */
  140. public $variable1 = true;
  141. /** @var bool */
  142. public $variable2 = true;
  143. /** @Assert\File(mimeTypes={ "image/jpeg", "image/png" }) */
  144. public $imageFileObject;
  145. }
  146. ',
  147. '<?php
  148. class Foo
  149. {
  150. /**
  151. * @var bool */
  152. public $variable1 = true;
  153. /** @var bool
  154. */
  155. public $variable2 = true;
  156. /**
  157. * @Assert\File(mimeTypes={ "image/jpeg", "image/png" })
  158. */
  159. public $imageFileObject;
  160. }
  161. ',
  162. [
  163. 'property' => 'single',
  164. ],
  165. ];
  166. yield 'It does not changes doc blocks from single if configured to do so' => [
  167. '<?php
  168. class Foo
  169. {
  170. /** Important */
  171. const FOO_BAR = "foobar";
  172. /** @var bool */
  173. public $variable = true;
  174. /** @var bool */
  175. private $var = false;
  176. /** @return void */
  177. public function hello() {}
  178. }
  179. ',
  180. null,
  181. [
  182. 'property' => 'single',
  183. 'const' => 'single',
  184. 'method' => 'single',
  185. ],
  186. ];
  187. yield 'It can be configured to change certain elements to single line' => [
  188. '<?php
  189. class Foo
  190. {
  191. /**
  192. * Important
  193. */
  194. const FOO_BAR = "foobar";
  195. /** @var bool */
  196. public $variable = true;
  197. /** @var bool */
  198. private $var = false;
  199. /**
  200. * @return void
  201. */
  202. public function hello() {}
  203. }
  204. ',
  205. '<?php
  206. class Foo
  207. {
  208. /**
  209. * Important
  210. */
  211. const FOO_BAR = "foobar";
  212. /**
  213. * @var bool
  214. */
  215. public $variable = true;
  216. /**
  217. * @var bool
  218. */
  219. private $var = false;
  220. /**
  221. * @return void
  222. */
  223. public function hello() {}
  224. }
  225. ',
  226. [
  227. 'property' => 'single',
  228. ],
  229. ];
  230. yield 'It wont change a doc block to single line if it has multiple useful lines' => [
  231. '<?php
  232. class Foo
  233. {
  234. /**
  235. * Important
  236. * Really important
  237. */
  238. const FOO_BAR = "foobar";
  239. }
  240. ',
  241. null,
  242. [
  243. 'const' => 'single',
  244. ],
  245. ];
  246. yield 'It updates doc blocks correctly, even with more indentation' => [
  247. '<?php
  248. if (false) {
  249. class Foo
  250. {
  251. /** @var bool */
  252. public $var = true;
  253. /**
  254. * @return void
  255. */
  256. public function hello () {}
  257. }
  258. }
  259. ',
  260. '<?php
  261. if (false) {
  262. class Foo
  263. {
  264. /**
  265. * @var bool
  266. */
  267. public $var = true;
  268. /** @return void */
  269. public function hello () {}
  270. }
  271. }
  272. ',
  273. [
  274. 'property' => 'single',
  275. ],
  276. ];
  277. yield 'It can convert empty doc blocks' => [
  278. '<?php
  279. class Foo
  280. {
  281. /**
  282. *
  283. */
  284. const FOO = "foobar";
  285. /** */
  286. private $foo;
  287. }',
  288. '<?php
  289. class Foo
  290. {
  291. /** */
  292. const FOO = "foobar";
  293. /**
  294. *
  295. */
  296. private $foo;
  297. }',
  298. [
  299. 'property' => 'single',
  300. ],
  301. ];
  302. yield 'It can update doc blocks of static properties' => [
  303. '<?php
  304. class Bar
  305. {
  306. /**
  307. * Important
  308. */
  309. public static $variable = "acme";
  310. }
  311. ',
  312. '<?php
  313. class Bar
  314. {
  315. /** Important */
  316. public static $variable = "acme";
  317. }
  318. ',
  319. ];
  320. yield 'It can update doc blocks of properties that use the var keyword instead of public' => [
  321. '<?php
  322. class Bar
  323. {
  324. /**
  325. * Important
  326. */
  327. var $variable = "acme";
  328. }
  329. ',
  330. '<?php
  331. class Bar
  332. {
  333. /** Important */
  334. var $variable = "acme";
  335. }
  336. ',
  337. ];
  338. yield 'It can update doc blocks of static that do not declare visibility' => [
  339. '<?php
  340. class Bar
  341. {
  342. /**
  343. * Important
  344. */
  345. static $variable = "acme";
  346. }
  347. ',
  348. '<?php
  349. class Bar
  350. {
  351. /** Important */
  352. static $variable = "acme";
  353. }
  354. ',
  355. ];
  356. yield 'It does not change method doc blocks if configured to do so' => [
  357. '<?php
  358. class Foo
  359. {
  360. /** @return mixed */
  361. public function bar() {}
  362. /**
  363. * @return void
  364. */
  365. public function baz() {}
  366. }',
  367. null,
  368. [
  369. 'method' => null,
  370. ],
  371. ];
  372. yield 'It does not change property doc blocks if configured to do so' => [
  373. '<?php
  374. class Foo
  375. {
  376. /**
  377. * @var int
  378. */
  379. public $foo;
  380. /** @var mixed */
  381. public $bar;
  382. }',
  383. null,
  384. [
  385. 'property' => null,
  386. ],
  387. ];
  388. yield 'It does not change const doc blocks if configured to do so' => [
  389. '<?php
  390. class Foo
  391. {
  392. /**
  393. * @var int
  394. */
  395. public const FOO = 1;
  396. /** @var mixed */
  397. public const BAR = null;
  398. }',
  399. null,
  400. [
  401. 'const' => null,
  402. ],
  403. ];
  404. yield 'It can handle constants with visibility, does not crash on trait imports' => [
  405. '<?php
  406. trait Bar
  407. {}
  408. class Foo
  409. {
  410. /** whatever */
  411. use Bar;
  412. /**
  413. *
  414. */
  415. public const FOO = "foobar";
  416. /** */
  417. private $foo;
  418. }',
  419. '<?php
  420. trait Bar
  421. {}
  422. class Foo
  423. {
  424. /** whatever */
  425. use Bar;
  426. /** */
  427. public const FOO = "foobar";
  428. /**
  429. *
  430. */
  431. private $foo;
  432. }',
  433. [
  434. 'property' => 'single',
  435. ],
  436. ];
  437. yield 'It can handle properties with type declaration' => [
  438. '<?php
  439. class Foo
  440. {
  441. /** */
  442. private ?string $foo;
  443. }',
  444. '<?php
  445. class Foo
  446. {
  447. /**
  448. *
  449. */
  450. private ?string $foo;
  451. }',
  452. [
  453. 'property' => 'single',
  454. ],
  455. ];
  456. yield 'It can handle properties with array type declaration' => [
  457. '<?php
  458. class Foo
  459. {
  460. /** @var string[] */
  461. private array $foo;
  462. }',
  463. '<?php
  464. class Foo
  465. {
  466. /**
  467. * @var string[]
  468. */
  469. private array $foo;
  470. }',
  471. [
  472. 'property' => 'single',
  473. ],
  474. ];
  475. }
  476. /**
  477. * @dataProvider provideFix80Cases
  478. *
  479. * @requires PHP 8.0
  480. *
  481. * @param array<string, mixed> $config
  482. */
  483. public function testFix80(string $expected, string $input = null, array $config = []): void
  484. {
  485. $this->fixer->configure($config);
  486. $this->doTest($expected, $input);
  487. }
  488. public static function provideFix80Cases(): iterable
  489. {
  490. yield 'It detects attributes between docblock and token' => [
  491. '<?php
  492. class Foo
  493. {
  494. /** @var string[] */
  495. #[Attribute1]
  496. private array $foo1;
  497. /** @var string[] */
  498. #[Attribute1]
  499. #[Attribute2]
  500. private array $foo2;
  501. /** @var string[] */
  502. #[Attribute1, Attribute2]
  503. public array $foo3;
  504. }',
  505. '<?php
  506. class Foo
  507. {
  508. /**
  509. * @var string[]
  510. */
  511. #[Attribute1]
  512. private array $foo1;
  513. /**
  514. * @var string[]
  515. */
  516. #[Attribute1]
  517. #[Attribute2]
  518. private array $foo2;
  519. /**
  520. * @var string[]
  521. */
  522. #[Attribute1, Attribute2]
  523. public array $foo3;
  524. }',
  525. [
  526. 'property' => 'single',
  527. ],
  528. ];
  529. yield 'It handles class constants correctly' => [
  530. '<?php
  531. class Foo
  532. {
  533. /**
  534. * 0
  535. */
  536. #[Attribute1]
  537. const B0 = "0";
  538. /**
  539. * 1
  540. */
  541. #[Attribute1]
  542. #[Attribute2]
  543. public const B1 = "1";
  544. /**
  545. * 2
  546. */
  547. #[Attribute1, Attribute2]
  548. public const B2 = "2";
  549. }
  550. ',
  551. '<?php
  552. class Foo
  553. {
  554. /** 0 */
  555. #[Attribute1]
  556. const B0 = "0";
  557. /** 1 */
  558. #[Attribute1]
  559. #[Attribute2]
  560. public const B1 = "1";
  561. /** 2 */
  562. #[Attribute1, Attribute2]
  563. public const B2 = "2";
  564. }
  565. ',
  566. ];
  567. yield 'It handles class functions correctly' => [
  568. '<?php
  569. class Foo
  570. {
  571. /**
  572. * @return void
  573. */
  574. #[Attribute1]
  575. public function hello1() {}
  576. /**
  577. * @return void
  578. */
  579. #[Attribute1]
  580. #[Attribute2]
  581. public function hello2() {}
  582. /**
  583. * @return void
  584. */
  585. #[Attribute1, Attribute2]
  586. public function hello3() {}
  587. }
  588. ',
  589. '<?php
  590. class Foo
  591. {
  592. /** @return void */
  593. #[Attribute1]
  594. public function hello1() {}
  595. /** @return void */
  596. #[Attribute1]
  597. #[Attribute2]
  598. public function hello2() {}
  599. /** @return void */
  600. #[Attribute1, Attribute2]
  601. public function hello3() {}
  602. }
  603. ',
  604. ];
  605. }
  606. /**
  607. * @dataProvider provideFix81Cases
  608. *
  609. * @requires PHP 8.1
  610. *
  611. * @param array<string, mixed> $config
  612. */
  613. public function testFix81(string $expected, string $input = null, array $config = []): void
  614. {
  615. $this->fixer->configure($config);
  616. $this->doTest($expected, $input);
  617. }
  618. public static function provideFix81Cases(): iterable
  619. {
  620. yield 'It handles readonly properties correctly' => [
  621. '<?php
  622. class Foo
  623. {
  624. /** @var string[] */
  625. private readonly array $foo1;
  626. /** @var string[] */
  627. readonly private array $foo2;
  628. /** @var string[] */
  629. readonly array $foo3;
  630. }',
  631. '<?php
  632. class Foo
  633. {
  634. /**
  635. * @var string[]
  636. */
  637. private readonly array $foo1;
  638. /**
  639. * @var string[]
  640. */
  641. readonly private array $foo2;
  642. /**
  643. * @var string[]
  644. */
  645. readonly array $foo3;
  646. }',
  647. [
  648. 'property' => 'single',
  649. ],
  650. ];
  651. yield 'It handles class constant correctly' => [
  652. '<?php
  653. class Foo
  654. {
  655. /**
  656. * 0
  657. */
  658. const B0 = "0";
  659. /**
  660. * 1
  661. */
  662. final public const B1 = "1";
  663. /**
  664. * 2
  665. */
  666. public final const B2 = "2";
  667. /**
  668. * 3
  669. */
  670. final const B3 = "3";
  671. }
  672. ',
  673. '<?php
  674. class Foo
  675. {
  676. /** 0 */
  677. const B0 = "0";
  678. /** 1 */
  679. final public const B1 = "1";
  680. /** 2 */
  681. public final const B2 = "2";
  682. /** 3 */
  683. final const B3 = "3";
  684. }
  685. ',
  686. ];
  687. yield 'It handles enum functions correctly' => [
  688. '<?php
  689. enum Foo
  690. {
  691. /**
  692. * @return void
  693. */
  694. public function hello() {}
  695. }
  696. ',
  697. '<?php
  698. enum Foo
  699. {
  700. /** @return void */
  701. public function hello() {}
  702. }
  703. ',
  704. ];
  705. yield 'It handles enum function with attributes correctly' => [
  706. '<?php
  707. enum Foo
  708. {
  709. /**
  710. * @return void
  711. */
  712. #[Attribute1]
  713. public function hello1() {}
  714. /**
  715. * @return void
  716. */
  717. #[Attribute1]
  718. #[Attribute2]
  719. public function hello2() {}
  720. /**
  721. * @return void
  722. */
  723. #[Attribute1, Attribute2]
  724. public function hello3() {}
  725. }
  726. ',
  727. '<?php
  728. enum Foo
  729. {
  730. /** @return void */
  731. #[Attribute1]
  732. public function hello1() {}
  733. /** @return void */
  734. #[Attribute1]
  735. #[Attribute2]
  736. public function hello2() {}
  737. /** @return void */
  738. #[Attribute1, Attribute2]
  739. public function hello3() {}
  740. }
  741. ',
  742. ];
  743. }
  744. /**
  745. * @dataProvider provideFix82Cases
  746. *
  747. * @requires PHP 8.2
  748. */
  749. public function testFix82(string $expected, ?string $input = null): void
  750. {
  751. $this->doTest($expected, $input);
  752. }
  753. public static function provideFix82Cases(): iterable
  754. {
  755. yield 'constant in trait' => [
  756. <<<'PHP'
  757. <?php
  758. trait Foo {
  759. /**
  760. * @var string
  761. */
  762. const Foo = 'foo';
  763. }
  764. PHP,
  765. <<<'PHP'
  766. <?php
  767. trait Foo {
  768. /** @var string */
  769. const Foo = 'foo';
  770. }
  771. PHP,
  772. ];
  773. }
  774. }