1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #114 - add optional Hack-like checks calls

This commit is contained in:
Matthew Brown 2017-04-14 21:32:14 -04:00
parent 97916c523e
commit ce6ca58291
7 changed files with 112 additions and 3 deletions

View File

@ -26,6 +26,7 @@
<xs:attribute name="requireVoidReturnType" type="xs:string" />
<xs:attribute name="useAssertForType" type="xs:string" />
<xs:attribute name="cacheFileContentHashes" type="xs:string" />
<xs:attribute name="rememberPropertyAssignmentsAfterCall" type="xs:string" />
</xs:complexType>
<xs:complexType name="ProjectFilesType">

View File

@ -5,6 +5,7 @@
useDocblockTypes="true"
totallyTyped="true"
strictBinaryOperands="false"
rememberPropertyAssignmentsAfterCall="true"
>
<projectFiles>
<directory name="src" />

View File

@ -49,7 +49,7 @@ class ForChecker
}
foreach ($context->vars_in_scope as $var => $type) {
if ($type->isMixed()) {
if ($type->isMixed() || !isset($for_context->vars_in_scope[$var])) {
continue;
}

View File

@ -296,6 +296,8 @@ class CallChecker
// fall through
}
$config = Config::getInstance();
if ($function_exists) {
$generic_params = null;
@ -326,8 +328,6 @@ class CallChecker
}
}
$config = Config::getInstance();
if ($stmt->name instanceof PhpParser\Node\Name && $method_id) {
if (!$in_call_map || $is_stubbed) {
if ($function_storage && $function_storage->template_types) {
@ -424,6 +424,10 @@ class CallChecker
}
}
if (!$config->remember_property_assignments_after_call && !$context->collect_initializations) {
$context->removeAllObjectVars();
}
if ($stmt->name instanceof PhpParser\Node\Name &&
($stmt->name->parts === ['get_class'] || $stmt->name->parts === ['gettype']) &&
$stmt->args
@ -609,6 +613,12 @@ class CallChecker
}
}
$config = Config::getInstance();
if (!$config->remember_property_assignments_after_call && !$context->collect_initializations) {
$context->removeAllObjectVars();
}
return null;
}
@ -918,6 +928,10 @@ class CallChecker
$statements_checker
);
}
if (!$config->remember_property_assignments_after_call && !$context->collect_initializations) {
$context->removeAllObjectVars();
}
}
/**
@ -1253,6 +1267,10 @@ class CallChecker
$statements_checker
);
}
if (!$config->remember_property_assignments_after_call && !$context->collect_initializations) {
$context->removeAllObjectVars();
}
}
/**

View File

@ -168,6 +168,11 @@ class Config
*/
public $use_assert_for_type = false;
/**
* @var boolean
*/
public $remember_property_assignments_after_call = true;
/**
* Psalm plugins
*
@ -323,6 +328,11 @@ class Config
$config->cache_file_hashes_during_run = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['rememberPropertyAssignmentsAfterCall'])) {
$attribute_text = (string) $config_xml['rememberPropertyAssignmentsAfterCall'];
$config->remember_property_assignments_after_call = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml->projectFiles)) {
$config->project_files = ProjectFileFilter::loadFromXMLElement($config_xml->projectFiles, true);
}

View File

@ -361,6 +361,51 @@ class Context
}
}
/**
* @return void
*/
public function removeAllObjectVars()
{
$vars_to_remove = [];
foreach ($this->vars_in_scope as $var_id => $_) {
if (strpos($var_id, '->') !== false || strpos($var_id, '::') !== false) {
$vars_to_remove[] = $var_id;
}
}
if (!$vars_to_remove) {
return;
}
foreach ($vars_to_remove as $var_id) {
unset($this->vars_in_scope[$var_id]);
}
$clauses_to_keep = [];
foreach ($this->clauses as $clause) {
$abandon_clause = false;
foreach (array_keys($clause->possibilities) as $key) {
if (strpos($key, '->') !== false || strpos($key, '::') !== false) {
$abandon_clause = true;
break;
}
}
if (!$abandon_clause) {
$clauses_to_keep[] = $clause;
}
}
$this->clauses = $clauses_to_keep;
if ($this->parent_context) {
$this->parent_context->removeAllObjectVars();
}
}
/**
* @param Context $op_context
* @return void

View File

@ -1072,4 +1072,38 @@ class PropertyTypeTest extends PHPUnit_Framework_TestCase
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
$file_checker->visitAndAnalyzeMethods();
}
/**
* @expectedException \Psalm\Exception\CodeException
* @expectedExceptionMessage InvalidReturnType
* @return void
*/
public function testForgetPropertyAssignments()
{
Config::getInstance()->remember_property_assignments_after_call = false;
$stmts = self::$parser->parse('<?php
class X {
/** @var ?int **/
private $x;
public function getX(): int {
if ($this->x === null) {
$this->x = 0;
}
$this->modifyX();
return $this->x;
}
private function modifyX(): void {
$this->x = null;
}
}
');
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
$file_checker->visitAndAnalyzeMethods();
}
}