1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 12:24:49 +01:00

Fix issue with $this instanceof checks in traits

This commit is contained in:
Matthew Brown 2017-06-23 00:39:37 -04:00
parent 967b51d5fc
commit bc35f88859
9 changed files with 97 additions and 76 deletions

View File

@ -118,7 +118,7 @@ class IfChecker
$reconcilable_if_types,
$if_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->cond, $context->include_location),
$statements_checker->getSuppressedIssues()
);
@ -156,7 +156,7 @@ class IfChecker
$if_scope->negated_types,
$temp_else_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->cond, $context->include_location),
$statements_checker->getSuppressedIssues()
);
@ -349,7 +349,7 @@ class IfChecker
$if_scope->negated_types,
$outer_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation(
$statements_checker->getSource(),
$stmt->cond,
@ -441,7 +441,7 @@ class IfChecker
$if_scope->negated_types,
$elseif_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation(
$statements_checker->getSource(),
$elseif->cond,
@ -516,7 +516,7 @@ class IfChecker
$reconcilable_elseif_types,
$elseif_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $elseif->cond, $outer_context->include_location),
$statements_checker->getSuppressedIssues()
);
@ -634,7 +634,7 @@ class IfChecker
$negated_elseif_types,
$pre_conditional_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $elseif, $outer_context->include_location),
$statements_checker->getSuppressedIssues()
);
@ -757,7 +757,7 @@ class IfChecker
$else_types,
$else_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $else, $outer_context->include_location),
$statements_checker->getSuppressedIssues()
);

View File

@ -58,7 +58,7 @@ class WhileChecker
$reconcilable_while_types,
$while_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->cond),
$statements_checker->getSuppressedIssues()
);
@ -101,7 +101,7 @@ class WhileChecker
$negated_while_types,
$context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->cond),
$statements_checker->getSuppressedIssues()
);

View File

@ -125,7 +125,7 @@ class AssignmentChecker
$array_var_id,
$context->vars_in_scope[$array_var_id],
$assign_value_type,
$statements_checker->getFileChecker()
$statements_checker
);
}

View File

@ -396,7 +396,7 @@ class CallChecker
$assert_type_assertions,
$context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt),
$statements_checker->getSuppressedIssues()
);

View File

@ -634,7 +634,7 @@ class ExpressionChecker
$var_id,
$existing_type,
$by_ref_type,
$statements_checker->getFileChecker()
$statements_checker
);
if ((string)$existing_type !== 'array<empty, empty>') {
@ -790,7 +790,7 @@ class ExpressionChecker
$left_type_assertions,
$context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt),
$statements_checker->getSuppressedIssues()
);
@ -862,7 +862,7 @@ class ExpressionChecker
$negated_type_assertions,
$context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt),
$statements_checker->getSuppressedIssues()
);
@ -896,7 +896,7 @@ class ExpressionChecker
'!empty',
$context->vars_in_scope[$var_id],
'',
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->left),
$statements_checker->getSuppressedIssues()
);
@ -953,7 +953,7 @@ class ExpressionChecker
$reconcilable_if_types,
$t_if_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->left),
$statements_checker->getSuppressedIssues()
);
@ -990,7 +990,7 @@ class ExpressionChecker
$negated_if_types,
$t_else_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->right),
$statements_checker->getSuppressedIssues()
);
@ -1770,7 +1770,7 @@ class ExpressionChecker
$reconcilable_if_types,
$t_if_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->cond),
$statements_checker->getSuppressedIssues()
);
@ -1806,7 +1806,7 @@ class ExpressionChecker
$negated_if_types,
$t_else_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt->else),
$statements_checker->getSuppressedIssues()
);
@ -1847,7 +1847,7 @@ class ExpressionChecker
'!empty',
$stmt->cond->inferredType,
'',
$statements_checker->getFileChecker(),
$statements_checker,
new CodeLocation($statements_checker->getSource(), $stmt),
$statements_checker->getSuppressedIssues()
);

View File

@ -34,7 +34,7 @@ class TypeChecker
* @param array<string, string> $new_types
* @param array<string, Type\Union> $existing_types
* @param array<string> $changed_types
* @param FileChecker $file_checker
* @param StatementsChecker $statements_checker
* @param CodeLocation $code_location
* @param array<string> $suppressed_issues
*
@ -44,7 +44,7 @@ class TypeChecker
array $new_types,
array $existing_types,
array &$changed_types,
FileChecker $file_checker,
StatementsChecker $statements_checker,
CodeLocation $code_location,
array $suppressed_issues = []
) {
@ -75,7 +75,7 @@ class TypeChecker
$result_type = isset($existing_types[$key])
? clone $existing_types[$key]
: self::getValueForKey($key, $existing_types, $file_checker);
: self::getValueForKey($key, $existing_types, $statements_checker->getFileChecker());
if ($result_type && empty($result_type->types)) {
throw new \InvalidArgumentException('Union::$types cannot be empty after get value for ' . $key);
@ -90,7 +90,7 @@ class TypeChecker
(string) $new_type_part,
$result_type,
$key,
$file_checker,
$statements_checker,
$code_location,
$suppressed_issues,
$failed_reconciliation
@ -136,7 +136,7 @@ class TypeChecker
* @param string $new_var_type
* @param Type\Union|null $existing_var_type
* @param string|null $key
* @param FileChecker $file_checker
* @param StatementsChecker $statements_checker
* @param CodeLocation $code_location
* @param array $suppressed_issues
* @param bool $failed_reconciliation if the types cannot be reconciled, we need to know
@ -147,11 +147,13 @@ class TypeChecker
$new_var_type,
$existing_var_type,
$key,
FileChecker $file_checker,
StatementsChecker $statements_checker,
CodeLocation $code_location = null,
array $suppressed_issues = [],
&$failed_reconciliation = false
) {
$file_checker = $statements_checker->getFileChecker();
if ($existing_var_type === null) {
if ($new_var_type === '^isset') {
return null;
@ -249,7 +251,9 @@ class TypeChecker
$existing_var_type->removeType($negated_type);
if (empty($existing_var_type->types)) {
if (!$existing_var_type->from_docblock) {
if (!$existing_var_type->from_docblock
&& ($key !== '$this' || !($statements_checker->getSource()->getSource() instanceof TraitChecker))
) {
if ($key && $code_location) {
if (IssueBuffer::accepts(
new FailedTypeResolution('Cannot resolve types for ' . $key, $code_location),

View File

@ -1,7 +1,7 @@
<?php
namespace Psalm;
use Psalm\Checker\FileChecker;
use Psalm\Checker\StatementsChecker;
use Psalm\Type\Union;
class Context
@ -273,14 +273,14 @@ class Context
/**
* @param string $remove_var_id
* @param Union|null $new_type
* @param FileChecker|null $file_checker
* @param ?StatementsChecker $statements_checker
*
* @return void
*/
public function removeVarFromConflictingClauses(
$remove_var_id,
Union $new_type = null,
FileChecker $file_checker = null
StatementsChecker $statements_checker = null
) {
$clauses_to_keep = [];
@ -293,7 +293,7 @@ class Context
$clause->possibilities[$remove_var_id] === [$new_type_string]
) {
$clauses_to_keep[] = $clause;
} elseif ($file_checker &&
} elseif ($statements_checker &&
$new_type &&
!$new_type->isMixed()
) {
@ -313,7 +313,7 @@ class Context
$type,
clone $new_type,
null,
$file_checker,
$statements_checker,
null,
[],
$failed_reconciliation
@ -342,7 +342,7 @@ class Context
* @param string $remove_var_id
* @param \Psalm\Type\Union|null $existing_type
* @param \Psalm\Type\Union|null $new_type
* @param FileChecker|null $file_checker
* @param ?StatementsChecker $statements_checker
*
* @return void
*/
@ -350,7 +350,7 @@ class Context
$remove_var_id,
Union $existing_type = null,
Union $new_type = null,
FileChecker $file_checker = null
StatementsChecker $statements_checker = null
) {
if (!$existing_type && isset($this->vars_in_scope[$remove_var_id])) {
$existing_type = $this->vars_in_scope[$remove_var_id];
@ -363,7 +363,7 @@ class Context
$this->removeVarFromConflictingClauses(
$remove_var_id,
$existing_type->isMixed() ? null : $new_type,
$file_checker
$statements_checker
);
if ($existing_type->hasArray() || $existing_type->isMixed()) {

View File

@ -18,10 +18,10 @@ class TraitTest extends TestCase
private function fooFoo() : void {
}
}
class B {
use T;
public function doFoo() : void {
$this->fooFoo();
}
@ -33,10 +33,10 @@ class TraitTest extends TestCase
protected function fooFoo() : void {
}
}
class B {
use T;
public function doFoo() : void {
$this->fooFoo();
}
@ -48,10 +48,10 @@ class TraitTest extends TestCase
public function fooFoo() : void {
}
}
class B {
use T;
public function doFoo() : void {
$this->fooFoo();
}
@ -63,10 +63,10 @@ class TraitTest extends TestCase
/** @var string */
private $fooFoo = "";
}
class B {
use T;
public function doFoo() : void {
echo $this->fooFoo;
}
@ -78,10 +78,10 @@ class TraitTest extends TestCase
/** @var string */
protected $fooFoo = "";
}
class B {
use T;
public function doFoo() : void {
echo $this->fooFoo;
}
@ -93,10 +93,10 @@ class TraitTest extends TestCase
/** @var string */
public $fooFoo = "";
}
class B {
use T;
public function doFoo() : void {
echo $this->fooFoo;
}
@ -108,11 +108,11 @@ class TraitTest extends TestCase
protected function fooFoo() : void {
}
}
class B {
use T;
}
class C extends B {
public function doFoo() : void {
$this->fooFoo();
@ -125,11 +125,11 @@ class TraitTest extends TestCase
public function fooFoo() : void {
}
}
class B {
use T;
}
class C extends B {
public function doFoo() : void {
$this->fooFoo();
@ -143,12 +143,12 @@ class TraitTest extends TestCase
self::barBar();
}
}
class B {
use T;
public static function barBar() : void {
}
}',
],
@ -158,14 +158,14 @@ class TraitTest extends TestCase
public function fooFoo() : void {
}
}
class B {
use T;
public function fooFoo(string $a) : void {
}
}
(new B)->fooFoo("hello");',
],
'redefinedTraitMethodWithAlias' => [
@ -174,12 +174,12 @@ class TraitTest extends TestCase
public function fooFoo() : void {
}
}
class B {
use T {
fooFoo as barBar;
}
public function fooFoo() : void {
$this->barBar();
}
@ -193,11 +193,11 @@ class TraitTest extends TestCase
return $this;
}
}
class A {
use T;
}
$a = (new A)->g();',
'assertions' => [
['A' => '$a'],
@ -211,18 +211,18 @@ class TraitTest extends TestCase
return $this;
}
}
class A {
use T;
}
class B extends A {
}
class C {
use T;
}
$a = (new B)->g();',
'assertions' => [
['A' => '$a'],
@ -236,7 +236,7 @@ class TraitTest extends TestCase
}
class A {
use T;
/** @return void */
public function bar() {
T::foo();
@ -249,16 +249,28 @@ class TraitTest extends TestCase
/** @return void */
abstract public function foo();
}
abstract class A {
use T;
/** @return void */
public function bar() {
$this->foo();
}
}',
],
'instanceOfTraitUser' => [
'<?php
trait T {
public function f() : void {
if ($this instanceof A) { }
}
}
class A {
use T;
}',
],
];
}
@ -274,11 +286,11 @@ class TraitTest extends TestCase
private function fooFoo() : void {
}
}
class B {
use T;
}
class C extends B {
public function doFoo() : void {
$this->fooFoo();
@ -300,7 +312,7 @@ class TraitTest extends TestCase
}
class A {
use T;
public function assignToFoo() : void {
$this->foo = 5;
}
@ -315,7 +327,7 @@ class TraitTest extends TestCase
}
class A {
use T;
public function __construct() : void {
$this->foo = 5;
}
@ -330,11 +342,11 @@ class TraitTest extends TestCase
}
class A {
use T;
public function __construct() : void {
$this->foo = 5;
}
public function makeNull() : void {
$this->foo = null;
}
@ -349,7 +361,7 @@ class TraitTest extends TestCase
}
class A {
use T;
public function __construct() : void {
$this->foo = 5;
}

View File

@ -3,6 +3,7 @@ namespace Psalm\Tests;
use Psalm\Checker\AlgebraChecker;
use Psalm\Checker\FileChecker;
use Psalm\Checker\StatementsChecker;
use Psalm\Checker\TypeChecker;
use Psalm\Clause;
use Psalm\Context;
@ -16,6 +17,9 @@ class TypeReconciliationTest extends TestCase
/** @var FileChecker */
protected $file_checker;
/** @var StatementsChecker */
protected $statements_checker;
/**
* @return void
*/
@ -25,6 +29,7 @@ class TypeReconciliationTest extends TestCase
$this->file_checker = new FileChecker('somefile.php', $this->project_checker);
$this->file_checker->context = new Context();
$this->statements_checker = new StatementsChecker($this->file_checker);
}
/**
@ -42,7 +47,7 @@ class TypeReconciliationTest extends TestCase
$type,
Type::parseString($string),
null,
$this->file_checker
$this->statements_checker
);
$this->assertSame(