mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Fix #302 - add a way to seal objects with magic properties
This commit is contained in:
parent
440db3be89
commit
a08306973a
@ -296,6 +296,10 @@ class CommentChecker
|
||||
$info->deprecated = true;
|
||||
}
|
||||
|
||||
if (isset($comments['specials']['psalm-seal-properties'])) {
|
||||
$info->sealed_properties = true;
|
||||
}
|
||||
|
||||
if (isset($comments['specials']['psalm-suppress'])) {
|
||||
/** @var string $suppress_entry */
|
||||
foreach ($comments['specials']['psalm-suppress'] as $suppress_entry) {
|
||||
|
@ -661,9 +661,9 @@ class AssignmentChecker
|
||||
if ($lhs_var_id !== '$this' &&
|
||||
MethodChecker::methodExists($project_checker, $lhs_type_part . '::__set')
|
||||
) {
|
||||
if ($var_id) {
|
||||
$class_storage = $project_checker->classlike_storage_provider->get((string)$lhs_type_part);
|
||||
$class_storage = $project_checker->classlike_storage_provider->get((string)$lhs_type_part);
|
||||
|
||||
if ($var_id) {
|
||||
if (isset($class_storage->pseudo_property_set_types['$' . $prop_name])) {
|
||||
$class_property_types[] =
|
||||
clone $class_storage->pseudo_property_set_types['$' . $prop_name];
|
||||
@ -674,7 +674,14 @@ class AssignmentChecker
|
||||
|
||||
$context->vars_in_scope[$var_id] = Type::getMixed();
|
||||
}
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If we have an explicit list of all allowed magic properties on the class, and we're
|
||||
* not in that list, fall through
|
||||
*/
|
||||
if (!$var_id || !$class_storage->sealed_properties) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$has_regular_setter = true;
|
||||
|
@ -246,7 +246,13 @@ class FetchChecker
|
||||
$stmt->inferredType = Type::getMixed();
|
||||
}
|
||||
|
||||
continue;
|
||||
/*
|
||||
* If we have an explicit list of all allowed magic properties on the class, and we're
|
||||
* not in that list, fall through
|
||||
*/
|
||||
if (!$class_storage->sealed_properties) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,11 @@ class ClassLikeDocblockComment
|
||||
*/
|
||||
public $properties = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $sealed_properties = false;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
|
@ -47,6 +47,11 @@ class ClassLikeStorage
|
||||
*/
|
||||
public $deprecated = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $sealed_properties = false;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
|
@ -225,6 +225,8 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
|
||||
$storage->deprecated = $docblock_info->deprecated;
|
||||
|
||||
$storage->sealed_properties = $docblock_info->sealed_properties;
|
||||
|
||||
$storage->suppressed_issues = $docblock_info->suppressed_issues;
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,8 @@ class AnnotationTest extends TestCase
|
||||
}
|
||||
|
||||
$a = new A();
|
||||
$a->foo = "hello";',
|
||||
$a->foo = "hello";
|
||||
$a->bar = "hello"; // not a property',
|
||||
],
|
||||
'ignoreNullableReturn' => [
|
||||
'<?php
|
||||
@ -421,6 +422,50 @@ class AnnotationTest extends TestCase
|
||||
$a->foo = 5;',
|
||||
'error_message' => 'InvalidPropertyAssignment',
|
||||
],
|
||||
'propertySealedDocblockUndefinedPropertyAssignment' => [
|
||||
'<?php
|
||||
/**
|
||||
* @property string $foo
|
||||
* @psalm-seal-properties
|
||||
*/
|
||||
class A {
|
||||
public function __get(string $name) : ?string {
|
||||
if ($name === "foo") {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
|
||||
/** @param mixed $value */
|
||||
public function __set(string $name, $value) : void {
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A();
|
||||
$a->bar = 5;',
|
||||
'error_message' => 'UndefinedPropertyAssignment',
|
||||
],
|
||||
'propertySealedDocblockUndefinedPropertyFetch' => [
|
||||
'<?php
|
||||
/**
|
||||
* @property string $foo
|
||||
* @psalm-seal-properties
|
||||
*/
|
||||
class A {
|
||||
public function __get(string $name) : ?string {
|
||||
if ($name === "foo") {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
|
||||
/** @param mixed $value */
|
||||
public function __set(string $name, $value) : void {
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A();
|
||||
echo $a->bar;',
|
||||
'error_message' => 'UndefinedPropertyFetch',
|
||||
],
|
||||
'noStringParamType' => [
|
||||
'<?php
|
||||
function fooFoo($a) : void {
|
||||
|
Loading…
Reference in New Issue
Block a user