mirror of
https://github.com/danog/psalm.git
synced 2024-11-29 20:28:59 +01:00
Fix #2242 - warn when using mutable dependencies
This commit is contained in:
parent
a50826ddb0
commit
a706f4d722
@ -271,6 +271,7 @@
|
||||
<xs:element name="MixedPropertyTypeCoercion" type="PropertyIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="MoreSpecificReturnType" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="MoreSpecificImplementedParamType" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="MutableDependency" type="PropertyIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="NoInterfaceProperties" type="ClassIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="NonStaticSelfCall" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="NullableReturnStatement" type="IssueHandlerType" minOccurs="0" />
|
||||
|
@ -1433,6 +1433,25 @@ function foo() : B {
|
||||
}
|
||||
```
|
||||
|
||||
### MutableDependency
|
||||
|
||||
Emitted when an immutable class inherits from a class or trait not marked immutable
|
||||
|
||||
```php
|
||||
class MutableParent {
|
||||
public int $i = 0;
|
||||
|
||||
public function increment() : void {
|
||||
$this->i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class NotReallyImmutableClass extends MutableParent {}
|
||||
```
|
||||
|
||||
### NoValue
|
||||
|
||||
Emitted when using the result of a function that never returns.
|
||||
|
@ -22,6 +22,7 @@ use Psalm\Issue\MissingConstructor;
|
||||
use Psalm\Issue\MissingImmutableAnnotation;
|
||||
use Psalm\Issue\MissingPropertyType;
|
||||
use Psalm\Issue\MissingTemplateParam;
|
||||
use Psalm\Issue\MutableDependency;
|
||||
use Psalm\Issue\OverriddenPropertyAccess;
|
||||
use Psalm\Issue\PropertyNotSetInConstructor;
|
||||
use Psalm\Issue\ReservedWord;
|
||||
@ -329,6 +330,20 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
if ($storage->mutation_free
|
||||
&& !$parent_class_storage->mutation_free
|
||||
) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MutableDependency(
|
||||
$fq_class_name . ' is marked immutable but ' . $parent_fq_class_name . ' is not',
|
||||
$code_location
|
||||
),
|
||||
$storage->suppressed_issues + $this->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
if ($codebase->store_node_types) {
|
||||
$codebase->analyzer->addNodeReference(
|
||||
$this->getFilePath(),
|
||||
@ -1433,6 +1448,18 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
if ($storage->mutation_free && !$trait_storage->mutation_free) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MutableDependency(
|
||||
$storage->name . ' is marked immutable but ' . $fq_trait_name . ' is not',
|
||||
new CodeLocation($this, $trait_name)
|
||||
),
|
||||
$storage->suppressed_issues + $this->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
$trait_file_analyzer = $project_analyzer->getFileAnalyzerForClassLike($fq_trait_name_resolved);
|
||||
$trait_node = $codebase->classlikes->getTraitNode($fq_trait_name_resolved);
|
||||
$trait_aliases = $codebase->classlikes->getTraitAliases($fq_trait_name_resolved);
|
||||
|
7
src/Psalm/Issue/MutableDependency.php
Normal file
7
src/Psalm/Issue/MutableDependency.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace Psalm\Issue;
|
||||
|
||||
class MutableDependency extends CodeIssue
|
||||
{
|
||||
const ERROR_LEVEL = 1;
|
||||
}
|
@ -333,6 +333,44 @@ class ImmutableAnnotationTest extends TestCase
|
||||
$item = new Item(5);
|
||||
new Immutable($item);',
|
||||
],
|
||||
'preventNonImmutableTraitInImmutableClass' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-immutable
|
||||
*/
|
||||
trait ImmutableTrait {
|
||||
public int $i = 0;
|
||||
|
||||
public function __construct(int $i) {
|
||||
$this->i = $i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class NotReallyImmutableClass {
|
||||
use ImmutableTrait;
|
||||
}',
|
||||
],
|
||||
'preventImmutableClassInheritingMutableParent' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class ImmutableParent {
|
||||
public int $i = 0;
|
||||
|
||||
public function __construct(int $i) {
|
||||
$this->i = $i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class ImmutableClass extends ImmutableParent {}',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -529,6 +567,40 @@ class ImmutableAnnotationTest extends TestCase
|
||||
new Immutable($item);',
|
||||
'error_message' => 'ImpureArgument',
|
||||
],
|
||||
'preventNonImmutableTraitInImmutableClass' => [
|
||||
'<?php
|
||||
trait MutableTrait {
|
||||
public int $i = 0;
|
||||
|
||||
public function increment() : void {
|
||||
$this->i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class NotReallyImmutableClass {
|
||||
use MutableTrait;
|
||||
}',
|
||||
'error_message' => 'MutableDependency'
|
||||
],
|
||||
'preventImmutableClassInheritingMutableParent' => [
|
||||
'<?php
|
||||
class MutableParent {
|
||||
public int $i = 0;
|
||||
|
||||
public function increment() : void {
|
||||
$this->i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class NotReallyImmutableClass extends MutableParent {}',
|
||||
'error_message' => 'MutableDependency'
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user