NativeTypeDeclarationCasingFixerTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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\Casing;
  13. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  14. /**
  15. * @internal
  16. *
  17. * @covers \PhpCsFixer\Fixer\Casing\NativeTypeDeclarationCasingFixer
  18. *
  19. * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\Casing\NativeTypeDeclarationCasingFixer>
  20. */
  21. final class NativeTypeDeclarationCasingFixerTest extends AbstractFixerTestCase
  22. {
  23. /**
  24. * @dataProvider provideFixCases
  25. */
  26. public function testFix(string $expected, ?string $input = null): void
  27. {
  28. $this->doTest($expected, $input);
  29. }
  30. public static function provideFixCases(): iterable
  31. {
  32. yield [
  33. '<?php
  34. function A(int $a): void {}
  35. class Foo
  36. {
  37. private bool $c = false;
  38. private bool $d = false;
  39. public function B(int $a): bool { return $this->c || $this->d; }
  40. }
  41. function C(float $a): array { return [$a];}
  42. function D(array $a): array { return [$a];}
  43. ',
  44. '<?php
  45. function A(INT $a): VOID {}
  46. class Foo
  47. {
  48. private BOOL $c = false;
  49. private BOOL $d = false;
  50. public function B(INT $a): BOOL { return $this->c || $this->d; }
  51. }
  52. function C(FLOAT $a): ARRAY { return [$a];}
  53. function D(ARRAY $a): ARRAY { return [$a];}
  54. ',
  55. ];
  56. yield [
  57. '<?php
  58. class Foo
  59. {
  60. private function Bar(array $bar) {
  61. return false;
  62. }
  63. }
  64. ',
  65. '<?php
  66. class Foo
  67. {
  68. private function Bar(ARRAY $bar) {
  69. return false;
  70. }
  71. }
  72. ',
  73. ];
  74. yield [
  75. '<?php
  76. interface Foo
  77. {
  78. public function Bar(array $bar);
  79. }
  80. ',
  81. '<?php
  82. interface Foo
  83. {
  84. public function Bar(ArrAY $bar);
  85. }
  86. ',
  87. ];
  88. yield [
  89. '<?php
  90. function Foo(/**/array/**/$bar) {
  91. return false;
  92. }
  93. ',
  94. '<?php
  95. function Foo(/**/ARRAY/**/$bar) {
  96. return false;
  97. }
  98. ',
  99. ];
  100. yield [
  101. '<?php
  102. class Bar { function Foo(array $a, callable $b, self $c) {} }
  103. ',
  104. '<?php
  105. class Bar { function Foo(ARRAY $a, CALLABLE $b, Self $c) {} }
  106. ',
  107. ];
  108. yield [
  109. '<?php
  110. function Foo(INTEGER $a) {}
  111. ',
  112. ];
  113. yield [
  114. '<?php function Foo(
  115. String\A $x,
  116. B\String\C $y
  117. ) {}',
  118. ];
  119. yield [
  120. '<?php final class Foo1 { final public function Foo(bool $A, float $B, int $C, string $D): int {} }',
  121. '<?php final class Foo1 { final public function Foo(BOOL $A, FLOAT $B, INT $C, STRING $D): INT {} }',
  122. ];
  123. yield [
  124. '<?php function Foo(bool $A, float $B, int $C, string $D): int {}',
  125. '<?php function Foo(BOOL $A, FLOAT $B, INT $C, STRING $D): INT {}',
  126. ];
  127. yield [
  128. '<?php function Foo(): Foo\A { return new Foo(); }',
  129. ];
  130. yield [
  131. '<?php trait XYZ { function Foo(iterable $A): void {} }',
  132. '<?php trait XYZ { function Foo(ITERABLE $A): VOID {} }',
  133. ];
  134. yield [
  135. '<?php function Foo(iterable $A): void {}',
  136. '<?php function Foo(ITERABLE $A): VOID {}',
  137. ];
  138. yield [
  139. '<?php function Foo(?int $A): void {}',
  140. '<?php function Foo(?INT $A): VOID {}',
  141. ];
  142. yield [
  143. '<?php function Foo(string $A): ?/* */int {}',
  144. '<?php function Foo(STRING $A): ?/* */INT {}',
  145. ];
  146. yield [
  147. '<?php function Foo(object $A): void {}',
  148. '<?php function Foo(OBJECT $A): VOID {}',
  149. ];
  150. yield [
  151. '<?php return function (callable $c) {};',
  152. '<?php return function (CALLABLE $c) {};',
  153. ];
  154. yield [
  155. '<?php return fn (callable $c): int => 1;',
  156. '<?php return fn (CALLABLE $c): INT => 1;',
  157. ];
  158. yield [
  159. '<?php
  160. class Foo
  161. {
  162. const A = 1;
  163. const B = [];
  164. const INT = "A"; // class constant; INT is the name of the const, not the type
  165. const FLOAT=1.2;
  166. }
  167. const INT = "A"; // outside class; INT is the name of the const, not the type
  168. ',
  169. ];
  170. yield 'class properties single type' => [
  171. '<?php
  172. class D{}
  173. $a = new class extends D {
  174. private array $ax;
  175. private bool $bx = false;
  176. private float $cx = 3.14;
  177. private int $dx = 667;
  178. private iterable $ex = [];
  179. private mixed $f;
  180. private object $g;
  181. private parent $h;
  182. private self $i;
  183. private static $j;
  184. private ?string $k;
  185. private $INT = 1;
  186. private FOO $bar;
  187. private A\INT\B $z;
  188. };
  189. ',
  190. '<?php
  191. class D{}
  192. $a = new class extends D {
  193. private ARRAY $ax;
  194. private BOOL $bx = false;
  195. private FLOAT $cx = 3.14;
  196. private INT $dx = 667;
  197. private ITERABLE $ex = [];
  198. private MIXED $f;
  199. private OBJECT $g;
  200. private PARENT $h;
  201. private Self $i;
  202. private STatic $j;
  203. private ?STRIng $k;
  204. private $INT = 1;
  205. private FOO $bar;
  206. private A\INT\B $z;
  207. };
  208. ',
  209. ];
  210. yield 'var keyword' => [
  211. '<?php class Foo {
  212. var $bar;
  213. }',
  214. ];
  215. yield 'static property without type' => [
  216. '<?php class Foo { static $bar; }',
  217. '<?php class Foo { STATIC $bar; }',
  218. ];
  219. }
  220. /**
  221. * @dataProvider provideFix80Cases
  222. *
  223. * @requires PHP 8.0
  224. */
  225. public function testFix80(string $expected, string $input): void
  226. {
  227. $this->doTest($expected, $input);
  228. }
  229. /**
  230. * @return iterable<int|string, array{string, string}>
  231. */
  232. public static function provideFix80Cases(): iterable
  233. {
  234. yield [
  235. '<?php class T { public function Foo(object $A): static {}}',
  236. '<?php class T { public function Foo(object $A): StatiC {}}',
  237. ];
  238. yield [
  239. '<?php class T { public function Foo(object $A): ?static {}}',
  240. '<?php class T { public function Foo(object $A): ?StatiC {}}',
  241. ];
  242. yield [
  243. '<?php class T { public function Foo(mixed $A): mixed {}}',
  244. '<?php class T { public function Foo(Mixed $A): MIXED {}}',
  245. ];
  246. yield 'mixed in arrow function' => [
  247. '<?php return fn (mixed $c): mixed => 1;',
  248. '<?php return fn (MiXeD $c): MIXED => 1;',
  249. ];
  250. yield [
  251. '<?php function foo(int|bool $x) {}',
  252. '<?php function foo(INT|BOOL $x) {}',
  253. ];
  254. yield [
  255. '<?php function foo(int | bool $x) {}',
  256. '<?php function foo(INT | BOOL $x) {}',
  257. ];
  258. yield [
  259. '<?php function foo(): int|bool {}',
  260. '<?php function foo(): INT|BOOL {}',
  261. ];
  262. yield 'return type string|false' => [
  263. '<?php function foo(): string|false {}',
  264. '<?php function foo(): string|FALSE {}',
  265. ];
  266. yield 'return type string|null' => [
  267. '<?php function foo(): string|null {}',
  268. '<?php function foo(): string|NULL {}',
  269. ];
  270. yield 'union types in arrow function' => [
  271. '<?php return fn (string|null $c): int|null => 1;',
  272. '<?php return fn (string|NULL $c): INT|NULL => 1;',
  273. ];
  274. yield 'union Types' => [
  275. '<?php $a = new class {
  276. private null|int|bool $a4 = false;
  277. };',
  278. '<?php $a = new class {
  279. private NULL|INT|BOOL $a4 = false;
  280. };',
  281. ];
  282. }
  283. /**
  284. * @dataProvider provideFix81Cases
  285. *
  286. * @requires PHP 8.1
  287. */
  288. public function testFix81(string $expected, string $input): void
  289. {
  290. $this->doTest($expected, $input);
  291. }
  292. /**
  293. * @return iterable<string, array{string, string}>
  294. */
  295. public static function provideFix81Cases(): iterable
  296. {
  297. yield 'return type `never`' => [
  298. '<?php class T { public function Foo(object $A): never {die;}}',
  299. '<?php class T { public function Foo(object $A): NEVER {die;}}',
  300. ];
  301. yield 'class readonly property' => [
  302. '<?php class Z {
  303. private readonly array $ax;
  304. };',
  305. '<?php class Z {
  306. private readonly ARRAY $ax;
  307. };',
  308. ];
  309. }
  310. /**
  311. * @dataProvider provideFix82Cases
  312. *
  313. * @requires PHP 8.2
  314. */
  315. public function testFix82(string $expected, string $input): void
  316. {
  317. $this->doTest($expected, $input);
  318. }
  319. /**
  320. * @return iterable<string, array{string, string}>
  321. */
  322. public static function provideFix82Cases(): iterable
  323. {
  324. yield 'disjunctive normal form types in arrow function' => [
  325. '<?php return fn ((A&B)|C|null $c): (X&Y)|Z|null => 1;',
  326. '<?php return fn ((A&B)|C|Null $c): (X&Y)|Z|NULL => 1;',
  327. ];
  328. yield 'iterable: disjunctive normal form types in arrow function' => [
  329. '<?php return fn (iterable|C|null $c): (X&Y)|Z|null => 1;',
  330. '<?php return fn (ITERABLE|C|Null $c): (X&Y)|Z|NULL => 1;',
  331. ];
  332. foreach (['true', 'false', 'null'] as $type) {
  333. yield \sprintf('standalone type `%s` in class method', $type) => [
  334. \sprintf('<?php class T { public function Foo(%s $A): %1$s {return $A;}}', $type),
  335. \sprintf('<?php class T { public function Foo(%s $A): %1$s {return $A;}}', strtoupper($type)),
  336. ];
  337. yield \sprintf('standalone type `%s` in function', $type) => [
  338. \sprintf('<?php function Foo(%s $A): %1$s {return $A;}', $type),
  339. \sprintf('<?php function Foo(%s $A): %1$s {return $A;}', strtoupper($type)),
  340. ];
  341. yield \sprintf('standalone type `%s` in closure', $type) => [
  342. \sprintf('<?php array_filter([], function (%s $A): %1$s {return $A;});', $type),
  343. \sprintf('<?php array_filter([], function (%s $A): %1$s {return $A;});', strtoupper($type)),
  344. ];
  345. yield \sprintf('standalone type `%s` in arrow function', $type) => [
  346. \sprintf('<?php array_filter([], fn (%s $A): %1$s => $A);', $type),
  347. \sprintf('<?php array_filter([], fn (%s $A): %1$s => $A);', strtoupper($type)),
  348. ];
  349. }
  350. yield 'intersection Types' => [
  351. '<?php $a = new class {
  352. private (A&B)|int|D $d5;
  353. private (A\STRING\B&B\INT\C)|int|(A&B) $e6;
  354. };',
  355. '<?php $a = new class {
  356. private (A&B)|INT|D $d5;
  357. private (A\STRING\B&B\INT\C)|int|(A&B) $e6;
  358. };',
  359. ];
  360. }
  361. /**
  362. * @dataProvider provideFix83Cases
  363. *
  364. * @requires PHP 8.3
  365. */
  366. public function testFix83(string $expected, ?string $input = null): void
  367. {
  368. $this->doTest($expected, $input);
  369. }
  370. /**
  371. * @return iterable<string, array{0: string, 1?: string}>
  372. */
  373. public static function provideFix83Cases(): iterable
  374. {
  375. yield 'simple case' => [
  376. '<?php
  377. class Foo
  378. {
  379. const int FOO = 6;
  380. }
  381. ',
  382. '<?php
  383. class Foo
  384. {
  385. const INT FOO = 6;
  386. }
  387. ',
  388. ];
  389. yield 'single types' => [
  390. '<?php
  391. class Foo
  392. {
  393. const int SOME_INT = 3;
  394. const array SOME_ARRAY = [7];
  395. const float SOME_FLOAT = 1.23;
  396. const iterable SOME_ITERABLE = [1, 2];
  397. const mixed SOME_MIXED = 1;
  398. const null SOME_NULL = NULL;
  399. const string SOME_STRING = "X";
  400. }
  401. ',
  402. '<?php
  403. class Foo
  404. {
  405. const INT SOME_INT = 3;
  406. const ARRAY SOME_ARRAY = [7];
  407. const Float SOME_FLOAT = 1.23;
  408. const ITERABLE SOME_ITERABLE = [1, 2];
  409. const MIXED SOME_MIXED = 1;
  410. const NULL SOME_NULL = NULL;
  411. const STRING SOME_STRING = "X";
  412. }
  413. ',
  414. ];
  415. yield 'nullables `self`, `parent` and `object`' => [
  416. '<?php
  417. class Foo extends FooParent
  418. {
  419. const ?object SOME_OBJECT = NULL;
  420. const ?parent SOME_PARENT = NULL;
  421. const ?self SOME_SELF = NULL;
  422. const ?int/* x */A/* y */= 3;
  423. const ?BAR B = null;
  424. const ?BAR C = null;
  425. const ?\BAR D = null;
  426. const ?\INT\B E = null;
  427. public const ?INT\A/* x */X=C;
  428. }
  429. ',
  430. '<?php
  431. class Foo extends FooParent
  432. {
  433. const ?OBJECT SOME_OBJECT = NULL;
  434. const ?PARENT SOME_PARENT = NULL;
  435. const ?Self SOME_SELF = NULL;
  436. const ?INT/* x */A/* y */= 3;
  437. const ?BAR B = null;
  438. const ?BAR C = null;
  439. const ?\BAR D = null;
  440. const ?\INT\B E = null;
  441. public const ?INT\A/* x */X=C;
  442. }
  443. ',
  444. ];
  445. yield 'simple `|`' => [
  446. '<?php class Foo1 {
  447. const D|float BAR = 1.0;
  448. }',
  449. '<?php class Foo1 {
  450. const D|Float BAR = 1.0;
  451. }',
  452. ];
  453. yield 'multiple `|`' => [
  454. '<?php class Foo2 {
  455. const int|array|null|A\B|\C\D|float BAR_1 = NULL;
  456. }',
  457. '<?php class Foo2 {
  458. const INT|ARRAY|NULL|A\B|\C\D|FLOAT BAR_1 = NULL;
  459. }',
  460. ];
  461. yield 'handle mix of `|` and `(X&Y)`' => [
  462. '<?php
  463. class Foo extends FooParent
  464. {
  465. private const Z|A\S\T\R|int|float|iterable SOMETHING = 123;
  466. protected const \A\B|(Foo & Stringable )|null|\X D = null;
  467. public const Foo&Stringable G = V::A;
  468. }
  469. ',
  470. '<?php
  471. class Foo extends FooParent
  472. {
  473. private const Z|A\S\T\R|INT|FLOAT|ITERABLE SOMETHING = 123;
  474. protected const \A\B|(Foo & Stringable )|NULL|\X D = null;
  475. public const Foo&Stringable G = V::A;
  476. }
  477. ',
  478. ];
  479. yield 'interface, nullable int' => [
  480. '<?php interface SomeInterface {
  481. const ?int FOO = 1;
  482. }',
  483. '<?php interface SomeInterface {
  484. const ?INT FOO = 1;
  485. }',
  486. ];
  487. yield 'trait, string' => [
  488. '<?php trait T {
  489. const string TEST = E::TEST;
  490. }',
  491. '<?php trait T {
  492. const STRING TEST = E::TEST;
  493. }',
  494. ];
  495. yield 'enum, int' => [
  496. '<?php enum E: string {
  497. case Hearts = "H";
  498. const int TEST = 789;
  499. const self A = self::Hearts;
  500. const static B = self::Hearts;
  501. }',
  502. '<?php enum E: string {
  503. case Hearts = "H";
  504. const INT TEST = 789;
  505. const SELF A = self::Hearts;
  506. const STATIC B = self::Hearts;
  507. }',
  508. ];
  509. yield 'do not fix' => [
  510. '<?php class Foo {
  511. PUBLIC CONST FOO&STRINGABLE G = A::B;
  512. }
  513. CONST A = 1;',
  514. ];
  515. }
  516. }