PhpdocLineSpanFixerTest.php 16 KB

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