allAssignNodePropertyTypeInferer = $allAssignNodePropertyTypeInferer; $this->propertyTypeDecorator = $propertyTypeDecorator; $this->varTagRemover = $varTagRemover; $this->makePropertyTypedGuard = $makePropertyTypedGuard; } public function configure(array $configuration) : void { $this->inlinePublic = $configuration[self::INLINE_PUBLIC] ?? (bool) \current($configuration); } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Add typed property from assigned types', [new ConfiguredCodeSample(<<<'CODE_SAMPLE' final class SomeClass { private $name; public function run() { $this->name = 'string'; } } CODE_SAMPLE , <<<'CODE_SAMPLE' final class SomeClass { private string|null $name = null; public function run() { $this->name = 'string'; } } CODE_SAMPLE , [self::INLINE_PUBLIC => \false])]); } /** * @return array> */ public function getNodeTypes() : array { return [Property::class]; } public function provideMinPhpVersion() : int { return PhpVersionFeature::TYPED_PROPERTIES; } /** * @param Property $node */ public function refactor(Node $node) : ?Node { if (!$this->makePropertyTypedGuard->isLegal($node, $this->inlinePublic)) { return null; } // non-private property can be anything with not inline public configured if (!$node->isPrivate() && !$this->inlinePublic) { return null; } $inferredType = $this->allAssignNodePropertyTypeInferer->inferProperty($node); if (!$inferredType instanceof Type) { return null; } if ($inferredType instanceof MixedType) { return null; } $inferredType = $this->decorateTypeWithNullableIfDefaultPropertyNull($node, $inferredType); $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($inferredType, TypeKind::PROPERTY); if ($typeNode === null) { return null; } $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); if ($inferredType instanceof UnionType) { $this->propertyTypeDecorator->decoratePropertyUnionType($inferredType, $typeNode, $node, $phpDocInfo, \false); } else { $node->type = $typeNode; } if (!$node->type instanceof Node) { return null; } $this->varTagRemover->removeVarTagIfUseless($phpDocInfo, $node); return $node; } private function decorateTypeWithNullableIfDefaultPropertyNull(Property $property, Type $inferredType) : Type { $defaultExpr = $property->props[0]->default; if (!$defaultExpr instanceof Expr) { return $inferredType; } if (!$this->valueResolver->isNull($defaultExpr)) { return $inferredType; } if (TypeCombinator::containsNull($inferredType)) { return $inferredType; } return TypeCombinator::addNull($inferredType); } }