Skip to content

Commit ebe06fa

Browse files
authored
Merge pull request #543 from ergebnis/feature/union-type
Enhancement: Add support for nullable union types
2 parents cdb4290 + eb91113 commit ebe06fa

File tree

24 files changed

+344
-10
lines changed

24 files changed

+344
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ For a full diff see [`1.0.0...main`][1.0.0...main].
1212

1313
- Added `methodsAllowedToUseContainerTypeDeclarations` parameter to allow configuring a list of method names that are allowed to have container parameter type declarations ([#541), by [@localheinz]
1414
- Allowed disabling rules ([#542), by [@localheinz]
15+
- Added support for nullable union types ([#543), by [@localheinz]
1516

1617
### Changed
1718

@@ -470,6 +471,7 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
470471
[#540]: https://github.com/ergebnis/phpstan-rules/pull/540
471472
[#541]: https://github.com/ergebnis/phpstan-rules/pull/541
472473
[#542]: https://github.com/ergebnis/phpstan-rules/pull/542
474+
[#543]: https://github.com/ergebnis/phpstan-rules/pull/543
473475

474476
[@enumag]: https://github.com/enumag
475477
[@ergebnis]: https://github.com/ergebnis

infection.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"logs": {
44
"text": ".build/infection/infection-log.txt"
55
},
6-
"minCoveredMsi": 84,
7-
"minMsi": 77,
6+
"minCoveredMsi": 81,
7+
"minMsi": 74,
88
"phpUnit": {
99
"configDir": "test\/Integration"
1010
},

src/Closures/NoNullableReturnTypeDeclarationRule.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,35 @@ public function processNode(
3737
));
3838
}
3939

40-
if (!$node->getReturnType() instanceof Node\NullableType) {
40+
if (!self::hasNullableReturnType($node)) {
4141
return [];
4242
}
4343

4444
return [
4545
'Closure has a nullable return type declaration.',
4646
];
4747
}
48+
49+
private static function hasNullableReturnType(Node\Expr\Closure $node): bool
50+
{
51+
$returnType = $node->getReturnType();
52+
53+
if ($returnType instanceof Node\NullableType) {
54+
return true;
55+
}
56+
57+
if ($returnType instanceof Node\UnionType) {
58+
foreach ($returnType->types as $type) {
59+
if (!$type instanceof Node\Identifier) {
60+
continue;
61+
}
62+
63+
if ('null' === $type->toString()) {
64+
return true;
65+
}
66+
}
67+
}
68+
69+
return false;
70+
}
4871
}

src/Closures/NoParameterWithNullableTypeDeclarationRule.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function processNode(
4242
}
4343

4444
$params = \array_filter($node->params, static function (Node\Param $node): bool {
45-
return $node->type instanceof Node\NullableType;
45+
return self::isNullable($node);
4646
});
4747

4848
if (0 === \count($params)) {
@@ -62,4 +62,27 @@ public function processNode(
6262
);
6363
}, $params);
6464
}
65+
66+
private static function isNullable(Node\Param $node): bool
67+
{
68+
if ($node->type instanceof Node\NullableType) {
69+
return true;
70+
}
71+
72+
if ($node->type instanceof Node\UnionType) {
73+
foreach ($node->type->types as $type) {
74+
if (!$type instanceof Node\Identifier) {
75+
continue;
76+
}
77+
78+
if ('null' !== $type->toString()) {
79+
continue;
80+
}
81+
82+
return true;
83+
}
84+
}
85+
86+
return false;
87+
}
6588
}

src/Functions/NoNullableReturnTypeDeclarationRule.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function processNode(
4141
return [];
4242
}
4343

44-
if (!$node->getReturnType() instanceof Node\NullableType) {
44+
if (!self::hasNullableReturnType($node)) {
4545
return [];
4646
}
4747

@@ -52,4 +52,27 @@ public function processNode(
5252
),
5353
];
5454
}
55+
56+
private static function hasNullableReturnType(Node\Stmt\Function_ $node): bool
57+
{
58+
$returnType = $node->getReturnType();
59+
60+
if ($returnType instanceof Node\NullableType) {
61+
return true;
62+
}
63+
64+
if ($returnType instanceof Node\UnionType) {
65+
foreach ($returnType->types as $type) {
66+
if (!$type instanceof Node\Identifier) {
67+
continue;
68+
}
69+
70+
if ('null' === $type->toString()) {
71+
return true;
72+
}
73+
}
74+
}
75+
76+
return false;
77+
}
5578
}

src/Functions/NoParameterWithNullableTypeDeclarationRule.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function processNode(
4242
}
4343

4444
$params = \array_filter($node->params, static function (Node\Param $node): bool {
45-
return $node->type instanceof Node\NullableType;
45+
return self::isNullable($node);
4646
});
4747

4848
if (0 === \count($params)) {
@@ -65,4 +65,27 @@ public function processNode(
6565
);
6666
}, $params);
6767
}
68+
69+
private static function isNullable(Node\Param $node): bool
70+
{
71+
if ($node->type instanceof Node\NullableType) {
72+
return true;
73+
}
74+
75+
if ($node->type instanceof Node\UnionType) {
76+
foreach ($node->type->types as $type) {
77+
if (!$type instanceof Node\Identifier) {
78+
continue;
79+
}
80+
81+
if ('null' !== $type->toString()) {
82+
continue;
83+
}
84+
85+
return true;
86+
}
87+
}
88+
89+
return false;
90+
}
6891
}

src/Methods/NoNullableReturnTypeDeclarationRule.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ public function processNode(
3838
));
3939
}
4040

41-
$returnType = $node->getReturnType();
42-
43-
if (!$returnType instanceof Node\NullableType) {
41+
if (!self::hasNullableReturnType($node)) {
4442
return [];
4543
}
4644

@@ -64,4 +62,27 @@ public function processNode(
6462
),
6563
];
6664
}
65+
66+
private static function hasNullableReturnType(Node\Stmt\ClassMethod $node): bool
67+
{
68+
$returnType = $node->getReturnType();
69+
70+
if ($returnType instanceof Node\NullableType) {
71+
return true;
72+
}
73+
74+
if ($returnType instanceof Node\UnionType) {
75+
foreach ($returnType->types as $type) {
76+
if (!$type instanceof Node\Identifier) {
77+
continue;
78+
}
79+
80+
if ('null' === $type->toString()) {
81+
return true;
82+
}
83+
}
84+
}
85+
86+
return false;
87+
}
6788
}

src/Methods/NoParameterWithNullableTypeDeclarationRule.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function processNode(
4343
}
4444

4545
$params = \array_filter($node->params, static function (Node\Param $node): bool {
46-
return $node->type instanceof Node\NullableType;
46+
return self::isNullable($node);
4747
});
4848

4949
if (0 === \count($params)) {
@@ -88,4 +88,27 @@ public function processNode(
8888
);
8989
}, $params);
9090
}
91+
92+
private static function isNullable(Node\Param $node): bool
93+
{
94+
if ($node->type instanceof Node\NullableType) {
95+
return true;
96+
}
97+
98+
if ($node->type instanceof Node\UnionType) {
99+
foreach ($node->type->types as $type) {
100+
if (!$type instanceof Node\Identifier) {
101+
continue;
102+
}
103+
104+
if ('null' !== $type->toString()) {
105+
continue;
106+
}
107+
108+
return true;
109+
}
110+
}
111+
112+
return false;
113+
}
91114
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ergebnis\PHPStan\Rules\Test\Fixture\Closures\NoNullableReturnTypeDeclarationRule\Failure;
6+
7+
$foo = function (): string|null {
8+
return 'Hello';
9+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ergebnis\PHPStan\Rules\Test\Fixture\Closures\NoParameterWithNullDefaultValueRule\Success;
6+
7+
$foo = function (string|null $bar) {
8+
return $bar;
9+
};

0 commit comments

Comments
 (0)