Skip to content
Closed
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
15 changes: 15 additions & 0 deletions src/Rules/RuleLevelHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Type\CallableType;
use PHPStan\Type\ClosureType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Generic\TemplateMixedType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\MixedType;
Expand Down Expand Up @@ -126,6 +127,20 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):
);
}

if ($acceptedType instanceof GenericObjectType) {
if (!$this->checkNullables && $acceptingType instanceof GenericObjectType) {
return $acceptedType->traverseSimultaneously($acceptingType, static function (Type $acceptedInner, Type $acceptingInner): Type {
if (TypeCombinator::containsNull($acceptingInner)) {
return $acceptedInner;
}

return TypeCombinator::removeNull($acceptedInner);
});
}

return $acceptedType;
}

if (
!$this->checkNullables
&& !$acceptingType instanceof NullType
Expand Down
10 changes: 9 additions & 1 deletion tests/PHPStan/Rules/Functions/ClosureReturnTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
class ClosureReturnTypeRuleTest extends RuleTestCase
{

private bool $checkNullables = true;

protected function getRule(): Rule
{
return new ClosureReturnTypeRule(new FunctionReturnTypeCheck(
new RuleLevelHelper(
self::createReflectionProvider(),
checkNullables: true,
checkNullables: $this->checkNullables,
checkThisOnly: false,
checkUnionTypes: true,
checkExplicitMixed: false,
Expand Down Expand Up @@ -149,4 +151,10 @@ public function testBug13964(): void
$this->analyse([__DIR__ . '/data/bug-13964.php'], []);
}

public function testBug12490(): void
{
$this->checkNullables = false;
$this->analyse([__DIR__ . '/data/bug-12490.php'], []);
}

}
36 changes: 36 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-12490.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php declare(strict_types = 1);

namespace Bug12490Closure;

/**
* @template T
*/
class Container
{
/** @var T */
public $value;

/**
* @param T $value
*/
public function __construct($value)
{
$this->value = $value;
}
}

class Foo
{
public function test(): void
{
/** @return Container<string|null> */
$closure = function (?string $val): Container {
return new Container($val);
};

/** @return Container<int|null> */
$closure2 = function (?int $val): Container {
return new Container($val);
};
}
}
8 changes: 8 additions & 0 deletions tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4009,4 +4009,12 @@ public function testBug10422(): void
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-10422.php'], []);
}

public function testBug12490(): void
{
$this->checkThisOnly = false;
$this->checkNullables = false;
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/data/bug-12490-call.php'], []);
}

}
10 changes: 9 additions & 1 deletion tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
class ReturnTypeRuleTest extends RuleTestCase
{

private bool $checkNullables = true;

private bool $checkExplicitMixed = false;

private bool $checkUnionTypes = true;
Expand All @@ -27,7 +29,7 @@ protected function getRule(): Rule
return new ReturnTypeRule(new FunctionReturnTypeCheck(
new RuleLevelHelper(
self::createReflectionProvider(),
checkNullables: true,
checkNullables: $this->checkNullables,
checkThisOnly: false,
checkUnionTypes: $this->checkUnionTypes,
checkExplicitMixed: $this->checkExplicitMixed,
Expand Down Expand Up @@ -1331,4 +1333,10 @@ public function testBug11430(): void
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-11430.php'], []);
}

public function testBug12490(): void
{
$this->checkNullables = false;
$this->analyse([__DIR__ . '/data/bug-12490.php'], []);
}

}
75 changes: 75 additions & 0 deletions tests/PHPStan/Rules/Methods/data/bug-12490-call.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php declare(strict_types = 1);

namespace Bug12490Call;

/**
* @template T
*/
class Container
{
/** @var T */
public $value;

/**
* @param T $value
*/
public function __construct($value)
{
$this->value = $value;
}
}

class Foo
{
/**
* @param Container<string|null> $container
*/
public function acceptsNullableString(Container $container): void
{
}

/**
* @param Container<int|null> $container
*/
public function acceptsNullableInt(Container $container): void
{
}

/**
* @param Container<float|null> $container
*/
public function acceptsNullableFloat(Container $container): void
{
}

/**
* @return Container<string|null>
*/
public function createNullableString(): Container
{
return new Container(null);
}

/**
* @return Container<int|null>
*/
public function createNullableInt(): Container
{
return new Container(null);
}

/**
* @return Container<float|null>
*/
public function createNullableFloat(): Container
{
return new Container(null);
}

public function test(): void
{
$this->acceptsNullableString($this->createNullableString());
$this->acceptsNullableInt($this->createNullableInt());
$this->acceptsNullableFloat($this->createNullableFloat());
}
}
89 changes: 89 additions & 0 deletions tests/PHPStan/Rules/Methods/data/bug-12490.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php declare(strict_types = 1);

namespace Bug12490;

/**
* @template TGet
* @template TSet
*/
class Attribute
{
/** @var (callable(mixed, array<string, mixed>): TGet)|null */
public $get;

/** @var (callable(TSet, array<string, mixed>): mixed)|null*/
public $set;

/**
* @param (callable(mixed, array<string, mixed>): TGet)|null $get
* @param (callable(TSet, array<string, mixed>): mixed)|null $set
*/
public function __construct(?callable $get = null, ?callable $set = null)
{
$this->get = $get;
$this->set = $set;
}

/**
* @template T
* @param callable(mixed, array<string, mixed>): T $get
* @return Attribute<T, never>
*/
public static function get(callable $get): self
{
return new self($get);
}
}


class Foo
{
public ?int $id = null;
public ?string $surveyable_type = null;

/**
* @return Attribute<null|string, never>
*/
protected function surveyedLink(): Attribute
{
return Attribute::get(fn () => $this->surveyable_type);
}

/** @return Attribute<null|float, never> */
protected function packageWeightCalculated(): Attribute
{
return Attribute::get(fn () => $this->id === null ? null : round(50 * .15, 2));
}

/** @return Attribute<?int, never> */
protected function durationMs(): Attribute
{
return Attribute::get(fn () => $this->id);
}
}

/**
* @template T
*/
class Container
{
/** @var T */
public $value;

/**
* @param T $value
*/
public function __construct($value)
{
$this->value = $value;
}
}

class Bar
{
/** @return Container<string> */
public function test(?string $val): Container
{
return new Container($val);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
class TypesAssignedToPropertiesRuleTest extends RuleTestCase
{

private bool $checkNullables = true;

private bool $checkExplicitMixed = false;

private bool $checkImplicitMixed = false;
Expand All @@ -22,7 +24,7 @@ protected function getRule(): Rule
return new TypesAssignedToPropertiesRule(
new RuleLevelHelper(
self::createReflectionProvider(),
checkNullables: true,
checkNullables: $this->checkNullables,
checkThisOnly: false,
checkUnionTypes: true,
checkExplicitMixed: $this->checkExplicitMixed,
Expand Down Expand Up @@ -1066,4 +1068,10 @@ public function testBug10924(): void
$this->analyse([__DIR__ . '/../Methods/data/bug-10924.php'], []);
}

public function testBug12490(): void
{
$this->checkNullables = false;
$this->analyse([__DIR__ . '/data/bug-12490.php'], []);
}

}
39 changes: 39 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-12490.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php declare(strict_types = 1);

namespace Bug12490Property;

/**
* @template T
*/
class Container
{
/** @var T */
public $value;

/**
* @param T $value
*/
public function __construct($value)
{
$this->value = $value;
}
}

class Foo
{
/** @var Container<string|null> */
public Container $stringContainer;

/** @var Container<int|null> */
public Container $intContainer;

/**
* @param Container<string|null> $s
* @param Container<int|null> $i
*/
public function test(Container $s, Container $i): void
{
$this->stringContainer = $s;
$this->intContainer = $i;
}
}
Loading