NullableTypeDeclarationFixerTest.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. }
  175. /**
  176. * @param _AutogeneratedInputConfiguration $config
  177. *
  178. * @dataProvider provideFix81Cases
  179. *
  180. * @requires PHP 8.1
  181. */
  182. public function testFix81(string $expected, ?string $input = null, array $config = []): void
  183. {
  184. $this->fixer->configure($config);
  185. $this->doTest($expected, $input);
  186. }
  187. /**
  188. * @return iterable<string, array{string, 1?: ?string, 2?: array<string, mixed>}>
  189. */
  190. public static function provideFix81Cases(): iterable
  191. {
  192. yield 'readonly property' => [
  193. '<?php
  194. class Qux
  195. {
  196. public readonly ?int $baz;
  197. }
  198. ',
  199. '<?php
  200. class Qux
  201. {
  202. public readonly int|null $baz;
  203. }
  204. ',
  205. ];
  206. yield 'readonly property with union syntax expected' => [
  207. '<?php
  208. class Qux
  209. {
  210. public readonly null|int $baz;
  211. }
  212. ',
  213. '<?php
  214. class Qux
  215. {
  216. public readonly ?int $baz;
  217. }
  218. ',
  219. ['syntax' => 'union'],
  220. ];
  221. }
  222. /**
  223. * @param _AutogeneratedInputConfiguration $config
  224. *
  225. * @dataProvider provideFix82Cases
  226. *
  227. * @requires PHP 8.2
  228. */
  229. public function testFix82(string $expected, ?string $input = null, array $config = []): void
  230. {
  231. $this->fixer->configure($config);
  232. $this->doTest($expected, $input);
  233. }
  234. /**
  235. * @return iterable<string, array{string, 1?: ?string, 2?: array<string, mixed>}>
  236. */
  237. public static function provideFix82Cases(): iterable
  238. {
  239. yield 'skips DNF types' => [
  240. '<?php
  241. class Infinite
  242. {
  243. private static (A&B)|null $dft;
  244. }
  245. ',
  246. ];
  247. yield 'standalone null' => [
  248. '<?php
  249. class Foo
  250. {
  251. public function bar(null|array $config = null): null {}
  252. public function baz(null|ARRAY $config = NULL): NULL {}
  253. }
  254. ',
  255. '<?php
  256. class Foo
  257. {
  258. public function bar(?array $config = null): null {}
  259. public function baz(?ARRAY $config = NULL): NULL {}
  260. }
  261. ',
  262. ['syntax' => 'union'],
  263. ];
  264. }
  265. }