1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Fix #2242 - warn when using mutable dependencies

This commit is contained in:
Matthew Brown 2020-02-22 10:04:38 -05:00
parent a50826ddb0
commit a706f4d722
5 changed files with 126 additions and 0 deletions

View File

@ -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" />

View File

@ -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.

View File

@ -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);

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class MutableDependency extends CodeIssue
{
const ERROR_LEVEL = 1;
}

View File

@ -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'
],
];
}
}