Skip to content
Open
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
18 changes: 13 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@
all: deps test

deps:
docker run -it --rm -v ${PWD}:/app -w /app composer update --prefer-dist --verbose --no-interaction --optimize-autoloader
docker run -it --rm -v ${PWD}:/app -w /app composer update --prefer-dist --verbose --no-interaction

test:
docker run -it --rm -v ${PWD}:/app -w /app composer run-script test
$(MAKE) phpunit
$(MAKE) phpstan
$(MAKE) fmt-check

phpstan:
docker run -it --rm -v ${PWD}:/app -w /app composer run-script phpstan
docker run -it --rm -v ${PWD}:/app -w /app php:7.3-cli-alpine php -d error_reporting=-1 -d memory_limit=-1 bin/phpstan --ansi analyse

phpstan-clear-cache:
docker run -it --rm -v ${PWD}:/app -w /app php:7.3-cli-alpine php -d error_reporting=-1 -d memory_limit=-1 bin/phpstan --ansi clear-result-cache

phpunit:
docker run -it --rm -v ${PWD}:/app -w /app php:7.3-cli-alpine php -d error_reporting=-1 bin/phpunit --colors=always -c phpunit.xml

fmt-check:
docker run -it --rm -v ${PWD}:/app -w /app composer run-script phpcs
docker run -it --rm -v ${PWD}:/app -w /app php:7.3-cli-alpine php bin/phpcs --standard=./ruleset.xml --extensions=php --tab-width=4 -sp ./src ./tests

fmt:
docker run -it --rm -v ${PWD}:/app -w /app composer run-script phpcbf
docker run -it --rm -v ${PWD}:/app -w /app php:7.3-cli-alpine php bin/phpcbf --standard=./ruleset.xml --extensions=php --tab-width=4 -sp ./src ./tests
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
],
"require": {
"php": ">=7.3|^8.0",
"phpstan/phpstan": "^0.12|^1.0"
"phpstan/phpstan": "^1.0"
},
"require-dev": {
"roave/security-advisories": "dev-latest",
"ergebnis/composer-normalize": "^2.0.2",
"phpunit/phpunit": "^9.4.2",
"slevomat/coding-standard": "^6.4.1",
"squizlabs/php_codesniffer": "^3.5.0",
"bonami/collections": "^0.3.8"
"bonami/collections": "^0.4.5"
},
"config": {
"bin-dir": "bin",
Expand Down
2 changes: 1 addition & 1 deletion extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ services:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: Bonami\Collection\Phpstan\LateStaticBindingStaticMethodReturnTypeExtension
factory: Bonami\Collection\Phpstan\LateStaticBindingStaticMethodReturnTypeExtension::forMethods('Bonami\Collection\LazyList', ['fill', 'fromEmpty', 'fromArray', 'fromIterable', 'of'])
factory: Bonami\Collection\Phpstan\LateStaticBindingStaticMethodReturnTypeExtension::forMethods('Bonami\Collection\LazyList', ['fill', 'fromEmpty', 'fromIterable', 'of'])
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@

namespace Bonami\Collection\Phpstan;

use PhpParser\Node\Arg;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\ConstantType;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
use PHPStan\Type\GeneralizePrecision;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;

Expand Down Expand Up @@ -60,7 +57,6 @@ public function getTypeFromStaticMethodCall(
StaticCall $methodCall,
Scope $scope
): Type {
$declaringClassReflection = $methodReflection->getDeclaringClass();
$calledClassExpr = $methodCall->class;
if ($calledClassExpr instanceof PropertyFetch) {
$type = $scope->getType($calledClassExpr);
Expand All @@ -78,26 +74,11 @@ public function getTypeFromStaticMethodCall(
);

if ($calledOnTopLevelParent) {
if ($methodReflection->getName() === 'of') {
$arg = $methodCall->getArgs()[0];
assert($arg instanceof Arg);

$type = $scope->getType($arg->value);
$widend = $type instanceof ConstantType
? $type->generalize(GeneralizePrecision::lessSpecific())
: $type;

return new GenericObjectType(
$declaringClassReflection->getName(),
[$widend]
);
}
return new GenericObjectType(
$declaringClassReflection->getName(),
$declaringClassReflection->typeMapToList(
$declaringClassReflection->getTemplateTypeMap()->resolveToBounds()
)
);
return ParametersAcceptorSelector::selectFromArgs(
$scope,
$methodCall->getArgs(),
$methodReflection->getVariants()
)->getReturnType();
}

$calledInsideExtendedClass = $calledClassExprString === 'self';
Expand Down
18 changes: 3 additions & 15 deletions tests/Bonami/Collection/Phpstan/ArrayListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
use Bonami\Collection\Map;
use PHPUnit\Framework\TestCase;

use function PHPStan\dumpType;

class ArrayListTest extends TestCase
{
public function testFromEmptyReturnType(): void
{
/** @var ArrayList<Foo> $genericList */
$genericList = ArrayList::fromEmpty();
$this->requireArrayListOfFoo($genericList);
self::assertInstanceOf(ArrayList::class, $genericList);
Expand Down Expand Up @@ -140,21 +143,6 @@ public function testSliceReturnType(): void
self::assertInstanceOf(FooArrayList::class, $concreteList);
}

public function testWithoutNullsReturnType(): void
{
/** @var iterable<int, Foo|null> $iterable */
$iterable = [new Foo(), null];
/** @var ArrayList<Foo|null> $list */
$list = ArrayList::fromIterable($iterable);
$genericList = $list->withoutNulls();
$this->requireArrayListOfFoo($genericList->withoutNulls());
self::assertInstanceOf(ArrayList::class, $genericList);

$concreteList = FooArrayList::fromIterable($genericList)->withoutNulls();
$this->requireFooList($concreteList->withoutNulls());
self::assertInstanceOf(FooArrayList::class, $concreteList);
}

public function testMinusReturnType(): void
{
$genericList = ArrayList::fromIterable([new Foo()])->minus([new Foo()]);
Expand Down
12 changes: 1 addition & 11 deletions tests/Bonami/Collection/Phpstan/LazyListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public function testFillReturnType(): void

public function testFromEmptyReturnType(): void
{
/** @var LazyList<Foo> $genericList */
$genericList = LazyList::fromEmpty();
$this->requireLazyListOfFoo($genericList);
self::assertInstanceOf(LazyList::class, $genericList);
Expand All @@ -32,17 +33,6 @@ public function testFromEmptyReturnType(): void
self::assertInstanceOf(FooLazyList::class, $concreteList);
}

public function testFromArrayReturnType(): void
{
$genericList = LazyList::fromArray();
$this->requireLazyListOfFoo($genericList);
self::assertInstanceOf(LazyList::class, $genericList);

$concreteList = FooLazyList::fromArray([new Foo()]);
$this->requireFooList($concreteList);
self::assertInstanceOf(FooLazyList::class, $concreteList);
}

public function testFromIterableReturnType(): void
{
$genericList = LazyList::fromIterable([new Foo()]);
Expand Down
1 change: 1 addition & 0 deletions tests/Bonami/Collection/Phpstan/MapTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public function testFromOnlyReturnType(): void

public function testFromEmptyReturnType(): void
{
/** @var Map<int, Foo> $genericList */
$genericList = Map::fromEmpty();
$this->requireMapOfFoo($genericList);
self::assertInstanceOf(Map::class, $genericList);
Expand Down