PhpdocAddMissingParamAnnotationFixerTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  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\ConfigurationException\InvalidConfigurationException;
  14. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  15. use PhpCsFixer\WhitespacesFixerConfig;
  16. /**
  17. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  18. *
  19. * @internal
  20. *
  21. * @covers \PhpCsFixer\Fixer\Phpdoc\PhpdocAddMissingParamAnnotationFixer
  22. */
  23. final class PhpdocAddMissingParamAnnotationFixerTest extends AbstractFixerTestCase
  24. {
  25. public function testConfigureRejectsUnknownConfigurationKey(): void
  26. {
  27. $key = 'foo';
  28. $this->expectException(InvalidConfigurationException::class);
  29. $this->expectExceptionMessage(sprintf(
  30. '[phpdoc_add_missing_param_annotation] Invalid configuration: The option "%s" does not exist.',
  31. $key
  32. ));
  33. $this->fixer->configure([
  34. $key => 'bar',
  35. ]);
  36. }
  37. /**
  38. * @dataProvider provideConfigureRejectsInvalidConfigurationValueCases
  39. *
  40. * @param mixed $value
  41. */
  42. public function testConfigureRejectsInvalidConfigurationValue($value, string $expectedMessage): void
  43. {
  44. $this->expectException(InvalidConfigurationException::class);
  45. $this->expectExceptionMessageMatches($expectedMessage);
  46. $this->fixer->configure([
  47. 'only_untyped' => $value,
  48. ]);
  49. }
  50. /**
  51. * @return iterable<string, array>
  52. */
  53. public static function provideConfigureRejectsInvalidConfigurationValueCases(): iterable
  54. {
  55. yield 'null' => [
  56. null,
  57. '#expected to be of type "bool", but is of type "(null|NULL)"\.$#',
  58. ];
  59. yield 'int' => [
  60. 1,
  61. '#expected to be of type "bool", but is of type "(int|integer)"\.$#',
  62. ];
  63. yield 'array' => [
  64. [],
  65. '#expected to be of type "bool", but is of type "array"\.$#',
  66. ];
  67. yield 'float' => [
  68. 0.1,
  69. '#expected to be of type "bool", but is of type "(float|double)"\.$#',
  70. ];
  71. yield 'object' => [
  72. new \stdClass(),
  73. '#expected to be of type "bool", but is of type "stdClass"\.$#',
  74. ];
  75. }
  76. /**
  77. * @param null|array<string, mixed> $config
  78. *
  79. * @dataProvider provideFixCases
  80. */
  81. public function testFix(string $expected, ?string $input = null, ?array $config = null): void
  82. {
  83. $this->fixer->configure($config ?? ['only_untyped' => false]);
  84. $this->doTest($expected, $input);
  85. }
  86. public static function provideFixCases(): iterable
  87. {
  88. return [
  89. [
  90. '<?php
  91. /**
  92. *
  93. */',
  94. ],
  95. [
  96. '<?php
  97. /**
  98. * @param int $foo
  99. * @param mixed $bar
  100. */
  101. function f1($foo, $bar) {}',
  102. '<?php
  103. /**
  104. * @param int $foo
  105. */
  106. function f1($foo, $bar) {}',
  107. ],
  108. [
  109. '<?php
  110. /**
  111. * @param int $bar
  112. * @param mixed $foo
  113. */
  114. function f2($foo, $bar) {}',
  115. '<?php
  116. /**
  117. * @param int $bar
  118. */
  119. function f2($foo, $bar) {}',
  120. ],
  121. [
  122. '<?php
  123. /**
  124. * @return void
  125. * @param mixed $foo
  126. * @param mixed $bar
  127. */
  128. function f3($foo, $bar) {}',
  129. '<?php
  130. /**
  131. * @return void
  132. */
  133. function f3($foo, $bar) {}',
  134. ],
  135. [
  136. '<?php
  137. abstract class Foo {
  138. /**
  139. * @param int $bar
  140. * @param mixed $foo
  141. */
  142. abstract public function f4a($foo, $bar);
  143. }',
  144. '<?php
  145. abstract class Foo {
  146. /**
  147. * @param int $bar
  148. */
  149. abstract public function f4a($foo, $bar);
  150. }',
  151. ],
  152. [
  153. '<?php
  154. class Foo {
  155. /**
  156. * @param int $bar
  157. * @param mixed $foo
  158. */
  159. static final public function f4b($foo, $bar) {}
  160. }',
  161. '<?php
  162. class Foo {
  163. /**
  164. * @param int $bar
  165. */
  166. static final public function f4b($foo, $bar) {}
  167. }',
  168. ],
  169. [
  170. '<?php
  171. class Foo {
  172. /**
  173. * @var int
  174. */
  175. private $foo;
  176. }',
  177. ],
  178. [
  179. '<?php
  180. /**
  181. * @param $bar No type !!
  182. * @param mixed $foo
  183. */
  184. function f5($foo, $bar) {}',
  185. '<?php
  186. /**
  187. * @param $bar No type !!
  188. */
  189. function f5($foo, $bar) {}',
  190. ],
  191. [
  192. '<?php
  193. /**
  194. * @param int
  195. * @param int $bar
  196. * @param Foo\Bar $foo
  197. */
  198. function f6(Foo\Bar $foo, $bar) {}',
  199. '<?php
  200. /**
  201. * @param int
  202. * @param int $bar
  203. */
  204. function f6(Foo\Bar $foo, $bar) {}',
  205. ],
  206. [
  207. '<?php
  208. /**
  209. * @param int $bar
  210. * @param null|string $foo
  211. */
  212. function f7(string $foo = nuLl, $bar) {}',
  213. '<?php
  214. /**
  215. * @param int $bar
  216. */
  217. function f7(string $foo = nuLl, $bar) {}',
  218. ],
  219. [
  220. '<?php
  221. /**
  222. * @param int $bar
  223. * @param mixed $baz
  224. *
  225. * @return void
  226. */
  227. function f9(string $foo, $bar, $baz) {}',
  228. '<?php
  229. /**
  230. * @param int $bar
  231. *
  232. * @return void
  233. */
  234. function f9(string $foo, $bar, $baz) {}',
  235. ['only_untyped' => true],
  236. ],
  237. [
  238. '<?php
  239. /**
  240. * @param bool|bool[] $caseSensitive Line 1
  241. * Line 2
  242. */
  243. function f11($caseSensitive) {}
  244. ',
  245. ],
  246. [
  247. '<?php
  248. /** @return string */
  249. function hello($string)
  250. {
  251. return $string;
  252. }',
  253. ],
  254. [
  255. '<?php
  256. /** @return string
  257. * @param mixed $string
  258. */
  259. function hello($string)
  260. {
  261. return $string;
  262. }',
  263. '<?php
  264. /** @return string
  265. */
  266. function hello($string)
  267. {
  268. return $string;
  269. }',
  270. ],
  271. [
  272. '<?php
  273. /**
  274. * @param mixed $string
  275. * @return string */
  276. function hello($string)
  277. {
  278. return $string;
  279. }',
  280. '<?php
  281. /**
  282. * @return string */
  283. function hello($string)
  284. {
  285. return $string;
  286. }',
  287. ],
  288. [
  289. '<?php
  290. /**
  291. * @param int $bar
  292. * @param string $foo
  293. */
  294. function f8(string $foo = "null", $bar) {}',
  295. '<?php
  296. /**
  297. * @param int $bar
  298. */
  299. function f8(string $foo = "null", $bar) {}',
  300. ],
  301. [
  302. '<?php
  303. /**
  304. * @{inheritdoc}
  305. */
  306. function f10(string $foo = "null", $bar) {}',
  307. ],
  308. [
  309. '<?php
  310. /**
  311. * @inheritDoc
  312. */
  313. function f10(string $foo = "null", $bar) {}',
  314. ],
  315. [
  316. '<?php
  317. /**
  318. * @param int $bar
  319. * @param ?array $foo
  320. */
  321. function p1(?array $foo = null, $bar) {}',
  322. '<?php
  323. /**
  324. * @param int $bar
  325. */
  326. function p1(?array $foo = null, $bar) {}',
  327. ['only_untyped' => false],
  328. ],
  329. [
  330. '<?php
  331. /**
  332. * Foo
  333. * @param mixed $bar
  334. */
  335. function p1(?int $foo = 0, $bar) {}',
  336. '<?php
  337. /**
  338. * Foo
  339. */
  340. function p1(?int $foo = 0, $bar) {}',
  341. ['only_untyped' => true],
  342. ],
  343. [
  344. '<?php
  345. /**
  346. * Foo
  347. * @return int
  348. */
  349. function p1(?int $foo = 0) {}',
  350. null,
  351. ['only_untyped' => true],
  352. ],
  353. ];
  354. }
  355. /**
  356. * @param null|array<string, mixed> $config
  357. *
  358. * @dataProvider provideMessyWhitespacesCases
  359. */
  360. public function testMessyWhitespaces(string $expected, ?string $input = null, ?array $config = null): void
  361. {
  362. $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n"));
  363. $this->fixer->configure($config ?? ['only_untyped' => false]);
  364. $this->doTest($expected, $input);
  365. }
  366. public static function provideMessyWhitespacesCases(): iterable
  367. {
  368. return [
  369. [
  370. "<?php\r\n\t/**\r\n\t * @param int \$bar\r\n\t * @param null|string \$foo\r\n\t */\r\n\tfunction f7(string \$foo = nuLl, \$bar) {}",
  371. "<?php\r\n\t/**\r\n\t * @param int \$bar\r\n\t */\r\n\tfunction f7(string \$foo = nuLl, \$bar) {}",
  372. ],
  373. ];
  374. }
  375. /**
  376. * @dataProvider provideByReferenceCases
  377. */
  378. public function testByReference(string $expected, string $input): void
  379. {
  380. $this->fixer->configure(['only_untyped' => false]);
  381. $this->doTest($expected, $input);
  382. }
  383. public static function provideByReferenceCases(): iterable
  384. {
  385. return [
  386. [
  387. '<?php
  388. /**
  389. * something
  390. * @param mixed $numbers
  391. */
  392. function add(&$numbers) {}
  393. ',
  394. '<?php
  395. /**
  396. * something
  397. */
  398. function add(&$numbers) {}
  399. ',
  400. ],
  401. [
  402. '<?php
  403. /**
  404. * something
  405. * @param null|array $numbers
  406. */
  407. function add(array &$numbers = null) {}
  408. ',
  409. '<?php
  410. /**
  411. * something
  412. */
  413. function add(array &$numbers = null) {}
  414. ',
  415. ],
  416. ];
  417. }
  418. /**
  419. * @dataProvider provideVariableNumberOfArgumentsCases
  420. */
  421. public function testVariableNumberOfArguments(string $expected, string $input): void
  422. {
  423. $this->fixer->configure(['only_untyped' => false]);
  424. $this->doTest($expected, $input);
  425. }
  426. public static function provideVariableNumberOfArgumentsCases(): iterable
  427. {
  428. return [
  429. [
  430. '<?php
  431. /**
  432. * something
  433. * @param array $numbers
  434. */
  435. function sum(...$numbers) {}
  436. ',
  437. '<?php
  438. /**
  439. * something
  440. */
  441. function sum(...$numbers) {}
  442. ',
  443. ],
  444. [
  445. '<?php
  446. /**
  447. * @param int $a
  448. * @param array $numbers
  449. */
  450. function sum($a, ...$numbers) {}
  451. ',
  452. '<?php
  453. /**
  454. * @param int $a
  455. */
  456. function sum($a, ...$numbers) {}
  457. ',
  458. ],
  459. [
  460. '<?php
  461. /**
  462. * @param \Date[] $numbers
  463. */
  464. function sum(\Date ...$numbers) {}
  465. ',
  466. '<?php
  467. /**
  468. */
  469. function sum(\Date ...$numbers) {}
  470. ',
  471. ],
  472. ];
  473. }
  474. /**
  475. * @dataProvider provideFix80Cases
  476. *
  477. * @requires PHP 8.0
  478. */
  479. public function testFix80(string $expected, ?string $input = null): void
  480. {
  481. $this->fixer->configure(['only_untyped' => false]);
  482. $this->doTest($expected, $input);
  483. }
  484. public static function provideFix80Cases(): iterable
  485. {
  486. yield [
  487. '<?php class Foo {
  488. /**
  489. * @param Bar $x
  490. * @param ?Bar $y
  491. * @param null|Bar $z
  492. */
  493. public function __construct(
  494. public Bar $x,
  495. protected ?Bar $y,
  496. private null|Bar $z,
  497. ) {}
  498. }',
  499. '<?php class Foo {
  500. /**
  501. */
  502. public function __construct(
  503. public Bar $x,
  504. protected ?Bar $y,
  505. private null|Bar $z,
  506. ) {}
  507. }',
  508. ];
  509. }
  510. /**
  511. * @dataProvider provideFix81Cases
  512. *
  513. * @requires PHP 8.1
  514. */
  515. public function testFix81(string $expected, ?string $input = null): void
  516. {
  517. $this->fixer->configure(['only_untyped' => false]);
  518. $this->doTest($expected, $input);
  519. }
  520. public static function provideFix81Cases(): iterable
  521. {
  522. yield [
  523. '<?php class Foo {
  524. /**
  525. * @param Bar $bar
  526. * @param Baz $baz
  527. */
  528. public function __construct(
  529. public readonly Bar $bar,
  530. readonly public Baz $baz,
  531. ) {}
  532. }',
  533. '<?php class Foo {
  534. /**
  535. */
  536. public function __construct(
  537. public readonly Bar $bar,
  538. readonly public Baz $baz,
  539. ) {}
  540. }',
  541. ];
  542. }
  543. }