diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonDivArithmeticOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonDivArithmeticOpAnalyzer.php index 7e59c0527..c4520f937 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonDivArithmeticOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonDivArithmeticOpAnalyzer.php @@ -290,8 +290,12 @@ class NonDivArithmeticOpAnalyzer ): ?Type\Union { if ($left_type_part instanceof TLiteralInt && $right_type_part instanceof TLiteralInt - && ($left instanceof PhpParser\Node\Scalar || $left instanceof PhpParser\Node\Expr\ConstFetch) - && ($right instanceof PhpParser\Node\Scalar || $right instanceof PhpParser\Node\Expr\ConstFetch) + && ($left instanceof PhpParser\Node\Scalar + || $left instanceof PhpParser\Node\Expr\ConstFetch + || $left instanceof PhpParser\Node\Expr\ClassConstFetch) + && ($right instanceof PhpParser\Node\Scalar + || $right instanceof PhpParser\Node\Expr\ConstFetch + || $right instanceof PhpParser\Node\Expr\ClassConstFetch) ) { // time for some arithmetic! diff --git a/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php index 19d1efef6..27da36a43 100644 --- a/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php @@ -272,6 +272,12 @@ class ScalarTypeComparator return true; } + if (get_class($container_type_part) === TInt::class + && $input_type_part instanceof TInt + ) { + return true; + } + if ((get_class($input_type_part) === TInt::class && $container_type_part instanceof TLiteralInt) || (get_class($input_type_part) === TPositiveInt::class && $container_type_part instanceof TLiteralInt diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index df5c11f72..4d4c18cd6 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -379,7 +379,46 @@ class TypeExpander $final ); - if (\is_array($new_value_type) || !$new_value_type instanceof Type\Atomic\TLiteralInt) { + if (\is_array($new_value_type)) { + $new_value_type = reset($new_value_type); + } + + if (!$new_value_type instanceof Type\Atomic\TLiteralInt) { + return new Type\Atomic\TInt(); + } + + $potential_ints[] = $new_value_type->value; + } + + return \Psalm\Internal\Type\TypeParser::getComputedIntsFromMask($potential_ints); + } + + if ($return_type instanceof Type\Atomic\TIntMaskOf) { + if (!$evaluate_class_constants) { + return new Type\Atomic\TInt(); + } + + $value_type = $return_type->value; + + $new_value_types = self::expandAtomic( + $codebase, + $value_type, + $self_class, + $static_class_type, + $parent_class, + $evaluate_class_constants, + $evaluate_conditional_types, + $final + ); + + if (!is_array($new_value_types)) { + return new Type\Atomic\TInt(); + } + + $potential_ints = []; + + foreach ($new_value_types as $new_value_type) { + if (!$new_value_type instanceof Type\Atomic\TLiteralInt) { return new Type\Atomic\TInt(); } diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index 3da9826a3..3a3c4ece8 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -1143,6 +1143,40 @@ class AnnotationTest extends TestCase /** @var DateTime $obj */ echo $obj->format("Y");' ], + 'intMaskWithClassConstants' => [ + ' $flags + */ + function takesFlags(int $flags) : void { + echo $flags; + } + + takesFlags(FileFlag::MODIFIED | FileFlag::NEW);' + ], + 'intMaskOfWithClassWildcard' => [ + ' $flags + */ + function takesFlags(int $flags) : void { + echo $flags; + } + + takesFlags(FileFlag::MODIFIED | FileFlag::NEW);' + ], ]; } diff --git a/tests/Php56Test.php b/tests/Php56Test.php index cdc1f786d..352021387 100644 --- a/tests/Php56Test.php +++ b/tests/Php56Test.php @@ -49,8 +49,8 @@ class Php56Test extends TestCase $c4 = (new C)->four;', 'assertions' => [ '$c1' => 'int', - '$c2' => 'positive-int', - '$c3' => 'positive-int', + '$c2===' => 'int(2)', + '$c3===' => 'int(3)', '$c1_3rd' => 'float|int', '$c_sentence' => 'string', '$cf' => 'int',