From a2f73fe13c5c639bcfb9110aafafa5ef656b8b37 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Thu, 10 Mar 2022 10:52:17 +0100 Subject: [PATCH 1/3] Refactor nullOr handling --- .../AssertTypeSpecifyingExtension.php | 108 +++++++++++++----- 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php b/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php index 6480d97..5172c4e 100644 --- a/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php +++ b/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php @@ -146,6 +146,14 @@ public function specifyTypes( ); } + if (substr($staticMethodReflection->getName(), 0, 6) === 'nullOr') { + return $this->handleNullOr( + $staticMethodReflection->getName(), + $node, + $scope + ); + } + $expression = self::createExpression($scope, $staticMethodReflection->getName(), $node->getArgs()); if ($expression === null) { return new SpecifiedTypes([], []); @@ -170,22 +178,7 @@ private static function createExpression( $trimmedName = self::trimName($name); $resolvers = self::getExpressionResolvers(); $resolver = $resolvers[$trimmedName]; - $expression = $resolver($scope, ...$args); - if ($expression === null) { - return null; - } - - if (substr($name, 0, 6) === 'nullOr') { - $expression = new BooleanOr( - $expression, - new Identical( - $args[0]->value, - new ConstFetch(new Name('null')) - ) - ); - } - - return $expression; + return $resolver($scope, ...$args); } /** @@ -799,24 +792,53 @@ private function handleAll( TypeSpecifierContext::createTruthy() ); - if (count($specifiedTypes->getSureTypes()) > 0) { - $sureTypes = $specifiedTypes->getSureTypes(); - $exprString = key($sureTypes); - [$exprNode, $type] = $sureTypes[$exprString]; + $typeBefore = $scope->getType($node->getArgs()[0]->value); + $type = $this->determineVariableTypeFromSpecifiedTypes($typeBefore, $specifiedTypes); + if ($type === null) { + return new SpecifiedTypes(); + } - return $this->arrayOrIterable( - $scope, - $exprNode, - static function () use ($type): Type { - return $type->getIterableValueType(); - } - ); + return $this->arrayOrIterable( + $scope, + $node->getArgs()[0]->value, + static function () use ($type): Type { + return $type->getIterableValueType(); + } + ); + } + + private function handleNullOr( + string $methodName, + StaticCall $node, + Scope $scope + ): SpecifiedTypes + { + $innerExpression = self::createExpression($scope, $methodName, $node->getArgs()); + if ($innerExpression === null) { + return new SpecifiedTypes(); } - if (count($specifiedTypes->getSureNotTypes()) > 0) { - throw new ShouldNotHappenException(); + + $expression = new BooleanAnd( + new NotIdentical( + $node->getArgs()[0]->value, + new ConstFetch(new Name('null')) + ), + $innerExpression + ); + + $specifiedTypes = $this->typeSpecifier->specifyTypesInCondition( + $scope, + $expression, + TypeSpecifierContext::createTruthy() + ); + + $typeBefore = $scope->getType($node->getArgs()[0]->value); + $type = $this->determineVariableTypeFromSpecifiedTypes($typeBefore, $specifiedTypes); + if ($type === null) { + return new SpecifiedTypes(); } - return $specifiedTypes; + return $this->typeSpecifier->create($node->getArgs()[0]->value, TypeCombinator::addNull($type), TypeSpecifierContext::createTruthy(), false, $scope); } private function arrayOrIterable( @@ -856,6 +878,32 @@ private function arrayOrIterable( ); } + private function determineVariableTypeFromSpecifiedTypes(Type $typeBefore, SpecifiedTypes $specifiedTypes): ?Type + { + if (count($specifiedTypes->getSureTypes()) > 0) { + $sureTypes = $specifiedTypes->getSureTypes(); + $sureNotTypes = $specifiedTypes->getSureNotTypes(); + $exprString = key($sureTypes); + [, $type] = $sureTypes[$exprString]; + + if (array_key_exists($exprString, $sureNotTypes)) { + $type = TypeCombinator::remove($type, $sureNotTypes[$exprString][1]); + } + + return $type; + } + + if (count($specifiedTypes->getSureNotTypes()) > 0) { + $sureNotTypes = $specifiedTypes->getSureNotTypes(); + $exprString = key($sureNotTypes); + [, $type] = $sureNotTypes[$exprString]; + + return TypeCombinator::remove($typeBefore, $type); + } + + return null; + } + /** * @param Expr[] $expressions * @param class-string $binaryOp From 4edf14f8f638dd2d6d58770fe4444cf1aa8800e2 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Thu, 10 Mar 2022 11:02:53 +0100 Subject: [PATCH 2/3] Make type determination stricter --- .../WebMozartAssert/AssertTypeSpecifyingExtension.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php b/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php index 5172c4e..f6faa69 100644 --- a/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php +++ b/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php @@ -794,9 +794,6 @@ private function handleAll( $typeBefore = $scope->getType($node->getArgs()[0]->value); $type = $this->determineVariableTypeFromSpecifiedTypes($typeBefore, $specifiedTypes); - if ($type === null) { - return new SpecifiedTypes(); - } return $this->arrayOrIterable( $scope, @@ -834,9 +831,6 @@ private function handleNullOr( $typeBefore = $scope->getType($node->getArgs()[0]->value); $type = $this->determineVariableTypeFromSpecifiedTypes($typeBefore, $specifiedTypes); - if ($type === null) { - return new SpecifiedTypes(); - } return $this->typeSpecifier->create($node->getArgs()[0]->value, TypeCombinator::addNull($type), TypeSpecifierContext::createTruthy(), false, $scope); } @@ -878,7 +872,7 @@ private function arrayOrIterable( ); } - private function determineVariableTypeFromSpecifiedTypes(Type $typeBefore, SpecifiedTypes $specifiedTypes): ?Type + private function determineVariableTypeFromSpecifiedTypes(Type $typeBefore, SpecifiedTypes $specifiedTypes): Type { if (count($specifiedTypes->getSureTypes()) > 0) { $sureTypes = $specifiedTypes->getSureTypes(); @@ -901,7 +895,7 @@ private function determineVariableTypeFromSpecifiedTypes(Type $typeBefore, Speci return TypeCombinator::remove($typeBefore, $type); } - return null; + throw new ShouldNotHappenException(); } /** From bfd1c520eb73025d7374e24e04eef906c976e028 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Thu, 10 Mar 2022 11:04:24 +0100 Subject: [PATCH 3/3] Remove weirdness of determineVariableTypeFromSpecifiedTypes by using the scope --- .../AssertTypeSpecifyingExtension.php | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php b/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php index f6faa69..6220aab 100644 --- a/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php +++ b/src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php @@ -792,8 +792,7 @@ private function handleAll( TypeSpecifierContext::createTruthy() ); - $typeBefore = $scope->getType($node->getArgs()[0]->value); - $type = $this->determineVariableTypeFromSpecifiedTypes($typeBefore, $specifiedTypes); + $type = $this->determineVariableTypeFromSpecifiedTypes($scope, $specifiedTypes); return $this->arrayOrIterable( $scope, @@ -829,8 +828,7 @@ private function handleNullOr( TypeSpecifierContext::createTruthy() ); - $typeBefore = $scope->getType($node->getArgs()[0]->value); - $type = $this->determineVariableTypeFromSpecifiedTypes($typeBefore, $specifiedTypes); + $type = $this->determineVariableTypeFromSpecifiedTypes($scope, $specifiedTypes); return $this->typeSpecifier->create($node->getArgs()[0]->value, TypeCombinator::addNull($type), TypeSpecifierContext::createTruthy(), false, $scope); } @@ -872,11 +870,12 @@ private function arrayOrIterable( ); } - private function determineVariableTypeFromSpecifiedTypes(Type $typeBefore, SpecifiedTypes $specifiedTypes): Type + private function determineVariableTypeFromSpecifiedTypes(Scope $scope, SpecifiedTypes $specifiedTypes): Type { - if (count($specifiedTypes->getSureTypes()) > 0) { - $sureTypes = $specifiedTypes->getSureTypes(); - $sureNotTypes = $specifiedTypes->getSureNotTypes(); + $sureTypes = $specifiedTypes->getSureTypes(); + $sureNotTypes = $specifiedTypes->getSureNotTypes(); + + if (count($sureTypes) > 0) { $exprString = key($sureTypes); [, $type] = $sureTypes[$exprString]; @@ -887,12 +886,12 @@ private function determineVariableTypeFromSpecifiedTypes(Type $typeBefore, Speci return $type; } - if (count($specifiedTypes->getSureNotTypes()) > 0) { + if (count($sureNotTypes) > 0) { $sureNotTypes = $specifiedTypes->getSureNotTypes(); $exprString = key($sureNotTypes); - [, $type] = $sureNotTypes[$exprString]; + [$exprNode, $type] = $sureNotTypes[$exprString]; - return TypeCombinator::remove($typeBefore, $type); + return TypeCombinator::remove($scope->getType($exprNode), $type); } throw new ShouldNotHappenException();