diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md
index 3325db1d93c..17705b6d737 100644
--- a/build/target-repository/docs/rector_rules_overview.md
+++ b/build/target-repository/docs/rector_rules_overview.md
@@ -1,4 +1,4 @@
-# 421 Rules Overview
+# 422 Rules Overview
@@ -64,7 +64,7 @@
- [Transform](#transform) (34)
-- [TypeDeclaration](#typedeclaration) (40)
+- [TypeDeclaration](#typedeclaration) (41)
- [Visibility](#visibility) (3)
@@ -9689,6 +9689,25 @@ Add strict return array type based on created empty array and returned
+### ReturnTypeFromStrictTernaryRector
+
+Add method return type based on strict ternary values
+
+- class: [`Rector\TypeDeclaration\Rector\Class_\ReturnTypeFromStrictTernaryRector`](../rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php)
+
+```diff
+ final class SomeClass
+ {
+- public function getValue($number)
++ public function getValue($number): int
+ {
+ return $number ? 100 : 500;
+ }
+ }
+```
+
+
+
### ReturnTypeFromStrictTypedCallRector
Add return type from strict return type of call
diff --git a/config/set/type-declaration.php b/config/set/type-declaration.php
index 6f67c6ca5a9..75135ab7ad8 100644
--- a/config/set/type-declaration.php
+++ b/config/set/type-declaration.php
@@ -6,6 +6,7 @@
use Rector\Config\RectorConfig;
use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector;
use Rector\TypeDeclaration\Rector\Class_\PropertyTypeFromStrictSetterGetterRector;
+use Rector\TypeDeclaration\Rector\Class_\ReturnTypeFromStrictTernaryRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeBasedOnPHPUnitDataProviderRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector;
@@ -70,5 +71,6 @@
ReturnNeverTypeRector::class,
EmptyOnNullableObjectToInstanceOfRector::class,
PropertyTypeFromStrictSetterGetterRector::class,
+ ReturnTypeFromStrictTernaryRector::class,
]);
};
diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/local_constants.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/local_constants.php.inc
new file mode 100644
index 00000000000..9c1fba82299
--- /dev/null
+++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/local_constants.php.inc
@@ -0,0 +1,33 @@
+
+-----
+
diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_dynamic_scalar.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_dynamic_scalar.php.inc
new file mode 100644
index 00000000000..2faf7cc8daf
--- /dev/null
+++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_dynamic_scalar.php.inc
@@ -0,0 +1,11 @@
+
+-----
+
diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/ReturnTypeFromStrictTernaryRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/ReturnTypeFromStrictTernaryRectorTest.php
new file mode 100644
index 00000000000..5d281c4f50e
--- /dev/null
+++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/ReturnTypeFromStrictTernaryRectorTest.php
@@ -0,0 +1,32 @@
+doTestFile($filePath);
+ }
+
+ /**
+ * @return Iterator
+ */
+ public function provideData(): Iterator
+ {
+ return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
+ }
+
+ public function provideConfigFilePath(): string
+ {
+ return __DIR__ . '/config/configured_rule.php';
+ }
+}
diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/config/configured_rule.php
new file mode 100644
index 00000000000..c619d2c8dcd
--- /dev/null
+++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/config/configured_rule.php
@@ -0,0 +1,11 @@
+rule(ReturnTypeFromStrictTernaryRector::class);
+};
diff --git a/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php b/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php
new file mode 100644
index 00000000000..ae4e9692609
--- /dev/null
+++ b/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php
@@ -0,0 +1,186 @@
+>
+ */
+ public function getNodeTypes(): array
+ {
+ return [Class_::class];
+ }
+
+ /**
+ * @param Class_ $node
+ */
+ public function refactor(Node $node): ?Node
+ {
+ $hasChanged = false;
+
+ foreach ($node->getMethods() as $classMethod) {
+ if ($classMethod->returnType instanceof Node) {
+ continue;
+ }
+
+ $onlyStmt = $classMethod->stmts[0] ?? null;
+ if (! $onlyStmt instanceof Return_) {
+ continue;
+ }
+
+ if (! $onlyStmt->expr instanceof Ternary) {
+ continue;
+ }
+
+ $ternary = $onlyStmt->expr;
+
+ // has scalar in if/else of ternary
+ $ternaryIfElseTypes = $this->matchScalarTernaryIfElseTypes($ternary);
+ if (! $ternaryIfElseTypes instanceof TernaryIfElseTypes) {
+ continue;
+ }
+
+ $ifType = $ternaryIfElseTypes->getFirstType();
+ $elseType = $ternaryIfElseTypes->getSecondType();
+
+ if (! $this->areTypesEqual($ifType, $elseType)) {
+ continue;
+ }
+
+ $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($ifType, TypeKind::RETURN);
+
+ if ($this->parentClassMethodTypeOverrideGuard->shouldSkipReturnTypeChange($classMethod, $ifType)) {
+ continue;
+ }
+
+ if (! $returnTypeNode instanceof Node) {
+ continue;
+ }
+
+ $classMethod->returnType = $returnTypeNode;
+ $hasChanged = true;
+ }
+
+ if ($hasChanged) {
+ return $node;
+ }
+
+ return null;
+ }
+
+ public function provideMinPhpVersion(): int
+ {
+ return PhpVersionFeature::SCALAR_TYPES;
+ }
+
+ private function isAlwaysScalarExpr(?Expr $expr): bool
+ {
+ // check if Scalar node
+ if ($expr instanceof Scalar) {
+ return true;
+ }
+
+ // check if constant
+ if ($expr instanceof ConstFetch) {
+ return true;
+ }
+
+ // check if class constant
+ return $expr instanceof ClassConstFetch;
+ }
+
+ private function areTypesEqual(Type $firstType, Type $secondType): bool
+ {
+ // this is needed to make comparison tolerant to constant values, e.g. 5 and 10 are same only then
+ if ($firstType instanceof ConstantType) {
+ $firstType = $firstType->generalize(GeneralizePrecision::lessSpecific());
+ }
+
+ if ($secondType instanceof ConstantType) {
+ $secondType = $secondType->generalize(GeneralizePrecision::lessSpecific());
+ }
+
+ return $firstType->equals($secondType);
+ }
+
+ private function matchScalarTernaryIfElseTypes(Ternary $ternary): ?TernaryIfElseTypes
+ {
+ if (! $this->isAlwaysScalarExpr($ternary->if)) {
+ return null;
+ }
+
+ if (! $this->isAlwaysScalarExpr($ternary->else)) {
+ return null;
+ }
+
+ /** @var Node\Expr $if */
+ $if = $ternary->if;
+
+ /** @var Node\Expr $else */
+ $else = $ternary->else;
+
+ $ifType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($if);
+ $elseType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($else);
+
+ return new TernaryIfElseTypes($ifType, $elseType);
+ }
+}
diff --git a/rules/TypeDeclaration/ValueObject/TernaryIfElseTypes.php b/rules/TypeDeclaration/ValueObject/TernaryIfElseTypes.php
new file mode 100644
index 00000000000..2c1562aeebf
--- /dev/null
+++ b/rules/TypeDeclaration/ValueObject/TernaryIfElseTypes.php
@@ -0,0 +1,26 @@
+firstType;
+ }
+
+ public function getSecondType(): Type
+ {
+ return $this->secondType;
+ }
+}