PhpdocVarWithoutNameFixerTest.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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(): array
  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. ];
  465. }
  466. /**
  467. * @dataProvider provideFix81Cases
  468. *
  469. * @requires PHP 8.1
  470. */
  471. public function testFix81(string $expected, ?string $input = null): void
  472. {
  473. $this->doTest($expected, $input);
  474. }
  475. public static function provideFix81Cases(): iterable
  476. {
  477. yield 'readonly' => [
  478. '<?php
  479. class Foo
  480. {
  481. /** @var Foo */
  482. public $bar1;
  483. /** @var Foo */
  484. public readonly int $bar2;
  485. /** @var Foo */
  486. readonly public int $bar3;
  487. /** @var Foo */
  488. readonly int $bar4;
  489. }',
  490. '<?php
  491. class Foo
  492. {
  493. /** @var Foo $bar1 */
  494. public $bar1;
  495. /** @var Foo $bar2 */
  496. public readonly int $bar2;
  497. /** @var Foo $bar3 */
  498. readonly public int $bar3;
  499. /** @var Foo $bar4 */
  500. readonly int $bar4;
  501. }',
  502. ];
  503. yield 'final public const are not handled by this fixer' => [
  504. '<?php
  505. class A
  506. {
  507. /**
  508. * @var array<string, true> SKIPPED_TYPES
  509. */
  510. final public const SKIPPED_TYPES = ["a" => true];
  511. }
  512. ',
  513. ];
  514. }
  515. }