PhpdocAddMissingParamAnnotationFixerTest.php 14 KB

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