strictNativeFunctionReturnTypeAnalyzer = $strictNativeFunctionReturnTypeAnalyzer; $this->typeFactory = $typeFactory; $this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard; $this->staticTypeMapper = $staticTypeMapper; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Add strict return type based native function or class method return', [new CodeSample(<<<'CODE_SAMPLE' final class SomeClass { public function run() { return strlen('value'); } } CODE_SAMPLE , <<<'CODE_SAMPLE' final class SomeClass { public function run(): int { return strlen('value'); } } CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [ClassMethod::class, Closure::class, Function_::class]; } /** * @param ClassMethod|Closure|Function_ $node */ public function refactorWithScope(Node $node, Scope $scope) : ?Node { if ($node->returnType !== null) { return null; } if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { return null; } $nativeCallLikes = $this->strictNativeFunctionReturnTypeAnalyzer->matchAlwaysReturnNativeCallLikes($node); if ($nativeCallLikes === null) { return null; } $callLikeTypes = []; foreach ($nativeCallLikes as $nativeCallLike) { $callLikeTypes[] = $this->getType($nativeCallLike); } $returnType = $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($callLikeTypes); if ($returnType instanceof MixedType) { return null; } $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN); if (!$returnTypeNode instanceof Node) { return null; } $node->returnType = $returnTypeNode; return $node; } public function provideMinPhpVersion() : int { return PhpVersion::PHP_70; } }