PhpdocToCommentFixerTest.php 17 KB

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