-
Notifications
You must be signed in to change notification settings - Fork 242
Intersection types #637
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
base: master
Are you sure you want to change the base?
Intersection types #637
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,9 @@ | |
use Prophecy\Doubler\Generator\Node\ClassNode; | ||
use Prophecy\Doubler\Generator\Node\MethodNode; | ||
use Prophecy\Doubler\Generator\Node\ReturnTypeNode; | ||
use Prophecy\Doubler\Generator\Node\Type\IntersectionType; | ||
use Prophecy\Doubler\Generator\Node\Type\SimpleType; | ||
use Prophecy\Doubler\Generator\Node\Type\UnionType; | ||
|
||
class ClassCodeGeneratorSpec extends ObjectBehavior | ||
{ | ||
|
@@ -115,10 +118,10 @@ class CustomClass extends \RuntimeException implements \Prophecy\Doubler\Generat | |
public $name; | ||
private $email; | ||
|
||
public static function getName(array $fullname, \ReflectionClass $class, object $instance): ?string { | ||
public static function getName(array $fullname, \ReflectionClass $class, object $instance): string|null { | ||
return $this->name; | ||
} | ||
protected function getEmail(?string $default = '[email protected]') { | ||
protected function getEmail(string|null $default = '[email protected]') { | ||
return $this->email; | ||
} | ||
public function &getRefValue( $refValue): string { | ||
|
@@ -271,7 +274,7 @@ function it_overrides_properly_methods_with_args_passed_by_reference( | |
namespace { | ||
class CustomClass extends \RuntimeException implements \Prophecy\Doubler\Generator\MirroredInterface { | ||
|
||
public function getName(?array &$fullname = NULL) { | ||
public function getName(array|null &$fullname = NULL) { | ||
return $this->name; | ||
} | ||
|
||
|
@@ -310,6 +313,47 @@ public function foo(): int|string|null { | |
|
||
} | ||
|
||
} | ||
} | ||
PHP; | ||
$expected = strtr($expected, array("\r\n" => "\n", "\r" => "\n")); | ||
|
||
$code->shouldBe($expected); | ||
} | ||
|
||
function it_generates_proper_code_for_intersection_return_types( | ||
ClassNode $class, | ||
MethodNode $method | ||
) { | ||
$class->getParentClass()->willReturn('stdClass'); | ||
$class->getInterfaces()->willReturn([]); | ||
$class->getProperties()->willReturn([]); | ||
$class->getMethods()->willReturn(array($method)); | ||
$class->isReadOnly()->willReturn(false); | ||
|
||
$method->getName()->willReturn('foo'); | ||
$method->getVisibility()->willReturn('public'); | ||
$method->isStatic()->willReturn(false); | ||
$method->getArguments()->willReturn([]); | ||
$method->getReturnTypeNode()->willReturn(new ReturnTypeNode( | ||
new UnionType([ | ||
new IntersectionType([new SimpleType('Foo'), new SimpleType('Bar')]), | ||
new SimpleType('string'), | ||
]) | ||
)); | ||
$method->returnsReference()->willReturn(false); | ||
$method->getCode()->willReturn(''); | ||
|
||
$code = $this->generate('CustomClass', $class); | ||
|
||
$expected = <<<'PHP' | ||
namespace { | ||
class CustomClass extends \stdClass implements { | ||
|
||
public function foo(): \Foo&\Bar|string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a valid DNF type in the code: https://3v4l.org/aivA5 (I just removed the |
||
|
||
} | ||
|
||
} | ||
} | ||
PHP; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
namespace spec\Prophecy\Doubler\Generator\Node\Type; | ||
|
||
use PhpSpec\ObjectBehavior; | ||
use Prophecy\Doubler\Generator\Node\Type\SimpleType; | ||
use Prophecy\Doubler\Generator\Node\Type\TypeInterface; | ||
use Prophecy\Doubler\Generator\Node\Type\UnionType; | ||
use Prophecy\Exception\Doubler\DoubleException; | ||
|
||
class IntersectionTypeSpec extends ObjectBehavior | ||
{ | ||
function let(): void | ||
{ | ||
$this->beConstructedWith([ | ||
new SimpleType('Foo'), | ||
new SimpleType('Bar'), | ||
]); | ||
} | ||
|
||
function it_should_implement_type_union(): void | ||
{ | ||
$this->shouldImplement(TypeInterface::class); | ||
} | ||
|
||
function it_should_throw_double_exception_for_builtin_types() | ||
{ | ||
$this->beConstructedWith([ | ||
new SimpleType('string'), | ||
new SimpleType('Foo'), | ||
]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
|
||
function it_should_throw_double_exception_if_less_than_2_types_provided() | ||
{ | ||
$this->beConstructedWith([ | ||
new SimpleType('Bar'), | ||
]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
|
||
function it_should_throw_double_exception_if_union_type_given(): void | ||
{ | ||
$this->beConstructedWith([ | ||
new SimpleType('Bar'), | ||
new UnionType([new SimpleType('Foo'), new SimpleType('Baz')]), | ||
]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php | ||
|
||
namespace spec\Prophecy\Doubler\Generator\Node\Type; | ||
|
||
use PhpSpec\ObjectBehavior; | ||
use Prophecy\Doubler\Generator\Node\Type\TypeInterface; | ||
|
||
class SimpleTypeSpec extends ObjectBehavior | ||
{ | ||
function let(): void | ||
{ | ||
$this->beConstructedWith('string'); | ||
} | ||
|
||
function it_implements_type_interface(): void | ||
{ | ||
$this->shouldImplement(TypeInterface::class); | ||
} | ||
|
||
function it_is_stringable(): void | ||
{ | ||
$this->beConstructedWith('int'); | ||
$this->getType()->shouldReturn('int'); | ||
$this->__toString()->shouldReturn('int'); | ||
} | ||
|
||
function it_prefix_namespace_with_antislash(): void | ||
{ | ||
$this->beConstructedWith('Prophecy\\Doubler\\Generator\\Node\\Type\\SimpleType'); | ||
$this->getType()->shouldReturn('\\Prophecy\\Doubler\\Generator\\Node\\Type\\SimpleType'); | ||
$this->isBuiltin()->shouldReturn(false); | ||
} | ||
|
||
function it_resolves_builtin_aliases(): void | ||
{ | ||
$this->beConstructedWith('double'); | ||
$this->getType()->shouldReturn('float'); | ||
$this->isBuiltin()->shouldReturn(true); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?php | ||
|
||
namespace spec\Prophecy\Doubler\Generator\Node\Type; | ||
|
||
use PhpSpec\ObjectBehavior; | ||
use Prophecy\Doubler\Generator\Node\Type\IntersectionType; | ||
use Prophecy\Doubler\Generator\Node\Type\SimpleType; | ||
use Prophecy\Doubler\Generator\Node\Type\TypeInterface; | ||
use Prophecy\Doubler\Generator\Node\Type\UnionType; | ||
use Prophecy\Exception\Doubler\DoubleException; | ||
|
||
class UnionTypeSpec extends ObjectBehavior | ||
{ | ||
function let(): void | ||
{ | ||
$this->beConstructedWith([ | ||
new SimpleType('int'), | ||
new SimpleType('string'), | ||
]); | ||
} | ||
function it_implements_type_interface(): void | ||
{ | ||
$this->shouldImplement(TypeInterface::class); | ||
} | ||
|
||
function it_throws_double_exception_when_union_type_given(): void | ||
{ | ||
$this->beConstructedWith([ | ||
new UnionType([new SimpleType('int'), new SimpleType('string')]), | ||
new SimpleType('bool'), | ||
]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
|
||
function it_throws_double_exception_when_types_duplicated(): void | ||
{ | ||
$this->beConstructedWith([new SimpleType('string'), new SimpleType('string')]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
|
||
function it_throws_double_exception_when_union_with_void(): void | ||
{ | ||
$this->beConstructedWith([new SimpleType('void'), new SimpleType('string')]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
|
||
function it_throws_double_exception_when_union_with_never(): void | ||
{ | ||
$this->beConstructedWith([new SimpleType('never'), new SimpleType('string')]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
|
||
function it_throws_double_exception_when_union_with_mixed(): void | ||
{ | ||
$this->beConstructedWith([new SimpleType('mixed'), new SimpleType('string')]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
|
||
function it_throws_double_exception_when_union_with_only_one_type(): void | ||
{ | ||
$this->beConstructedWith([new SimpleType('string')]); | ||
$this->shouldThrow(DoubleException::class)->duringInstantiation(); | ||
} | ||
|
||
function it_return_array_of_its_types(): void | ||
{ | ||
$this->getTypes()->shouldBeLike([ | ||
new SimpleType('int'), | ||
new SimpleType('string'), | ||
]); | ||
} | ||
|
||
function it_should_accept_simple_type_and_intersection() | ||
{ | ||
$type1 = new SimpleType('string'); | ||
$type2 = new IntersectionType([new SimpleType('A'), new SimpleType('B')]); | ||
$this->beConstructedWith([$type1, $type2]); | ||
|
||
$this->has($type1)->shouldBe(true); | ||
$this->has($type2)->shouldBe(true); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest having separate tests for an intersection return type (what the current name says) and for a DNF type (what it does)