PhpdocVarWithoutNameFixerTest.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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 Graham Campbell <hello@gjcampbell.co.uk>
  16. *
  17. * @internal
  18. *
  19. * @covers \PhpCsFixer\Fixer\Phpdoc\PhpdocVarWithoutNameFixer
  20. */
  21. final class PhpdocVarWithoutNameFixerTest extends AbstractFixerTestCase
  22. {
  23. /**
  24. * @dataProvider provideFixVarCases
  25. */
  26. public function testFixVar(string $expected, ?string $input = null): void
  27. {
  28. $this->doTest($expected, $input);
  29. }
  30. /**
  31. * @dataProvider provideFixVarCases
  32. */
  33. public function testFixType(string $expected, ?string $input = null): void
  34. {
  35. $expected = str_replace('@var', '@type', $expected);
  36. if (null !== $input) {
  37. $input = str_replace('@var', '@type', $input);
  38. }
  39. $this->doTest($expected, $input);
  40. }
  41. public static function provideFixVarCases(): iterable
  42. {
  43. return [
  44. 'testFixVar' => [
  45. <<<'EOF'
  46. <?php
  47. class Foo
  48. {
  49. /**
  50. * @var string Hello!
  51. */
  52. public $foo;
  53. }
  54. EOF
  55. ,
  56. <<<'EOF'
  57. <?php
  58. class Foo
  59. {
  60. /**
  61. * @var string $foo Hello!
  62. */
  63. public $foo;
  64. }
  65. EOF
  66. ,
  67. ],
  68. 'testFixType' => [
  69. <<<'EOF'
  70. <?php
  71. class Foo
  72. {
  73. /**
  74. * @var int|null
  75. */
  76. public $bar;
  77. }
  78. EOF
  79. ,
  80. <<<'EOF'
  81. <?php
  82. class Foo
  83. {
  84. /**
  85. * @var int|null $bar
  86. */
  87. public $bar;
  88. }
  89. EOF
  90. ,
  91. ],
  92. 'testDoNothing' => [
  93. <<<'EOF'
  94. <?php
  95. class Foo
  96. {
  97. /**
  98. * @var Foo\Bar This is a variable.
  99. */
  100. public $bar;
  101. }
  102. EOF
  103. ],
  104. 'testFixVarWithNestedKeys' => [
  105. <<<'EOF'
  106. <?php
  107. class Foo
  108. {
  109. /**
  110. * @var array {
  111. * @var bool $required Whether this element is required
  112. * @var string $label The display name for this element
  113. * }
  114. */
  115. public $options;
  116. }
  117. EOF
  118. ,
  119. <<<'EOF'
  120. <?php
  121. class Foo
  122. {
  123. /**
  124. * @var array $options {
  125. * @var bool $required Whether this element is required
  126. * @var string $label The display name for this element
  127. * }
  128. */
  129. public $options;
  130. }
  131. EOF
  132. ],
  133. 'testSingleLine' => [
  134. <<<'EOF'
  135. <?php
  136. class Foo
  137. {
  138. /** @var Foo\Bar */
  139. public $bar;
  140. }
  141. EOF
  142. ,
  143. <<<'EOF'
  144. <?php
  145. class Foo
  146. {
  147. /** @var Foo\Bar $bar */
  148. public $bar;
  149. }
  150. EOF
  151. ,
  152. ],
  153. 'testSingleLineProtected' => [
  154. <<<'EOF'
  155. <?php
  156. class Foo
  157. {
  158. /** @var Foo\Bar */
  159. protected $bar;
  160. }
  161. EOF
  162. ,
  163. <<<'EOF'
  164. <?php
  165. class Foo
  166. {
  167. /** @var Foo\Bar $bar */
  168. protected $bar;
  169. }
  170. EOF
  171. ,
  172. ],
  173. 'testSingleLinePrivate' => [
  174. <<<'EOF'
  175. <?php
  176. class Foo
  177. {
  178. /** @var Foo\Bar */
  179. private $bar;
  180. }
  181. EOF
  182. ,
  183. <<<'EOF'
  184. <?php
  185. class Foo
  186. {
  187. /** @var Foo\Bar $bar */
  188. private $bar;
  189. }
  190. EOF
  191. ,
  192. ],
  193. 'testSingleLineVar' => [
  194. <<<'EOF'
  195. <?php
  196. class Foo
  197. {
  198. /** @var Foo\Bar */
  199. var $bar;
  200. }
  201. EOF
  202. ,
  203. <<<'EOF'
  204. <?php
  205. class Foo
  206. {
  207. /** @var Foo\Bar $bar */
  208. var $bar;
  209. }
  210. EOF
  211. ,
  212. ],
  213. 'testSingleLineStatic' => [
  214. <<<'EOF'
  215. <?php
  216. class Foo
  217. {
  218. /** @var Foo\Bar */
  219. static public $bar;
  220. }
  221. EOF
  222. ,
  223. <<<'EOF'
  224. <?php
  225. class Foo
  226. {
  227. /** @var Foo\Bar $bar */
  228. static public $bar;
  229. }
  230. EOF
  231. ,
  232. ],
  233. 'testSingleLineNoSpace' => [
  234. <<<'EOF'
  235. <?php
  236. class Foo
  237. {
  238. /** @var Foo\Bar*/
  239. public $bar;
  240. }
  241. EOF
  242. ,
  243. <<<'EOF'
  244. <?php
  245. class Foo
  246. {
  247. /** @var Foo\Bar $bar*/
  248. public $bar;
  249. }
  250. EOF
  251. ,
  252. ],
  253. 'testInlineDoc' => [
  254. <<<'EOF'
  255. <?php
  256. class Foo
  257. {
  258. /**
  259. * Initializes this class with the given options.
  260. *
  261. * @param array $options {
  262. * @var bool $required Whether this element is required
  263. * @var string $label The display name for this element
  264. * }
  265. */
  266. public function init($options)
  267. {
  268. // Do something
  269. }
  270. }
  271. EOF
  272. ,
  273. ],
  274. 'testSingleLineNoProperty' => [
  275. <<<'EOF'
  276. <?php
  277. /** @var Foo\Bar $bar */
  278. $bar;
  279. EOF
  280. ],
  281. 'testMultiLineNoProperty' => [
  282. <<<'EOF'
  283. <?php
  284. /**
  285. * @var Foo\Bar $bar
  286. */
  287. $bar;
  288. EOF
  289. ],
  290. 'testVeryNestedInlineDoc' => [
  291. <<<'EOF'
  292. <?php
  293. class Foo
  294. {
  295. /**
  296. * @var array {
  297. * @var array $secondLevelOne {
  298. * {@internal This should not break}
  299. * @var int $thirdLevel
  300. * }
  301. * @var array $secondLevelTwo {
  302. * @var array $thirdLevel {
  303. * @var string $fourthLevel
  304. * }
  305. * @var int $moreThirdLevel
  306. * }
  307. * @var int $secondLevelThree
  308. * }
  309. */
  310. public $nestedFoo;
  311. }
  312. EOF
  313. ,
  314. <<<'EOF'
  315. <?php
  316. class Foo
  317. {
  318. /**
  319. * @var array $nestedFoo {
  320. * @var array $secondLevelOne {
  321. * {@internal This should not break}
  322. * @var int $thirdLevel
  323. * }
  324. * @var array $secondLevelTwo {
  325. * @var array $thirdLevel {
  326. * @var string $fourthLevel
  327. * }
  328. * @var int $moreThirdLevel
  329. * }
  330. * @var int $secondLevelThree
  331. * }
  332. */
  333. public $nestedFoo;
  334. }
  335. EOF
  336. ],
  337. [
  338. '<?php
  339. class Foo
  340. {
  341. /**
  342. * @no_candidate string Hello!
  343. */
  344. public $foo;
  345. }
  346. ',
  347. ],
  348. [
  349. '<?php
  350. class Foo{}
  351. /** */',
  352. ],
  353. 'anonymousClass' => [
  354. <<<'EOF'
  355. <?php
  356. class Anon
  357. {
  358. public function getNewAnon()
  359. {
  360. return new class()
  361. {
  362. /**
  363. * @var string
  364. */
  365. public $stringVar;
  366. public function getNewAnon()
  367. {
  368. return new class()
  369. {
  370. /**
  371. * @var string
  372. */
  373. public $stringVar;
  374. };
  375. }
  376. };
  377. }
  378. }
  379. EOF
  380. ,
  381. <<<'EOF'
  382. <?php
  383. class Anon
  384. {
  385. public function getNewAnon()
  386. {
  387. return new class()
  388. {
  389. /**
  390. * @var $stringVar string
  391. */
  392. public $stringVar;
  393. public function getNewAnon()
  394. {
  395. return new class()
  396. {
  397. /**
  398. * @var $stringVar string
  399. */
  400. public $stringVar;
  401. };
  402. }
  403. };
  404. }
  405. }
  406. EOF
  407. ,
  408. ],
  409. [
  410. '<?php
  411. /**
  412. * Header
  413. */
  414. class A {} // for the candidate check
  415. /**
  416. * @var ClassLoader $loader
  417. */
  418. $loader = require __DIR__.\'/../vendor/autoload.php\';
  419. /**
  420. * @var \Foo\Bar $bar
  421. */
  422. $bar->doSomething(1);
  423. /**
  424. * @var $bar \Foo\Bar
  425. */
  426. $bar->doSomething(2);
  427. /**
  428. * @var User $bar
  429. */
  430. ($bar = tmp())->doSomething(3);
  431. /**
  432. * @var User $bar
  433. */
  434. list($bar) = a();
  435. ',
  436. ],
  437. 'const are not handled by this fixer' => [
  438. '<?php
  439. class A
  440. {
  441. /**
  442. * @var array<string, true> SKIPPED_TYPES
  443. */
  444. private const SKIPPED_TYPES = ["a" => true];
  445. }
  446. ',
  447. ],
  448. 'trait' => [
  449. '<?php
  450. trait StaticExample {
  451. /**
  452. * @var string Hello!
  453. */
  454. public static $static = "foo";
  455. }',
  456. '<?php
  457. trait StaticExample {
  458. /**
  459. * @var string $static Hello!
  460. */
  461. public static $static = "foo";
  462. }',
  463. ],
  464. 'complex type with union containing callable that has `$this` in signature' => [
  465. <<<'EOF'
  466. <?php
  467. class Foo
  468. {
  469. /**
  470. * @var array<string, string|array{ string|\Closure(mixed, string, $this): int|float }>|false Hello!
  471. */
  472. public $foo;
  473. /** @var int Hello! */
  474. public $foo2;
  475. /** @var int Hello! */
  476. public $foo3;
  477. /** @var int Hello! */
  478. public $foo4;
  479. }
  480. EOF
  481. ,
  482. <<<'EOF'
  483. <?php
  484. class Foo
  485. {
  486. /**
  487. * @var array<string, string|array{ string|\Closure(mixed, string, $this): int|float }>|false $foo Hello!
  488. */
  489. public $foo;
  490. /** @var int $thi Hello! */
  491. public $foo2;
  492. /** @var int $thiss Hello! */
  493. public $foo3;
  494. /** @var int $this2 Hello! */
  495. public $foo4;
  496. }
  497. EOF
  498. ,
  499. ],
  500. 'testFixMultibyteVariableName' => [
  501. <<<'EOF'
  502. <?php
  503. class Foo
  504. {
  505. /** @var int Hello! */
  506. public $foo;
  507. /** @var ๐Ÿš€ ๐Ÿš€ */
  508. public $foo2;
  509. }
  510. EOF
  511. ,
  512. <<<'EOF'
  513. <?php
  514. class Foo
  515. {
  516. /** @var int $my๐Ÿš€ Hello! */
  517. public $foo;
  518. /** @var ๐Ÿš€ $my ๐Ÿš€ */
  519. public $foo2;
  520. }
  521. EOF
  522. ,
  523. ],
  524. ];
  525. }
  526. /**
  527. * @dataProvider provideFix81Cases
  528. *
  529. * @requires PHP 8.1
  530. */
  531. public function testFix81(string $expected, ?string $input = null): void
  532. {
  533. $this->doTest($expected, $input);
  534. }
  535. public static function provideFix81Cases(): iterable
  536. {
  537. yield 'readonly' => [
  538. '<?php
  539. class Foo
  540. {
  541. /** @var Foo */
  542. public $bar1;
  543. /** @var Foo */
  544. public readonly int $bar2;
  545. /** @var Foo */
  546. readonly public int $bar3;
  547. /** @var Foo */
  548. readonly int $bar4;
  549. }',
  550. '<?php
  551. class Foo
  552. {
  553. /** @var Foo $bar1 */
  554. public $bar1;
  555. /** @var Foo $bar2 */
  556. public readonly int $bar2;
  557. /** @var Foo $bar3 */
  558. readonly public int $bar3;
  559. /** @var Foo $bar4 */
  560. readonly int $bar4;
  561. }',
  562. ];
  563. yield 'final public const are not handled by this fixer' => [
  564. '<?php
  565. class A
  566. {
  567. /**
  568. * @var array<string, true> SKIPPED_TYPES
  569. */
  570. final public const SKIPPED_TYPES = ["a" => true];
  571. }
  572. ',
  573. ];
  574. }
  575. }