NullableTypeDeclarationFixerTest.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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\LanguageConstruct;
  13. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  14. /**
  15. * @author John Paul E. Balandan, CPA <paulbalandan@gmail.com>
  16. *
  17. * @internal
  18. *
  19. * @covers \PhpCsFixer\Fixer\LanguageConstruct\NullableTypeDeclarationFixer
  20. *
  21. * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\LanguageConstruct\NullableTypeDeclarationFixer>
  22. *
  23. * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\LanguageConstruct\NullableTypeDeclarationFixer
  24. */
  25. final class NullableTypeDeclarationFixerTest extends AbstractFixerTestCase
  26. {
  27. /**
  28. * @dataProvider provideFixCases
  29. *
  30. * @requires PHP 8.0
  31. */
  32. public function testFix(string $expected, ?string $input = null): void
  33. {
  34. $this->doTest($expected, $input);
  35. }
  36. /**
  37. * @return iterable<string, array{string, 1?: ?string}>
  38. */
  39. public static function provideFixCases(): iterable
  40. {
  41. yield 'scalar with null' => [
  42. "<?php\nfunction foo(?int \$bar): void {}\n",
  43. "<?php\nfunction foo(int|null \$bar): void {}\n",
  44. ];
  45. yield 'class with null' => [
  46. "<?php\nfunction bar(?\\stdClass \$crate): int {}\n",
  47. "<?php\nfunction bar(null | \\stdClass \$crate): int {}\n",
  48. ];
  49. yield 'static null' => [
  50. '<?php
  51. class Foo
  52. {
  53. public function bar(?array $config = null): ?static {}
  54. }
  55. ',
  56. '<?php
  57. class Foo
  58. {
  59. public function bar(null|array $config = null): null|static {}
  60. }
  61. ',
  62. ];
  63. yield 'multiple parameters' => [
  64. "<?php\nfunction baz(?Foo \$foo, int|string \$value, ?array \$config = null): ?int {}\n",
  65. "<?php\nfunction baz(null|Foo \$foo, int|string \$value, null|array \$config = null): int|null {}\n",
  66. ];
  67. yield 'class properties' => [
  68. '<?php
  69. class Dto
  70. {
  71. public ?string $name;
  72. public ?array $parameters;
  73. public ?int $count;
  74. public ?Closure $callable;
  75. }
  76. ',
  77. '<?php
  78. class Dto
  79. {
  80. public null|string $name;
  81. public array|null $parameters;
  82. public int|null $count;
  83. public null|Closure $callable;
  84. }
  85. ',
  86. ];
  87. yield 'skips more than two atomic types' => [
  88. "<?php\nstatic fn (int|null|string \$bar): bool => true;\n",
  89. ];
  90. yield 'skips already fixed' => [
  91. "<?php\n\$bar = function (?string \$input): int {};\n",
  92. ];
  93. }
  94. /**
  95. * @dataProvider provideFix80Cases
  96. *
  97. * @requires PHP 8.0
  98. */
  99. public function testFix80(string $expected, ?string $input = null): void
  100. {
  101. $this->fixer->configure(['syntax' => 'union']);
  102. $this->doTest($expected, $input);
  103. }
  104. /**
  105. * @return iterable<string, array{string, 1?: ?string}>
  106. */
  107. public static function provideFix80Cases(): iterable
  108. {
  109. yield 'scalar with null' => [
  110. "<?php\nfunction foo(null|int \$bar): void {}\n",
  111. "<?php\nfunction foo(?int \$bar): void {}\n",
  112. ];
  113. yield 'class with null' => [
  114. "<?php\nfunction bar(null|\\stdClass \$crate): int {}\n",
  115. "<?php\nfunction bar(?\\stdClass \$crate): int {}\n",
  116. ];
  117. yield 'static null' => [
  118. '<?php
  119. class Foo
  120. {
  121. public function bar(null|array $config = null): null|static {}
  122. }
  123. ',
  124. '<?php
  125. class Foo
  126. {
  127. public function bar(?array $config = null): ?static {}
  128. }
  129. ',
  130. ];
  131. yield 'multiple parameters' => [
  132. "<?php\nfunction baz(null|Foo \$foo, int|string \$value, null|array \$config = null): null|int {}\n",
  133. "<?php\nfunction baz(?Foo \$foo, int|string \$value, ?array \$config = null): ?int {}\n",
  134. ];
  135. yield 'class properties' => [
  136. '<?php
  137. class Dto
  138. {
  139. public null|\Closure $callable;
  140. public null|string $name;
  141. public null|array $parameters;
  142. public null|int $count;
  143. }
  144. ',
  145. '<?php
  146. class Dto
  147. {
  148. public ?\Closure $callable;
  149. public ?string $name;
  150. public ?array $parameters;
  151. public ?int $count;
  152. }
  153. ',
  154. ];
  155. yield 'space after ?' => [
  156. '<?php
  157. class Foo
  158. {
  159. private null|int $x;
  160. public static function from(null|int $x): null|static {}
  161. }
  162. ',
  163. '<?php
  164. class Foo
  165. {
  166. private ? int $x;
  167. public static function from(? int $x): ? static {}
  168. }
  169. ',
  170. ];
  171. yield 'skips already fixed' => [
  172. "<?php\n\$bar = function (null | string \$input): int {};\n",
  173. ];
  174. yield 'no space before ?' => [
  175. <<<'PHP'
  176. <?php
  177. class Foo
  178. {
  179. public null|int $a;
  180. protected null|array $b;
  181. private null|string $c;
  182. private static null|bool $d;
  183. }
  184. PHP,
  185. <<<'PHP'
  186. <?php
  187. class Foo
  188. {
  189. public?int $a;
  190. protected?array $b;
  191. private?string $c;
  192. private static?bool $d;
  193. }
  194. PHP,
  195. ];
  196. }
  197. /**
  198. * @param _AutogeneratedInputConfiguration $config
  199. *
  200. * @dataProvider provideFix81Cases
  201. *
  202. * @requires PHP 8.1
  203. */
  204. public function testFix81(string $expected, ?string $input = null, array $config = []): void
  205. {
  206. $this->fixer->configure($config);
  207. $this->doTest($expected, $input);
  208. }
  209. /**
  210. * @return iterable<string, array{string, 1?: ?string, 2?: array<string, mixed>}>
  211. */
  212. public static function provideFix81Cases(): iterable
  213. {
  214. yield 'readonly property' => [
  215. '<?php
  216. class Qux
  217. {
  218. public readonly ?int $baz;
  219. }
  220. ',
  221. '<?php
  222. class Qux
  223. {
  224. public readonly int|null $baz;
  225. }
  226. ',
  227. ];
  228. yield 'readonly property with union syntax expected' => [
  229. '<?php
  230. class Qux
  231. {
  232. public readonly null|int $baz;
  233. }
  234. ',
  235. '<?php
  236. class Qux
  237. {
  238. public readonly ?int $baz;
  239. }
  240. ',
  241. ['syntax' => 'union'],
  242. ];
  243. }
  244. /**
  245. * @param _AutogeneratedInputConfiguration $config
  246. *
  247. * @dataProvider provideFix82Cases
  248. *
  249. * @requires PHP 8.2
  250. */
  251. public function testFix82(string $expected, ?string $input = null, array $config = []): void
  252. {
  253. $this->fixer->configure($config);
  254. $this->doTest($expected, $input);
  255. }
  256. /**
  257. * @return iterable<string, array{string, 1?: ?string, 2?: array<string, mixed>}>
  258. */
  259. public static function provideFix82Cases(): iterable
  260. {
  261. yield 'skips DNF types' => [
  262. '<?php
  263. class Infinite
  264. {
  265. private static (A&B)|null $dft;
  266. }
  267. ',
  268. ];
  269. yield 'standalone null' => [
  270. '<?php
  271. class Foo
  272. {
  273. public function bar(null|array $config = null): null {}
  274. public function baz(null|ARRAY $config = NULL): NULL {}
  275. }
  276. ',
  277. '<?php
  278. class Foo
  279. {
  280. public function bar(?array $config = null): null {}
  281. public function baz(?ARRAY $config = NULL): NULL {}
  282. }
  283. ',
  284. ['syntax' => 'union'],
  285. ];
  286. }
  287. }