self, $statements_analyzer ); $fq_class_name = (string) $statements_analyzer->node_data->getType($stmt->class); $codebase = $statements_analyzer->getCodebase(); $prop_name = $stmt->name; if (!$prop_name instanceof PhpParser\Node\Identifier) { if (ExpressionAnalyzer::analyze($statements_analyzer, $prop_name, $context) === false) { return false; } if ($fq_class_name && !$context->ignore_variable_property) { $codebase->analyzer->addMixedMemberName( strtolower($fq_class_name) . '::$', $context->calling_method_id ?: $statements_analyzer->getFileName() ); } return; } $property_id = $fq_class_name . '::$' . $prop_name; if (!$codebase->properties->propertyExists($property_id, false, $statements_analyzer, $context)) { if (IssueBuffer::accepts( new UndefinedPropertyAssignment( 'Static property ' . $property_id . ' is not defined', new CodeLocation($statements_analyzer->getSource(), $stmt), $property_id ), $statements_analyzer->getSuppressedIssues() )) { // fall through } return; } if (ClassLikeAnalyzer::checkPropertyVisibility( $property_id, $context, $statements_analyzer, new CodeLocation($statements_analyzer->getSource(), $stmt), $statements_analyzer->getSuppressedIssues() ) === false) { return false; } $declaring_property_class = (string) $codebase->properties->getDeclaringClassForProperty( $fq_class_name . '::$' . $prop_name->name, false ); $declaring_property_id = strtolower((string) $declaring_property_class) . '::$' . $prop_name; if ($codebase->alter_code && $stmt->class instanceof PhpParser\Node\Name) { $moved_class = $codebase->classlikes->handleClassLikeReferenceInMigration( $codebase, $statements_analyzer, $stmt->class, $fq_class_name, $context->calling_method_id ); if (!$moved_class) { foreach ($codebase->property_transforms as $original_pattern => $transformation) { if ($declaring_property_id === $original_pattern) { list($old_declaring_fq_class_name) = explode('::$', $declaring_property_id); list($new_fq_class_name, $new_property_name) = explode('::$', $transformation); $file_manipulations = []; if (strtolower($new_fq_class_name) !== strtolower($old_declaring_fq_class_name)) { $file_manipulations[] = new \Psalm\FileManipulation( (int) $stmt->class->getAttribute('startFilePos'), (int) $stmt->class->getAttribute('endFilePos') + 1, Type::getStringFromFQCLN( $new_fq_class_name, $statements_analyzer->getNamespace(), $statements_analyzer->getAliasedClassesFlipped(), null ) ); } $file_manipulations[] = new \Psalm\FileManipulation( (int) $stmt->name->getAttribute('startFilePos'), (int) $stmt->name->getAttribute('endFilePos') + 1, '$' . $new_property_name ); FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations); } } } } $class_storage = $codebase->classlike_storage_provider->get($declaring_property_class); $property_storage = $class_storage->properties[$prop_name->name]; if ($var_id) { $context->vars_in_scope[$var_id] = $assignment_value_type; } $class_property_type = $codebase->properties->getPropertyType( $property_id, true, $statements_analyzer, $context ); if (!$class_property_type) { $class_property_type = Type::getMixed(); if (!$assignment_value_type->hasMixed()) { if ($property_storage->suggested_type) { $property_storage->suggested_type = Type::combineUnionTypes( $assignment_value_type, $property_storage->suggested_type ); } else { $property_storage->suggested_type = Type::combineUnionTypes( Type::getNull(), $assignment_value_type ); } } } else { $class_property_type = clone $class_property_type; } if ($assignment_value_type->hasMixed()) { return null; } if ($class_property_type->hasMixed()) { return null; } $class_property_type = \Psalm\Internal\Type\TypeExpander::expandUnion( $codebase, $class_property_type, $fq_class_name, $fq_class_name, $class_storage->parent_class ); $union_comparison_results = new \Psalm\Internal\Analyzer\TypeComparisonResult(); $type_match_found = TypeAnalyzer::isContainedBy( $codebase, $assignment_value_type, $class_property_type, true, true, $union_comparison_results ); if ($union_comparison_results->type_coerced) { if ($union_comparison_results->type_coerced_from_mixed) { if (IssueBuffer::accepts( new MixedPropertyTypeCoercion( $var_id . ' expects \'' . $class_property_type->getId() . '\', ' . ' parent type `' . $assignment_value_type->getId() . '` provided', new CodeLocation( $statements_analyzer->getSource(), $assignment_value ?: $stmt, $context->include_location ), $property_id ), $statements_analyzer->getSuppressedIssues() )) { // keep soldiering on } } else { if (IssueBuffer::accepts( new PropertyTypeCoercion( $var_id . ' expects \'' . $class_property_type->getId() . '\', ' . ' parent type \'' . $assignment_value_type->getId() . '\' provided', new CodeLocation( $statements_analyzer->getSource(), $assignment_value ?: $stmt, $context->include_location ), $property_id ), $statements_analyzer->getSuppressedIssues() )) { // keep soldiering on } } } if ($union_comparison_results->to_string_cast) { if (IssueBuffer::accepts( new ImplicitToStringCast( $var_id . ' expects \'' . $class_property_type . '\', ' . '\'' . $assignment_value_type . '\' provided with a __toString method', new CodeLocation( $statements_analyzer->getSource(), $assignment_value ?: $stmt, $context->include_location ) ), $statements_analyzer->getSuppressedIssues() )) { // fall through } } if (!$type_match_found && !$union_comparison_results->type_coerced) { if (TypeAnalyzer::canBeContainedBy($codebase, $assignment_value_type, $class_property_type)) { if (IssueBuffer::accepts( new PossiblyInvalidPropertyAssignmentValue( $var_id . ' with declared type \'' . $class_property_type->getId() . '\' cannot be assigned type \'' . $assignment_value_type->getId() . '\'', new CodeLocation( $statements_analyzer->getSource(), $assignment_value ?: $stmt ), $property_id ), $statements_analyzer->getSuppressedIssues() )) { return false; } } else { if (IssueBuffer::accepts( new InvalidPropertyAssignmentValue( $var_id . ' with declared type \'' . $class_property_type->getId() . '\' cannot be assigned type \'' . $assignment_value_type->getId() . '\'', new CodeLocation( $statements_analyzer->getSource(), $assignment_value ?: $stmt ), $property_id ), $statements_analyzer->getSuppressedIssues() )) { return false; } } } if ($var_id) { $context->vars_in_scope[$var_id] = $assignment_value_type; } return null; } }