PhpdocToParamTypeFixerTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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\FunctionNotation;
  13. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  14. /**
  15. * @author Jan Gantzert <jan@familie-gantzert.de>
  16. *
  17. * @internal
  18. *
  19. * @covers \PhpCsFixer\Fixer\FunctionNotation\PhpdocToParamTypeFixer
  20. */
  21. final class PhpdocToParamTypeFixerTest extends AbstractFixerTestCase
  22. {
  23. /**
  24. * @dataProvider provideFixCases
  25. */
  26. public function testFix(string $expected, ?string $input = null, ?int $versionSpecificFix = null, array $config = null): void
  27. {
  28. if (
  29. null !== $input
  30. && (
  31. \PHP_VERSION_ID < 70000
  32. || (null !== $versionSpecificFix && \PHP_VERSION_ID < $versionSpecificFix)
  33. )
  34. ) {
  35. $expected = $input;
  36. $input = null;
  37. }
  38. if (null !== $config) {
  39. $this->fixer->configure($config);
  40. }
  41. $this->doTest($expected, $input);
  42. }
  43. public function provideFixCases(): array
  44. {
  45. return [
  46. 'typehint already defined' => [
  47. '<?php /** @param int $foo */ function foo(int $foo) {}',
  48. ],
  49. 'typehint already defined with wrong phpdoc typehint' => [
  50. '<?php /** @param string $foo */ function foo(int $foo) {}',
  51. ],
  52. 'no phpdoc param' => [
  53. '<?php function my_foo() {}',
  54. ],
  55. 'invalid - phpdoc param without variable' => [
  56. '<?php /** @param */ function my_foo($bar) {}',
  57. ],
  58. 'invalid - phpdoc param with non existing class' => [
  59. '<?php /** @param \9 */ function my_foo($bar) {}',
  60. ],
  61. 'invalid - phpdoc param with false class hint' => [
  62. '<?php /** @param $foo \\Foo\\\\Bar */ function my_foo($foo) {}',
  63. ],
  64. 'invalid - phpdoc param with false param order' => [
  65. '<?php /** @param $foo string */ function my_foo($foo) {}',
  66. ],
  67. 'invalid - phpdoc param with hint for next method' => [
  68. '<?php
  69. /**
  70. * @param string $bar
  71. */
  72. function my_foo() {}
  73. function my_foo2($bar) {}
  74. ',
  75. ],
  76. 'invalid - phpdoc param with keyword' => [
  77. '<?php
  78. /** @param Break $foo */ function foo_break($foo) {}
  79. /** @param __CLASS__ $foo */ function foo_class($foo) {}
  80. /** @param I\Want\To\Break\\\\Free $foo */ function foo_queen($foo) {}
  81. ',
  82. ],
  83. 'non-root class with single int param' => [
  84. '<?php /** @param int $bar */ function my_foo(int $bar) {}',
  85. '<?php /** @param int $bar */ function my_foo($bar) {}',
  86. ],
  87. 'non-root class with single float param' => [
  88. '<?php /** @param float $bar */ function my_foo(float $bar) {}',
  89. '<?php /** @param float $bar */ function my_foo($bar) {}',
  90. ],
  91. 'non-root class with multiple string params' => [
  92. '<?php
  93. /**
  94. * @param string $bar
  95. * @param string $baz
  96. */
  97. function my_foo(string $bar, string $baz) {}',
  98. '<?php
  99. /**
  100. * @param string $bar
  101. * @param string $baz
  102. */
  103. function my_foo($bar, $baz) {}',
  104. ],
  105. 'non-root class with not sorted multiple string params' => [
  106. '<?php
  107. /**
  108. * @param int $foo
  109. * @param string $bar
  110. */
  111. function my_foo(string $bar, int $foo) {}',
  112. '<?php
  113. /**
  114. * @param int $foo
  115. * @param string $bar
  116. */
  117. function my_foo($bar, $foo) {}',
  118. ],
  119. 'non-root class with not sorted multiple params and different types' => [
  120. '<?php
  121. /**
  122. * @param int $foo
  123. * @param string $bar
  124. * @param Baz $hey
  125. * @param float $tab
  126. * @param bool $baz
  127. */
  128. function my_foo(string $bar, int $foo, bool $baz, float $tab, Baz $hey) {}',
  129. '<?php
  130. /**
  131. * @param int $foo
  132. * @param string $bar
  133. * @param Baz $hey
  134. * @param float $tab
  135. * @param bool $baz
  136. */
  137. function my_foo($bar, $foo, $baz, $tab, $hey) {}',
  138. ],
  139. 'non-root class with massive string params' => [
  140. '<?php
  141. /**
  142. * @param string $bar
  143. * @param string $baz
  144. * @param string $tab
  145. * @param string $foo
  146. */
  147. function my_foo(string $bar, string $baz, string $tab, string $foo) {}',
  148. '<?php
  149. /**
  150. * @param string $bar
  151. * @param string $baz
  152. * @param string $tab
  153. * @param string $foo
  154. */
  155. function my_foo($bar, $baz, $tab, $foo) {}',
  156. ],
  157. 'non-root class with different types of params' => [
  158. '<?php
  159. /**
  160. * @param string $bar
  161. * @param int $baz
  162. * @param float $tab
  163. */
  164. function my_foo(string $bar, int $baz, float $tab) {}',
  165. '<?php
  166. /**
  167. * @param string $bar
  168. * @param int $baz
  169. * @param float $tab
  170. */
  171. function my_foo($bar, $baz, $tab) {}',
  172. ],
  173. 'non-root class with mixed type of param' => [
  174. '<?php
  175. /**
  176. * @param mixed $bar
  177. */
  178. function my_foo($bar) {}',
  179. ],
  180. 'non-root namespaced class' => [
  181. '<?php /** @param My\Bar $foo */ function my_foo(My\Bar $foo) {}',
  182. '<?php /** @param My\Bar $foo */ function my_foo($foo) {}',
  183. ],
  184. 'root class' => [
  185. '<?php /** @param \My\Bar $foo */ function my_foo(\My\Bar $foo) {}',
  186. '<?php /** @param \My\Bar $foo */ function my_foo($foo) {}',
  187. ],
  188. 'interface' => [
  189. '<?php interface Foo { /** @param Bar $bar */ function my_foo(Bar $bar); }',
  190. '<?php interface Foo { /** @param Bar $bar */ function my_foo($bar); }',
  191. ],
  192. 'iterable return on ^7.1' => [
  193. '<?php /** @param iterable $counter */ function my_foo(iterable $counter) {}',
  194. '<?php /** @param iterable $counter */ function my_foo($counter) {}',
  195. 70100,
  196. ],
  197. 'array native type' => [
  198. '<?php /** @param array $foo */ function my_foo(array $foo) {}',
  199. '<?php /** @param array $foo */ function my_foo($foo) {}',
  200. ],
  201. 'callable type' => [
  202. '<?php /** @param callable $foo */ function my_foo(callable $foo) {}',
  203. '<?php /** @param callable $foo */ function my_foo($foo) {}',
  204. ],
  205. 'self accessor' => [
  206. '<?php
  207. class Foo {
  208. /** @param self $foo */ function my_foo(self $foo) {}
  209. }
  210. ',
  211. '<?php
  212. class Foo {
  213. /** @param self $foo */ function my_foo($foo) {}
  214. }
  215. ',
  216. ],
  217. 'report static as self' => [
  218. '<?php
  219. class Foo {
  220. /** @param static $foo */ function my_foo(self $foo) {}
  221. }
  222. ',
  223. '<?php
  224. class Foo {
  225. /** @param static $foo */ function my_foo($foo) {}
  226. }
  227. ',
  228. ],
  229. 'skip resource special type' => [
  230. '<?php /** @param $bar resource */ function my_foo($bar) {}',
  231. ],
  232. 'skip mixed special type' => [
  233. '<?php /** @param $bar mixed */ function my_foo($bar) {}',
  234. ],
  235. 'null alone cannot be a param type' => [
  236. '<?php /** @param $bar null */ function my_foo($bar) {}',
  237. ],
  238. 'skip mixed types' => [
  239. '<?php /** @param Foo|Bar $bar */ function my_foo($bar) {}',
  240. ],
  241. 'skip mixed types including array' => [
  242. '<?php /** @param array|Foo $expected */ function testResolveIntersectionOfPaths($expected) {}',
  243. ],
  244. 'skip primitive or array types' => [
  245. '<?php /** @param string|string[] $expected */ function testResolveIntersectionOfPaths($expected) {}',
  246. ],
  247. 'array of types' => [
  248. '<?php /** @param Foo[] $foo */ function my_foo(array $foo) {}',
  249. '<?php /** @param Foo[] $foo */ function my_foo($foo) {}',
  250. ],
  251. 'nullable array of types' => [
  252. '<?php /** @param null|Foo[] $foo */ function my_foo(?array $foo) {}',
  253. '<?php /** @param null|Foo[] $foo */ function my_foo($foo) {}',
  254. 70100,
  255. ],
  256. 'nullable and mixed types of arrays' => [
  257. '<?php /** @param null|Foo[]|Bar[] $foo */ function my_foo(?array $foo) {}',
  258. '<?php /** @param null|Foo[]|Bar[] $foo */ function my_foo($foo) {}',
  259. 70100,
  260. ],
  261. 'nullable and array and array of types' => [
  262. '<?php /** @param null|Foo[]|array $foo */ function my_foo(?array $foo) {}',
  263. '<?php /** @param null|Foo[]|array $foo */ function my_foo($foo) {}',
  264. 70100,
  265. ],
  266. 'nullable array of array of types' => [
  267. '<?php /** @param null|Foo[][] $foo */ function my_foo(?array $foo) {}',
  268. '<?php /** @param null|Foo[][] $foo */ function my_foo($foo) {}',
  269. 70100,
  270. ],
  271. 'nullable and string param' => [
  272. '<?php /** @param null|string $foo */ function my_foo(?string $foo) {}',
  273. '<?php /** @param null|string $foo */ function my_foo($foo) {}',
  274. 70100,
  275. ],
  276. 'nullable and int param' => [
  277. '<?php /** @param null|int $foo */ function my_foo(?int $foo) {}',
  278. '<?php /** @param null|int $foo */ function my_foo($foo) {}',
  279. 70100,
  280. ],
  281. 'nullable and float param' => [
  282. '<?php /** @param null|float $foo */ function my_foo(?float $foo) {}',
  283. '<?php /** @param null|float $foo */ function my_foo($foo) {}',
  284. 70100,
  285. ],
  286. 'nullable and bool param' => [
  287. '<?php /** @param null|bool $foo */ function my_foo(?bool $foo) {}',
  288. '<?php /** @param null|bool $foo */ function my_foo($foo) {}',
  289. 70100,
  290. ],
  291. 'nullable and callable param' => [
  292. '<?php /** @param null|callable $foo */ function my_foo(?callable $foo) {}',
  293. '<?php /** @param null|callable $foo */ function my_foo($foo) {}',
  294. 70100,
  295. ],
  296. 'nullable and iterable param' => [
  297. '<?php /** @param null|iterable $foo */ function my_foo(?iterable $foo) {}',
  298. '<?php /** @param null|iterable $foo */ function my_foo($foo) {}',
  299. 70100,
  300. ],
  301. 'nullable and class name param' => [
  302. '<?php /** @param null|Foo $foo */ function my_foo(?Foo $foo) {}',
  303. '<?php /** @param null|Foo $foo */ function my_foo($foo) {}',
  304. 70100,
  305. ],
  306. 'array and iterable param' => [
  307. '<?php /** @param Foo[]|iterable $foo */ function my_foo(iterable $foo) {}',
  308. '<?php /** @param Foo[]|iterable $foo */ function my_foo($foo) {}',
  309. 70100,
  310. ],
  311. 'object param' => [
  312. '<?php /** @param object $foo */ function my_foo(object $foo) {}',
  313. '<?php /** @param object $foo */ function my_foo($foo) {}',
  314. 70200,
  315. ],
  316. 'nullable and object param' => [
  317. '<?php /** @param null|object $foo */ function my_foo(?object $foo) {}',
  318. '<?php /** @param null|object $foo */ function my_foo($foo) {}',
  319. 70200,
  320. ],
  321. 'generics with single type' => [
  322. '<?php /** @param array<foo> $foo */ function my_foo(array $foo) {}',
  323. '<?php /** @param array<foo> $foo */ function my_foo($foo) {}',
  324. ],
  325. 'generics with multiple types' => [
  326. '<?php /** @param array<int, string> $foo */ function my_foo(array $foo) {}',
  327. '<?php /** @param array<int, string> $foo */ function my_foo($foo) {}',
  328. ],
  329. 'stop searching last token' => [
  330. '<?php class Foo { /** @param Bar $bar */ public function foo($tab) { } }',
  331. ],
  332. 'param by reference' => [
  333. '<?php /** @param array $data */ function foo(array &$data) {}',
  334. '<?php /** @param array $data */ function foo(&$data) {}',
  335. ],
  336. 'optional param by reference' => [
  337. '<?php /** @param null|string[] $matches */ function matchAll(?array &$matches) {}',
  338. '<?php /** @param null|string[] $matches */ function matchAll(&$matches) {}',
  339. 70100,
  340. ],
  341. 'void as type in phpdoc' => [
  342. '<?php /** @param void $bar */ function foo($bar) {}',
  343. ],
  344. 'array and traversable' => [
  345. '<?php /** @param array|Traversable $foo */ function my_foo(iterable $foo) {}',
  346. '<?php /** @param array|Traversable $foo */ function my_foo($foo) {}',
  347. 70100,
  348. ],
  349. 'array and traversable with leading slash' => [
  350. '<?php /** @param array|\Traversable $foo */ function my_foo(iterable $foo) {}',
  351. '<?php /** @param array|\Traversable $foo */ function my_foo($foo) {}',
  352. 70100,
  353. ],
  354. 'array and traversable in a namespace' => [
  355. '<?php
  356. namespace App;
  357. /** @param array|Traversable $foo */
  358. function my_foo($foo) {}
  359. ',
  360. ],
  361. 'array and traversable with leading slash in a namespace' => [
  362. '<?php
  363. namespace App;
  364. /** @param array|\Traversable $foo */
  365. function my_foo(iterable $foo) {}
  366. ',
  367. '<?php
  368. namespace App;
  369. /** @param array|\Traversable $foo */
  370. function my_foo($foo) {}
  371. ',
  372. 70100,
  373. ],
  374. 'array and imported traversable in a namespace' => [
  375. '<?php
  376. namespace App;
  377. use Traversable;
  378. /** @param array|Traversable $foo */
  379. function my_foo(iterable $foo) {}
  380. ',
  381. '<?php
  382. namespace App;
  383. use Traversable;
  384. /** @param array|Traversable $foo */
  385. function my_foo($foo) {}
  386. ',
  387. 70100,
  388. ],
  389. 'array and object aliased as traversable in a namespace' => [
  390. '<?php
  391. namespace App;
  392. use Foo as Traversable;
  393. /** @param array|Traversable $foo */
  394. function my_foo($foo) {}
  395. ',
  396. null,
  397. 70100,
  398. ],
  399. 'array of object and traversable' => [
  400. '<?php /** @param Foo[]|Traversable $foo */ function my_foo(iterable $foo) {}',
  401. '<?php /** @param Foo[]|Traversable $foo */ function my_foo($foo) {}',
  402. 70100,
  403. ],
  404. 'array of object and iterable' => [
  405. '<?php /** @param Foo[]|iterable $foo */ function my_foo(iterable $foo) {}',
  406. '<?php /** @param Foo[]|iterable $foo */ function my_foo($foo) {}',
  407. 70100,
  408. ],
  409. 'array of string and array of int' => [
  410. '<?php /** @param string[]|int[] $foo */ function my_foo(array $foo) {}',
  411. '<?php /** @param string[]|int[] $foo */ function my_foo($foo) {}',
  412. ],
  413. 'do not fix scalar types when configured as such' => [
  414. '<?php /** @param int $foo */ function my_foo($foo) {}',
  415. null,
  416. null,
  417. ['scalar_types' => false],
  418. ],
  419. 'do not fix function call' => [
  420. '<?php
  421. /** @param string $foo */
  422. function bar($notFoo) {
  423. return baz($foo);
  424. }
  425. ',
  426. ],
  427. 'do not fix function call when no parameter' => [
  428. '<?php
  429. /** @param string $foo */
  430. function bar() {
  431. return baz($foo);
  432. }
  433. ',
  434. ],
  435. ];
  436. }
  437. }