Skip to content

Commit

Permalink
Merge pull request #543 from ergebnis/feature/union-type
Browse files Browse the repository at this point in the history
Enhancement: Add support for nullable union types
  • Loading branch information
localheinz authored May 1, 2023
2 parents cdb4290 + eb91113 commit ebe06fa
Show file tree
Hide file tree
Showing 24 changed files with 344 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ For a full diff see [`1.0.0...main`][1.0.0...main].

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

### Changed

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

[@enumag]: https://github.com/enumag
[@ergebnis]: https://github.com/ergebnis
Expand Down
4 changes: 2 additions & 2 deletions infection.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"logs": {
"text": ".build/infection/infection-log.txt"
},
"minCoveredMsi": 84,
"minMsi": 77,
"minCoveredMsi": 81,
"minMsi": 74,
"phpUnit": {
"configDir": "test\/Integration"
},
Expand Down
25 changes: 24 additions & 1 deletion src/Closures/NoNullableReturnTypeDeclarationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,35 @@ public function processNode(
));
}

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

return [
'Closure has a nullable return type declaration.',
];
}

private static function hasNullableReturnType(Node\Expr\Closure $node): bool
{
$returnType = $node->getReturnType();

if ($returnType instanceof Node\NullableType) {
return true;
}

if ($returnType instanceof Node\UnionType) {
foreach ($returnType->types as $type) {
if (!$type instanceof Node\Identifier) {
continue;
}

if ('null' === $type->toString()) {
return true;
}
}
}

return false;
}
}
25 changes: 24 additions & 1 deletion src/Closures/NoParameterWithNullableTypeDeclarationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function processNode(
}

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

if (0 === \count($params)) {
Expand All @@ -62,4 +62,27 @@ public function processNode(
);
}, $params);
}

private static function isNullable(Node\Param $node): bool
{
if ($node->type instanceof Node\NullableType) {
return true;
}

if ($node->type instanceof Node\UnionType) {
foreach ($node->type->types as $type) {
if (!$type instanceof Node\Identifier) {
continue;
}

if ('null' !== $type->toString()) {
continue;
}

return true;
}
}

return false;
}
}
25 changes: 24 additions & 1 deletion src/Functions/NoNullableReturnTypeDeclarationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function processNode(
return [];
}

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

Expand All @@ -52,4 +52,27 @@ public function processNode(
),
];
}

private static function hasNullableReturnType(Node\Stmt\Function_ $node): bool
{
$returnType = $node->getReturnType();

if ($returnType instanceof Node\NullableType) {
return true;
}

if ($returnType instanceof Node\UnionType) {
foreach ($returnType->types as $type) {
if (!$type instanceof Node\Identifier) {
continue;
}

if ('null' === $type->toString()) {
return true;
}
}
}

return false;
}
}
25 changes: 24 additions & 1 deletion src/Functions/NoParameterWithNullableTypeDeclarationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function processNode(
}

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

if (0 === \count($params)) {
Expand All @@ -65,4 +65,27 @@ public function processNode(
);
}, $params);
}

private static function isNullable(Node\Param $node): bool
{
if ($node->type instanceof Node\NullableType) {
return true;
}

if ($node->type instanceof Node\UnionType) {
foreach ($node->type->types as $type) {
if (!$type instanceof Node\Identifier) {
continue;
}

if ('null' !== $type->toString()) {
continue;
}

return true;
}
}

return false;
}
}
27 changes: 24 additions & 3 deletions src/Methods/NoNullableReturnTypeDeclarationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ public function processNode(
));
}

$returnType = $node->getReturnType();

if (!$returnType instanceof Node\NullableType) {
if (!self::hasNullableReturnType($node)) {
return [];
}

Expand All @@ -64,4 +62,27 @@ public function processNode(
),
];
}

private static function hasNullableReturnType(Node\Stmt\ClassMethod $node): bool
{
$returnType = $node->getReturnType();

if ($returnType instanceof Node\NullableType) {
return true;
}

if ($returnType instanceof Node\UnionType) {
foreach ($returnType->types as $type) {
if (!$type instanceof Node\Identifier) {
continue;
}

if ('null' === $type->toString()) {
return true;
}
}
}

return false;
}
}
25 changes: 24 additions & 1 deletion src/Methods/NoParameterWithNullableTypeDeclarationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function processNode(
}

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

if (0 === \count($params)) {
Expand Down Expand Up @@ -88,4 +88,27 @@ public function processNode(
);
}, $params);
}

private static function isNullable(Node\Param $node): bool
{
if ($node->type instanceof Node\NullableType) {
return true;
}

if ($node->type instanceof Node\UnionType) {
foreach ($node->type->types as $type) {
if (!$type instanceof Node\Identifier) {
continue;
}

if ('null' !== $type->toString()) {
continue;
}

return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Closures\NoNullableReturnTypeDeclarationRule\Failure;

$foo = function (): string|null {
return 'Hello';
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Closures\NoParameterWithNullDefaultValueRule\Success;

$foo = function (string|null $bar) {
return $bar;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Functions\NoNullableReturnTypeDeclarationRule\Failure;

function foo(): string|null
{
return 'Hello';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Functions\NoParameterWithNullableTypeDeclarationRule\Failure;

function foo(string|null $bar)
{
return $bar;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoNullableReturnTypeDeclarationRule\Failure;

final class MethodInAnonymousClassWithNullableReturnTypeDeclaration
{
public function foo()
{
return new class() {
public function toString(): string|null
{
return 'Hello';
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoNullableReturnTypeDeclarationRule\Failure;

final class MethodInClassWithNullableUnionReturnTypeDeclaration
{
public function toString(): string|null
{
return 'Hello';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoNullableReturnTypeDeclarationRule\Failure;

interface MethodInInterfaceWithNullableUnionReturnTypeDeclaration
{
public function toString(): string|null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoParameterWithNullableTypeDeclarationRule\Failure;

final class MethodInClassWithParameterWithNullableUnionTypeDeclaration
{
public function foo(string|null $bar)
{
return $bar;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoParameterWithNullableTypeDeclarationRule\Failure;

interface MethodInInterfaceWithParameterWithNullableUnionTypeDeclaration
{
public function foo(string|null $bar);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoParameterWithNullableTypeDeclarationRule\Success;

new class() {
public function foo(string|null $bar)
{
return $bar;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ public static function provideCasesWhereAnalysisShouldFail(): iterable
7,
],
],
'closure-with-nullable-union-return-type-declaration' => [
__DIR__ . '/../../Fixture/Closures/NoNullableReturnTypeDeclarationRule/Failure/closure-with-nullable-union-type-return-type-declaration.php',
[
'Closure has a nullable return type declaration.',
7,
],
],
];

foreach ($paths as $description => [$path, $error]) {
Expand Down
Loading

0 comments on commit ebe06fa

Please sign in to comment.