mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
PHP 8.2: seal all properties by default, add configuration key for lower versions
This commit is contained in:
parent
549c02d661
commit
b530f23acc
@ -95,6 +95,7 @@
|
||||
<xs:attribute name="usePhpDocPropertiesWithoutMagicCall" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="skipChecksOnUnresolvableIncludes" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="sealAllMethods" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="sealAllProperties" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="runTaintAnalysis" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="usePhpStormMetaPath" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="allowInternalNamedArgumentCalls" type="xs:boolean" default="true" />
|
||||
|
@ -305,6 +305,16 @@ This defaults to `false`.
|
||||
|
||||
When `true`, Psalm will treat all classes as if they had sealed methods, meaning that if you implement the magic method `__call`, you also have to add `@method` for each magic method. Defaults to false.
|
||||
|
||||
#### sealAllProperties
|
||||
|
||||
```xml
|
||||
<psalm
|
||||
sealAllProperties="[bool]"
|
||||
>
|
||||
```
|
||||
|
||||
When `true`, Psalm will treat all classes as if they had sealed properties, meaning that Psalm will disallow getting and setting any properties not contained in a list of `@property` (or `@property-read`/`@property-write`) annotations and not explicitly defined as a `property`. Defaults to false.
|
||||
|
||||
#### runTaintAnalysis
|
||||
|
||||
```xml
|
||||
|
@ -1896,6 +1896,16 @@ class Codebase
|
||||
return UnionTypeComparator::isContainedBy($this, $input_type, $container_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether all properties of a class should be sealed.
|
||||
*/
|
||||
public function shouldSealAllProperties(ClassLikeStorage $storage): bool
|
||||
{
|
||||
return $storage->sealed_properties
|
||||
|| $this->config->seal_all_properties
|
||||
|| ($this->php_major_version >= 8 && $this->php_minor_version >= 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if type has any part that is a subtype of other
|
||||
*
|
||||
|
@ -346,6 +346,11 @@ class Config
|
||||
*/
|
||||
public $seal_all_methods = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $seal_all_properties = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
@ -907,6 +912,7 @@ class Config
|
||||
'reportMixedIssues' => 'show_mixed_issues',
|
||||
'skipChecksOnUnresolvableIncludes' => 'skip_checks_on_unresolvable_includes',
|
||||
'sealAllMethods' => 'seal_all_methods',
|
||||
'sealAllProperties' => 'seal_all_properties',
|
||||
'runTaintAnalysis' => 'run_taint_analysis',
|
||||
'usePhpStormMetaPath' => 'use_phpstorm_meta_path',
|
||||
'allowInternalNamedArgumentsCalls' => 'allow_internal_named_arg_calls',
|
||||
|
@ -548,7 +548,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
case '__set':
|
||||
// If `@psalm-seal-properties` is set, the property must be defined with
|
||||
// a `@property` annotation
|
||||
if ($class_storage->sealed_properties
|
||||
if ($codebase->shouldSealAllProperties($class_storage)
|
||||
&& !isset($class_storage->pseudo_property_set_types['$' . $prop_name])
|
||||
&& IssueBuffer::accepts(
|
||||
new UndefinedThisPropertyAssignment(
|
||||
@ -647,7 +647,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
case '__get':
|
||||
// If `@psalm-seal-properties` is set, the property must be defined with
|
||||
// a `@property` annotation
|
||||
if ($class_storage->sealed_properties
|
||||
if ($codebase->shouldSealAllProperties($class_storage)
|
||||
&& !isset($class_storage->pseudo_property_get_types['$' . $prop_name])
|
||||
&& IssueBuffer::accepts(
|
||||
new UndefinedThisPropertyFetch(
|
||||
|
@ -676,7 +676,7 @@ class AtomicPropertyFetchAnalyzer
|
||||
* 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 && !$override_property_visibility) {
|
||||
if (!$codebase->shouldSealAllProperties($class_storage) && !$override_property_visibility) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ namespace Psalm\Tests;
|
||||
|
||||
use Psalm\Config;
|
||||
use Psalm\Context;
|
||||
use Psalm\Exception\CodeException;
|
||||
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
||||
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
||||
|
||||
@ -1159,4 +1160,28 @@ class MagicPropertyTest extends TestCase
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testSealAllMethodsWithoutFoo(): void
|
||||
{
|
||||
Config::getInstance()->seal_all_properties = true;
|
||||
|
||||
$this->addFile(
|
||||
'somefile.php',
|
||||
'<?php
|
||||
class A {
|
||||
public function __get(string $name) {}
|
||||
}
|
||||
|
||||
class B extends A {}
|
||||
|
||||
$b = new B();
|
||||
$result = $b->foo;
|
||||
'
|
||||
);
|
||||
|
||||
$error_message = 'UndefinedMagicPropertyFetch';
|
||||
$this->expectException(CodeException::class);
|
||||
$this->expectExceptionMessage($error_message);
|
||||
$this->analyzeFile('somefile.php', new Context());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user