fn validate_inline_fragments_with_parent()

in compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_directive.rs [135:248]


    fn validate_inline_fragments_with_parent(
        &self,
        parent_field: &LinkedField,
        // Cannot accept an iterator here because we need the length of the vec!
        fragment_spreads: Vec<&'a InlineFragment>,
    ) -> DiagnosticsResult<()> {
        // If we have no fragment spreads, return early
        if fragment_spreads.is_empty() {
            return Ok(());
        }

        // If a linked field contains inline fragments, it must *only* contain inline fragments.
        // This is because there exists a limitation of our typegen: if there are fields within
        // inline fragments with type refinements, and at the top-level, the parent field is not
        // emitted as a disjoint union where the key is the __typename field. Instead, the union
        // is flattened and every field is optional. This breaks type safety for updatable fragments,
        // as users would be able to assign to scalar fields that are not present on a given type.
        let mut errors = vec![];
        if parent_field.selections.len() != fragment_spreads.len() {
            errors.push(Diagnostic::error(
                ValidationMessage::UpdatableOnlyInlineFragments {
                    outer_type_plural: self.executable_definition_info.unwrap().type_plural,
                },
                parent_field.definition.location,
            ));
        }

        // Furthermore, inline fragments are only allowed if the parent type is an interface or union
        let parent_field_id = parent_field.definition.item;
        let parent_named_type = self.program.schema.field(parent_field_id).type_.inner();
        if !parent_named_type.is_abstract_type() {
            errors.push(Diagnostic::error(
                ValidationMessage::UpdatableInlineFragmentsOnlyOnInterfacesOrUnions {
                    outer_type_plural: self.executable_definition_info.unwrap().type_plural,
                },
                parent_field.definition.location,
            ));
        }

        let mut previously_encountered_concrete_types = HashSet::new();
        for fragment_spread in fragment_spreads.into_iter() {
            // A fragment spread on a linked field is valid iff:
            // - it contains a type condition
            // - that type condition is for a concrete type
            // - that concrete type has not occurred before
            // - it contains a typename field with no alias

            match fragment_spread.type_condition {
                None => errors.push(Diagnostic::error(
                    ValidationMessage::UpdatableInlineFragmentsRequireTypeConditions {
                        outer_type_plural: self.executable_definition_info.unwrap().type_plural,
                        parent_field_type: self.program.schema.get_type_name(parent_named_type),
                    },
                    parent_field.definition.location,
                )),
                Some(type_condition) => {
                    let type_condition_name = self.program.schema.get_type_name(type_condition);
                    if type_condition.is_abstract_type() {
                        errors.push(Diagnostic::error(
                            ValidationMessage::UpdatableInlineFragmentsTypeConditionsMustBeConcrete {
                                outer_type_plural: self.executable_definition_info.unwrap().type_plural,
                                type_condition: type_condition_name,
                            },
                            parent_field.definition.location,
                        ))
                    }

                    if previously_encountered_concrete_types.contains(&type_condition_name) {
                        errors.push(Diagnostic::error(
                            ValidationMessage::UpdatablePreviouslyEncounteredTypeCondition {
                                outer_type_plural: self
                                    .executable_definition_info
                                    .unwrap()
                                    .type_plural,
                                type_condition: type_condition_name,
                                parent_field_alias_or_name: parent_field
                                    .alias_or_name(&self.program.schema),
                            },
                            parent_field.definition.location,
                        ));
                    } else {
                        previously_encountered_concrete_types.insert(type_condition_name);
                    }
                }
            }

            // Attempt to find a typename field with no alias, in order to guarantee that
            // the linked field (parent_field) with fragment spreads is written by
            // relay-typegen as a disjoint union.
            if !fragment_spread.selections.iter().any(|selection| {
                if let Selection::ScalarField(scalar_field) = selection {
                    scalar_field.definition.item == self.program.schema.typename_field()
                        && scalar_field.alias.is_none()
                } else {
                    false
                }
            }) {
                errors.push(Diagnostic::error(
                    ValidationMessage::UpdatableInlineFragmentsMustHaveTypenameFields {
                        outer_type_plural: self.executable_definition_info.unwrap().type_plural,
                        parent_field_alias_or_name: parent_field
                            .alias_or_name(&self.program.schema),
                    },
                    parent_field.definition.location,
                ))
            }
        }

        if errors.is_empty() {
            Ok(())
        } else {
            Err(errors)
        }
    }