PhpdocToCommentFixerTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  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 Ceeram <ceeram@cakephp.org>
  16. *
  17. * @internal
  18. *
  19. * @covers \PhpCsFixer\Fixer\Phpdoc\PhpdocToCommentFixer
  20. *
  21. * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\Phpdoc\PhpdocToCommentFixer>
  22. *
  23. * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\Phpdoc\PhpdocToCommentFixer
  24. */
  25. final class PhpdocToCommentFixerTest extends AbstractFixerTestCase
  26. {
  27. /**
  28. * @param _AutogeneratedInputConfiguration $config
  29. *
  30. * @dataProvider provideFixCases
  31. */
  32. public function testFix(string $expected, ?string $input = null, array $config = []): void
  33. {
  34. $this->fixer->configure($config);
  35. $this->doTest($expected, $input);
  36. }
  37. public static function provideFixCases(): iterable
  38. {
  39. yield [
  40. '<?php
  41. /**
  42. * Do not convert this
  43. */
  44. namespace Docs;
  45. /**
  46. * Do not convert this
  47. */
  48. class DocBlocks
  49. {
  50. /**
  51. * Do not convert this
  52. */
  53. use TestTrait;
  54. /**
  55. * Do not convert this
  56. */
  57. const STRUCTURAL = true;
  58. /**
  59. * Do not convert this
  60. */
  61. protected $indent = false;
  62. /**
  63. * Do not convert this
  64. */
  65. var $oldPublicStyle;
  66. /**
  67. * Do not convert this
  68. */
  69. public function test() {}
  70. /**
  71. * Do not convert this
  72. */
  73. private function testPrivate() {}
  74. /**
  75. * Do not convert this
  76. */
  77. function testNoVisibility() {}
  78. }',
  79. ];
  80. yield [
  81. '<?php namespace Docs;
  82. /**
  83. * Do not convert this
  84. */
  85. /**
  86. * Do not convert this
  87. */
  88. class DocBlocks{}
  89. ',
  90. ];
  91. yield [
  92. '<?php
  93. /**
  94. * Do not convert this
  95. */
  96. namespace Foo;
  97. ',
  98. ];
  99. yield [
  100. '<?php
  101. $first = true;// needed because by default first docblock is never fixed.
  102. /**
  103. * Do not convert this
  104. */
  105. abstract class DocBlocks
  106. {
  107. /**
  108. * Do not convert this
  109. */
  110. abstract public function test();
  111. }',
  112. ];
  113. yield [
  114. '<?php
  115. $first = true;// needed because by default first docblock is never fixed.
  116. /**
  117. * Do not convert this
  118. */
  119. interface DocBlocks
  120. {
  121. public function test();
  122. }',
  123. ];
  124. yield [
  125. '<?php
  126. namespace NS;
  127. /**
  128. * Do not
  129. */
  130. final class Foo
  131. {
  132. }',
  133. ];
  134. yield [
  135. '<?php
  136. $first = true;// needed because by default first docblock is never fixed.
  137. /**
  138. * Do not convert this
  139. */
  140. require "require.php";
  141. /**
  142. * Do not convert this
  143. */
  144. require_once "require_once.php";
  145. /**
  146. * Do not convert this
  147. */
  148. include "include.php";
  149. /**
  150. * Do not convert this
  151. */
  152. include_once "include_once.php";
  153. ',
  154. ];
  155. yield [
  156. '<?php
  157. $first = true;// needed because by default first docblock is never fixed.
  158. /**
  159. * Do not convert this
  160. *
  161. * @var int
  162. */
  163. $a = require "require.php";
  164. /**
  165. * Do not convert this
  166. *
  167. * @var int
  168. */
  169. $b = require_once "require_once.php";
  170. /**
  171. * Do not convert this
  172. *
  173. * @var int
  174. */
  175. $c = include "include.php";
  176. /**
  177. * Do not convert this
  178. *
  179. * @var int
  180. */
  181. $d = include_once "include_once.php";
  182. /**
  183. * @var Composer\Autoload\ClassLoader $loader
  184. */
  185. $loader = require_once __DIR__."/vendor/autoload.php";
  186. ',
  187. ];
  188. yield [
  189. '<?php
  190. $first = true;// needed because by default first docblock is never fixed.
  191. /**
  192. * @var ClassLoader $loader
  193. */
  194. $loader = require_once __DIR__."/../app/autoload.php";
  195. ',
  196. ];
  197. yield [
  198. '<?php
  199. $first = true;// needed because by default first docblock is never fixed.
  200. /**
  201. * Do not convert this
  202. *
  203. * @var Foo
  204. */
  205. $foo = createFoo();
  206. ',
  207. ];
  208. yield [
  209. '<?php
  210. $first = true;// needed because by default first docblock is never fixed.
  211. /**
  212. * Do not convert this
  213. *
  214. * @var bool $local
  215. */
  216. $local = true;
  217. ',
  218. ];
  219. yield [
  220. '<?php
  221. $first = true;// needed because by default first docblock is never fixed.
  222. /**
  223. * Comment
  224. */
  225. $local = true;
  226. ',
  227. ];
  228. yield [
  229. '<?php
  230. $first = true;// needed because by default first docblock is never fixed.
  231. /** @var \Sqlite3 $sqlite */
  232. foreach($connections as $sqlite) {
  233. $sqlite->open($path);
  234. }',
  235. ];
  236. yield [
  237. '<?php
  238. $first = true;// needed because by default first docblock is never fixed.
  239. /** @var \Sqlite3 $sqlite */
  240. foreach($connections as $key => $sqlite) {
  241. $sqlite->open($path);
  242. }',
  243. ];
  244. yield [
  245. '<?php
  246. $first = true;// needed because by default first docblock is never fixed.
  247. /** @var int $key */
  248. foreach($connections as $key => $sqlite) {
  249. $sqlite->open($path);
  250. }',
  251. ];
  252. yield [
  253. '<?php
  254. $first = true;// needed because by default first docblock is never fixed.
  255. /* This should not be a docblock */
  256. foreach($connections as $key => $sqlite) {
  257. $sqlite->open($path);
  258. }',
  259. '<?php
  260. $first = true;// needed because by default first docblock is never fixed.
  261. /** This should not be a docblock */
  262. foreach($connections as $key => $sqlite) {
  263. $sqlite->open($path);
  264. }',
  265. ];
  266. yield [
  267. '<?php
  268. $first = true;// needed because by default first docblock is never fixed.
  269. /* there should be no docblock here */
  270. $sqlite1->open($path);
  271. ',
  272. '<?php
  273. $first = true;// needed because by default first docblock is never fixed.
  274. /** there should be no docblock here */
  275. $sqlite1->open($path);
  276. ',
  277. ];
  278. yield [
  279. '<?php
  280. $first = true;// needed because by default first docblock is never fixed.
  281. /* there should be no docblock here */
  282. $i++;
  283. ',
  284. '<?php
  285. $first = true;// needed because by default first docblock is never fixed.
  286. /** there should be no docblock here */
  287. $i++;
  288. ',
  289. ];
  290. yield [
  291. '<?php
  292. $first = true;// needed because by default first docblock is never fixed.
  293. /** @var int $index */
  294. $index = $a[\'number\'];
  295. ',
  296. ];
  297. yield [
  298. '<?php
  299. $first = true;// needed because by default first docblock is never fixed.
  300. /** @var string $two */
  301. list($one, $two) = explode("," , $csvLines);
  302. ',
  303. ];
  304. yield [
  305. '<?php
  306. $first = true;// needed because by default first docblock is never fixed.
  307. /* This should be a comment */
  308. list($one, $two) = explode("," , $csvLines);
  309. ',
  310. '<?php
  311. $first = true;// needed because by default first docblock is never fixed.
  312. /** This should be a comment */
  313. list($one, $two) = explode("," , $csvLines);
  314. ',
  315. ];
  316. yield [
  317. '<?php
  318. $first = true;// needed because by default first docblock is never fixed.
  319. /** @var int $index */
  320. foreach ($foo->getPairs($c->bar(), $bar) as $index => list($a, $b)) {
  321. // Do something with $index, $a and $b
  322. }
  323. /** @var \Closure $value */
  324. if (!$value = $this->getValue()) {
  325. return false;
  326. }
  327. /** @var string $name */
  328. switch ($name = $this->getName()) {
  329. case "John":
  330. return false;
  331. case "Jane":
  332. return true;
  333. }
  334. /** @var string $content */
  335. while ($content = $this->getContent()) {
  336. $name .= $content;
  337. }
  338. /** @var int $size */
  339. for($i = 0, $size = count($people); $i < $size; ++$i) {
  340. $people[$i][\'salt\'] = mt_rand(000000, 999999);
  341. }',
  342. ];
  343. yield [
  344. '<?php
  345. $first = true;// needed because by default first docblock is never fixed.
  346. /* @var int $wrong */
  347. foreach ($foo->getPairs($c->bar(), $bar) as $index => list($a, $b)) {
  348. // Do something with $index, $a and $b
  349. }
  350. /* @var \Closure $notValue */
  351. if (!$value = $this->getValue()) {
  352. return false;
  353. }
  354. /* @var string $notName */
  355. switch ($name = $this->getName()) {
  356. case "John":
  357. return false;
  358. case "Jane":
  359. return true;
  360. }
  361. /* @var string $notContent */
  362. while ($content = $this->getContent()) {
  363. $name .= $content;
  364. }
  365. /* @var int $notSize */
  366. for($i = 0, $size = count($people); $i < $size; ++$i) {
  367. $people[$i][\'salt\'] = mt_rand(000000, 999999);
  368. }',
  369. '<?php
  370. $first = true;// needed because by default first docblock is never fixed.
  371. /** @var int $wrong */
  372. foreach ($foo->getPairs($c->bar(), $bar) as $index => list($a, $b)) {
  373. // Do something with $index, $a and $b
  374. }
  375. /** @var \Closure $notValue */
  376. if (!$value = $this->getValue()) {
  377. return false;
  378. }
  379. /** @var string $notName */
  380. switch ($name = $this->getName()) {
  381. case "John":
  382. return false;
  383. case "Jane":
  384. return true;
  385. }
  386. /** @var string $notContent */
  387. while ($content = $this->getContent()) {
  388. $name .= $content;
  389. }
  390. /** @var int $notSize */
  391. for($i = 0, $size = count($people); $i < $size; ++$i) {
  392. $people[$i][\'salt\'] = mt_rand(000000, 999999);
  393. }',
  394. ];
  395. yield [
  396. '<?php
  397. /* This should be a comment */
  398. ',
  399. '<?php
  400. /** This should be a comment */
  401. ',
  402. ];
  403. yield [
  404. '<?php
  405. /**
  406. * This is a page level docblock should stay untouched
  407. */
  408. echo "Some string";
  409. ',
  410. ];
  411. yield [
  412. '<?php
  413. $first = true;// needed because by default first docblock is never fixed.
  414. /** @var \NumberFormatter $formatter */
  415. static $formatter;
  416. ',
  417. ];
  418. yield [
  419. '<?php
  420. $first = true;// needed because by default first docblock is never fixed.
  421. function getNumberFormatter()
  422. {
  423. /** @var \NumberFormatter $formatter */
  424. static $formatter;
  425. }
  426. ',
  427. ];
  428. yield [
  429. '<?php
  430. class A
  431. {
  432. public function b()
  433. {
  434. /** @var int $c */
  435. print($c = 0);
  436. }
  437. }
  438. ',
  439. ];
  440. yield ['<?php
  441. /** header */
  442. echo 123;
  443. /** @var int $bar1 */
  444. (print($bar1 = 0));
  445. ',
  446. ];
  447. yield [
  448. '<?php
  449. /** header */
  450. echo 123;
  451. /** @var ClassLoader $loader */
  452. $loader = require __DIR__.\'/../vendor/autoload.php\';
  453. ',
  454. ];
  455. yield [
  456. '<?php
  457. $first = true;// needed because by default first docblock is never fixed.
  458. /** @todo Do not convert this */
  459. foreach($connections as $key => $sqlite) {
  460. $sqlite->open($path);
  461. }',
  462. null,
  463. ['ignored_tags' => ['todo']],
  464. ];
  465. yield [
  466. '<?php
  467. $first = true;// needed because by default first docblock is never fixed.
  468. /* Convert this */
  469. foreach($connections as $key => $sqlite) {
  470. $sqlite->open($path);
  471. }
  472. /** @todo Do not convert this */
  473. foreach($connections as $key => $sqlite) {
  474. $sqlite->open($path);
  475. }',
  476. '<?php
  477. $first = true;// needed because by default first docblock is never fixed.
  478. /** Convert this */
  479. foreach($connections as $key => $sqlite) {
  480. $sqlite->open($path);
  481. }
  482. /** @todo Do not convert this */
  483. foreach($connections as $key => $sqlite) {
  484. $sqlite->open($path);
  485. }',
  486. ['ignored_tags' => ['todo']],
  487. ];
  488. yield [
  489. '<?php
  490. $first = true;// needed because by default first docblock is never fixed.
  491. /* Convert this */
  492. foreach($connections as $key => $sqlite) {
  493. $sqlite->open($path);
  494. }
  495. /** @fix-me Do not convert this */
  496. foreach($connections as $key => $sqlite) {
  497. $sqlite->open($path);
  498. }',
  499. '<?php
  500. $first = true;// needed because by default first docblock is never fixed.
  501. /** Convert this */
  502. foreach($connections as $key => $sqlite) {
  503. $sqlite->open($path);
  504. }
  505. /** @fix-me Do not convert this */
  506. foreach($connections as $key => $sqlite) {
  507. $sqlite->open($path);
  508. }',
  509. ['ignored_tags' => ['fix-me']],
  510. ];
  511. yield [
  512. '<?php
  513. $first = true;// needed because by default first docblock is never fixed.
  514. /* @todoNot Convert this */
  515. foreach($connections as $key => $sqlite) {
  516. $sqlite->open($path);
  517. }
  518. /** @TODO Do not convert this */
  519. foreach($connections as $key => $sqlite) {
  520. $sqlite->open($path);
  521. }',
  522. '<?php
  523. $first = true;// needed because by default first docblock is never fixed.
  524. /** @todoNot Convert this */
  525. foreach($connections as $key => $sqlite) {
  526. $sqlite->open($path);
  527. }
  528. /** @TODO Do not convert this */
  529. foreach($connections as $key => $sqlite) {
  530. $sqlite->open($path);
  531. }',
  532. ['ignored_tags' => ['todo']],
  533. ];
  534. yield [
  535. '<?php
  536. $first = true;// needed because by default first docblock is never fixed.
  537. /* Convert this */
  538. foreach($connections as $key => $sqlite) {
  539. $sqlite->open($path);
  540. }
  541. /**
  542. * @deprecated This tag is not in the list but the next one is
  543. * @todo This should be a PHPDoc as the tag is on "ignored_tags" list
  544. */
  545. foreach($connections as $key => $sqlite) {
  546. $sqlite->open($path);
  547. }',
  548. '<?php
  549. $first = true;// needed because by default first docblock is never fixed.
  550. /** Convert this */
  551. foreach($connections as $key => $sqlite) {
  552. $sqlite->open($path);
  553. }
  554. /**
  555. * @deprecated This tag is not in the list but the next one is
  556. * @todo This should be a PHPDoc as the tag is on "ignored_tags" list
  557. */
  558. foreach($connections as $key => $sqlite) {
  559. $sqlite->open($path);
  560. }',
  561. ['ignored_tags' => ['todo']],
  562. ];
  563. yield 'do not convert before fn' => [
  564. '<?php // needed because by default first comment is never fixed
  565. /** @param int $x */
  566. fn ($x) => $x + 42;
  567. ',
  568. ];
  569. yield 'convert before return without option' => [
  570. '<?php
  571. function doSomething()
  572. {
  573. /* @var void */
  574. return;
  575. }
  576. ',
  577. '<?php
  578. function doSomething()
  579. {
  580. /** @var void */
  581. return;
  582. }
  583. ',
  584. ['allow_before_return_statement' => false],
  585. ];
  586. yield 'do not convert before return with option' => [
  587. '<?php
  588. function doSomething()
  589. {
  590. /** @var void */
  591. return;
  592. }
  593. ',
  594. null,
  595. ['allow_before_return_statement' => true],
  596. ];
  597. yield [
  598. '<?php
  599. $first = true;// needed because by default first docblock is never fixed.
  600. /**
  601. * Do not convert this
  602. */
  603. trait DocBlocks
  604. {
  605. public function test() {}
  606. }',
  607. ];
  608. yield [
  609. '<?php
  610. /** header */
  611. echo 123;
  612. /** @var User $bar3 */
  613. ($bar3 = tmp())->doSomething();
  614. /** @var Session $session */ # test
  615. $session = new Session();
  616. ',
  617. ];
  618. yield [
  619. '<?php
  620. $first = true;// needed because by default first docblock is never fixed.
  621. /** @var int $a */
  622. [$a] = $b;
  623. /* @var int $c */
  624. [$a] = $c;
  625. ',
  626. '<?php
  627. $first = true;// needed because by default first docblock is never fixed.
  628. /** @var int $a */
  629. [$a] = $b;
  630. /** @var int $c */
  631. [$a] = $c;
  632. ',
  633. ];
  634. yield [
  635. '<?php
  636. $first = true;// needed because by default first docblock is never fixed.
  637. /**
  638. * @var int $a
  639. */
  640. [$a] = $b;
  641. ',
  642. ];
  643. yield [
  644. '<?php
  645. class Foo {
  646. /**
  647. * Do not convert this
  648. */
  649. private int $foo;
  650. }',
  651. ];
  652. yield [
  653. '<?php
  654. class Foo {
  655. /**
  656. * Do not convert this
  657. */
  658. protected ?string $foo;
  659. }',
  660. ];
  661. yield [
  662. '<?php
  663. class Foo {
  664. /**
  665. * Do not convert this
  666. */
  667. public ? float $foo;
  668. }',
  669. ];
  670. yield [
  671. '<?php
  672. class Foo {
  673. /**
  674. * Do not convert this
  675. */
  676. var ? Foo\Bar $foo;
  677. }',
  678. ];
  679. yield [
  680. '<?php
  681. class Foo {
  682. /**
  683. * Do not convert this
  684. */
  685. var ? array $foo;
  686. }',
  687. ];
  688. }
  689. /**
  690. * @dataProvider provideFix80Cases
  691. *
  692. * @requires PHP 8.0
  693. */
  694. public function testFix80(string $expected, ?string $input = null): void
  695. {
  696. $this->doTest($expected, $input);
  697. }
  698. /**
  699. * @return iterable<array{0: string, 1?: string}>
  700. */
  701. public static function provideFix80Cases(): iterable
  702. {
  703. yield [
  704. '<?php
  705. /**
  706. * @Annotation
  707. */
  708. #[CustomAnnotationA]
  709. Class MyAnnotation3
  710. {
  711. /**
  712. * @Annotation
  713. */
  714. #[CustomAnnotationB]
  715. #[CustomAnnotationC]
  716. public function foo() {}
  717. /**
  718. * @Annotation
  719. */
  720. #[CustomAnnotationD]
  721. public $var;
  722. /*
  723. * end of class
  724. */
  725. }',
  726. '<?php
  727. /**
  728. * @Annotation
  729. */
  730. #[CustomAnnotationA]
  731. Class MyAnnotation3
  732. {
  733. /**
  734. * @Annotation
  735. */
  736. #[CustomAnnotationB]
  737. #[CustomAnnotationC]
  738. public function foo() {}
  739. /**
  740. * @Annotation
  741. */
  742. #[CustomAnnotationD]
  743. public $var;
  744. /**
  745. * end of class
  746. */
  747. }',
  748. ];
  749. yield [
  750. '<?php
  751. class Foo
  752. {
  753. public function __construct(
  754. /** @var string Do not convert this */
  755. public string $bar
  756. ) {
  757. }
  758. }
  759. ',
  760. ];
  761. }
  762. /**
  763. * @dataProvider provideFix81Cases
  764. *
  765. * @requires PHP 8.1
  766. */
  767. public function testFix81(string $expected): void
  768. {
  769. $this->doTest($expected);
  770. }
  771. /**
  772. * @return iterable<string, array{string}>
  773. */
  774. public static function provideFix81Cases(): iterable
  775. {
  776. yield 'enum' => [
  777. '<?php
  778. declare(strict_types=1);
  779. namespace PhpCsFixer\Tests\Tokenizer\Analyzer;
  780. /** Before enum */
  781. enum Foo {
  782. //
  783. }',
  784. ];
  785. yield 'phpDoc over enum case' => [
  786. '<?php
  787. enum Foo: int
  788. {
  789. /**
  790. * @deprecated do not convert this
  791. */
  792. case BAR = 1;
  793. }
  794. ',
  795. ];
  796. }
  797. }