Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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 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,10 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):
);
}

if ($acceptedType instanceof GenericObjectType) {
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());
}
}
63 changes: 63 additions & 0 deletions tests/PHPStan/Rules/Methods/data/bug-12490.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?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);
}
}
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