From 5ed49c0c0374a605e0d50f03ae40e13c9e29f3e0 Mon Sep 17 00:00:00 2001 From: Brown Date: Wed, 26 Jun 2019 15:11:16 -0400 Subject: [PATCH] Fixed #1848 - allow static class strings to be compared --- .../Expression/Fetch/ConstFetchAnalyzer.php | 8 +++++++- src/Psalm/Type/Reconciler.php | 9 +++++++-- src/Psalm/Type/Union.php | 8 ++++++++ tests/AnnotationTest.php | 2 +- tests/ClassStringTest.php | 12 ++++++++++++ 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php index f957fe10e..43eaac1b9 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php @@ -172,7 +172,13 @@ class ConstFetchAnalyzer } } - $stmt->inferredType = Type::getLiteralClassString($fq_class_name); + if ($first_part_lc === 'static') { + $stmt->inferredType = new Type\Union([ + new Type\Atomic\TClassString($fq_class_name, new Type\Atomic\TNamedObject($fq_class_name)) + ]); + } else { + $stmt->inferredType = Type::getLiteralClassString($fq_class_name); + } if ($codebase->store_node_types) { $codebase->analyzer->addNodeReference( diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index dd84e65ff..646168fa0 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -249,6 +249,7 @@ class Reconciler && !$result_type->hasTemplate() && !$result_type->hasType('iterable') && (!$has_isset || substr($key, -1, 1) !== ']') + && !($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer) ) { $reconcile_key = implode( '&', @@ -1307,6 +1308,9 @@ class Reconciler && $new_type->getId() === $existing_var_type->getId() && !$is_equality && !$is_maybe_callable_array + && (!($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer) + || ($key !== '$this' + && !($existing_var_type->hasLiteralClassString() && $new_type->hasLiteralClassString()))) ) { self::triggerIssueForImpossible( $existing_var_type, @@ -1358,8 +1362,9 @@ class Reconciler // fall through } } - } elseif ($key !== '$this' - || !($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer) + } elseif (!($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer) + || ($key !== '$this' + && !($existing_var_type->hasLiteralClassString() && $new_type->hasLiteralClassString())) ) { if ($existing_var_type->from_docblock) { if (IssueBuffer::accepts( diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 01ca439b1..dd8ff9a7e 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -657,6 +657,14 @@ class Union || $this->typed_class_strings; } + /** + * @return bool + */ + public function hasLiteralClassString() + { + return count($this->typed_class_strings) > 0; + } + /** * @return bool */ diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index c4e134032..a0e029f63 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -1591,7 +1591,7 @@ class AnnotationTest extends TestCase */ function f($reference) {}', 'error_message' => 'MissingDocblockType' - ] + ], ]; } } diff --git a/tests/ClassStringTest.php b/tests/ClassStringTest.php index 5531e6708..c4c9dc777 100644 --- a/tests/ClassStringTest.php +++ b/tests/ClassStringTest.php @@ -591,6 +591,18 @@ class ClassStringTest extends TestCase } }' ], + 'allowComparisonToStaticClassString' => [ + ' B::class]; + + function foo(): bool { + return self::CLASSES["foobar"] === static::class; + } + } + + class B extends A {}' + ], ]; }