From 0997a576a9f55ab211c4a022235938d6b9f2fa24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ey=C3=BCp=20Can=20Akman?= Date: Fri, 5 Jun 2026 21:53:52 +0300 Subject: [PATCH] Fix false RedundantCondition for instanceof on a class-string variable `$x instanceof $class` where `$class` is a `class-string` for an interface `I` was reported as RedundantCondition when `$x` was already `I`. The variable can name any class implementing `I`, so the check can be false at runtime. Interface class-strings now resolve through is_a instead of a fixed type, like is_a($x, $class). Fixes #11076 --- .../Statements/Expression/AssertionFinder.php | 11 ++++++++--- tests/TypeReconciliation/RedundantConditionTest.php | 12 ++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index 7c2938f3014..2b00fde7a05 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -1292,9 +1292,14 @@ private static function getInstanceOfAssertions( ), ); } elseif ($atomic_type instanceof TClassString && $atomic_type->as !== 'object') { - $literal_class_strings[] = new IsType( - $atomic_type->as_type ?: new TNamedObject($atomic_type->as), - ); + $as_type = $atomic_type->as_type ?: new TNamedObject($atomic_type->as); + // an interface class-string can hold any implementor, so this is is_a, not a + // fixed type, and must not read as redundant (#11076) + if ($source->getCodebase()->interfaceExists($atomic_type->as)) { + $literal_class_strings[] = new IsAClass($as_type, false); + } else { + $literal_class_strings[] = new IsType($as_type); + } } } diff --git a/tests/TypeReconciliation/RedundantConditionTest.php b/tests/TypeReconciliation/RedundantConditionTest.php index 83c0e529a5e..929ae1875ef 100644 --- a/tests/TypeReconciliation/RedundantConditionTest.php +++ b/tests/TypeReconciliation/RedundantConditionTest.php @@ -983,6 +983,18 @@ function f(array $p) : void { assert(!!$p); }', ], + 'instanceofClassStringVarIsNotRedundant' => [ + 'code' => ' $class + */ + function test(Traversable $input, string $class): bool { + if ($input instanceof $class) { + return true; + } + return false; + }', + ], ]; }