Skip to content

Added extension for internal DateTime #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ services:
tags:
- exceptionRules.dynamicConstructorThrowTypeExtension

-
class: Pepakriz\PHPStanExceptionRules\Extension\DateTimeExtension
tags:
- exceptionRules.dynamicConstructorThrowTypeExtension

-
class: Pepakriz\PHPStanExceptionRules\Rules\ThrowsPhpDocRule
arguments:
Expand Down
1 change: 0 additions & 1 deletion phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,6 @@
<rule ref="SlevomatCodingStandard.ControlStructures.EarlyExit"/>
<rule ref="SlevomatCodingStandard.ControlStructures.UselessConditionWithReturn"/>
<rule ref="SlevomatCodingStandard.Exceptions.DeadCatch"/>
<rule ref="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly" />
<rule ref="SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure"/>
<rule ref="SlevomatCodingStandard.Functions.UselessParameterDefaultValue"/>
<rule ref="SlevomatCodingStandard.Functions.StaticClosure"/>
Expand Down
74 changes: 74 additions & 0 deletions src/Extension/DateTimeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php declare(strict_types = 1);

namespace Pepakriz\PHPStanExceptionRules\Extension;

use DateTime;
use DateTimeImmutable;
use Exception;
use Pepakriz\PHPStanExceptionRules\DynamicConstructorThrowTypeExtension;
use Pepakriz\PHPStanExceptionRules\UnsupportedClassException;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\New_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\NeverType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\VoidType;
use function is_a;

class DateTimeExtension implements DynamicConstructorThrowTypeExtension
{

/**
* @throws UnsupportedClassException
*/
public function getThrowTypeFromConstructor(MethodReflection $methodReflection, New_ $newNode, Scope $scope): Type
{
if (
is_a($methodReflection->getDeclaringClass()->getName(), DateTime::class, true)
|| is_a($methodReflection->getDeclaringClass()->getName(), DateTimeImmutable::class, true)
) {
return $this->resolveThrowType($newNode->args, $scope);
}

throw new UnsupportedClassException();
}

/**
* @param Arg[] $args
*/
private function resolveThrowType(array $args, Scope $scope): Type
{
if (!isset($args[0])) {
return new VoidType();
}

$valueType = $scope->getType($args[0]->value);
if ($valueType instanceof NullType) {
return new VoidType();
}

$valueType = TypeCombinator::removeNull($valueType);
$exceptionType = new ObjectType(Exception::class);
foreach (TypeUtils::getConstantStrings($valueType) as $constantString) {
try {
new DateTime($constantString->getValue());
} catch (Exception $e) {
return $exceptionType;
}

$valueType = TypeCombinator::remove($valueType, $constantString);
}

if (!$valueType instanceof NeverType) {
return $exceptionType;
}

return new VoidType();
}

}
45 changes: 45 additions & 0 deletions tests/src/Rules/PhpInternalsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php declare(strict_types = 1);

namespace Pepakriz\PHPStanExceptionRules\Rules;

use Pepakriz\PHPStanExceptionRules\CheckedExceptionService;
use Pepakriz\PHPStanExceptionRules\DynamicThrowTypeService;
use Pepakriz\PHPStanExceptionRules\Extension\DateTimeExtension;
use Pepakriz\PHPStanExceptionRules\Extension\ReflectionExtension;
use Pepakriz\PHPStanExceptionRules\RuleTestCase;
use PHPStan\Rules\Rule;
use Throwable;

class PhpInternalsTest extends RuleTestCase
{

protected function getRule(): Rule
{
$reflectionClassExtension = new ReflectionExtension($this->createBroker());
$dateTimeExtension = new DateTimeExtension();
return new ThrowsPhpDocRule(
new CheckedExceptionService(
[
Throwable::class,
]
),
new DynamicThrowTypeService(
[],
[],
[
$reflectionClassExtension,
$dateTimeExtension,
],
[]
),
$this->createBroker(),
true
);
}

public function testPhpInternalFunctions(): void
{
$this->analyse(__DIR__ . '/data/throws-php-internal-functions.php');
}

}
8 changes: 0 additions & 8 deletions tests/src/Rules/ThrowsPhpDocRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Pepakriz\PHPStanExceptionRules\CheckedExceptionService;
use Pepakriz\PHPStanExceptionRules\DynamicThrowTypeService;
use Pepakriz\PHPStanExceptionRules\Extension\ReflectionExtension;
use Pepakriz\PHPStanExceptionRules\Rules\Data\CheckedException;
use Pepakriz\PHPStanExceptionRules\Rules\DynamicExtension\DynamicExtension;
use Pepakriz\PHPStanExceptionRules\RuleTestCase;
Expand All @@ -23,7 +22,6 @@ class ThrowsPhpDocRuleTest extends RuleTestCase
protected function getRule(): Rule
{
$dynamicExtension = new DynamicExtension();
$reflectionClassExtension = new ReflectionExtension($this->createBroker());
return new ThrowsPhpDocRule(
new CheckedExceptionService(
[
Expand All @@ -38,7 +36,6 @@ protected function getRule(): Rule
$dynamicExtension,
], [
$dynamicExtension,
$reflectionClassExtension,
], [
$dynamicExtension,
]),
Expand Down Expand Up @@ -103,9 +100,4 @@ public function testAnonymClass(): void
$this->analyse(__DIR__ . '/data/throws-anonym-class.php');
}

public function testPhpInternalFunctions(): void
{
$this->analyse(__DIR__ . '/data/throws-php-internal-functions.php');
}

}
38 changes: 37 additions & 1 deletion tests/src/Rules/data/throws-php-internal-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Pepakriz\PHPStanExceptionRules\Rules\PhpInternalFunctions;

use DateTime;
use DateTimeImmutable;
use function rand;
use ReflectionClass;
use ReflectionFunction;
use ReflectionProperty;
Expand All @@ -12,7 +15,7 @@ class Example

private $property;

public function testName(): void
public function testReflection(): void
{
new ReflectionClass(self::class);
new ReflectionClass('undefinedClass'); // error: Missing @throws ReflectionException annotation
Expand All @@ -28,5 +31,38 @@ public function testName(): void
new ReflectionZendExtension('unknownZendExtension'); // error: Missing @throws ReflectionException annotation
}

public function testDateTime(): void
{
new DateTime();
new DateTime(null);
new DateTime('2018-01-01');
new DateTime('invalid format'); // error: Missing @throws Exception annotation

new DateTimeImmutable();
new DateTimeImmutable(null);
new DateTimeImmutable('2018-01-01');
new DateTimeImmutable('invalid format'); // error: Missing @throws Exception annotation

$date1 = '2018-01-01';
if (rand(0, 1) === 0) {
$date1 = '2019-01-01';
}
new DateTime($date1);

if (rand(0, 1) === 0) {
$date2 = '2019-01-01';
} else {
$date2 = null;
}
new DateTime($date2);

if (rand(0, 1) === 0) {
$date3 = '2019-01-01';
} else {
$date3 = 123;
}
new DateTime($date3); // error: Missing @throws Exception annotation
}

}