Skip to main content

apollo_compiler/schema/
from_ast.rs

1use super::*;
2use crate::ast::OperationType;
3use crate::validation::WithErrors;
4use indexmap::map::Entry;
5use std::sync::Arc;
6
7#[derive(Clone)]
8pub struct SchemaBuilder {
9    adopt_orphan_extensions: bool,
10    ignore_builtin_redefinitions: bool,
11    validate_default_values: bool,
12    pub(crate) schema: Schema,
13    schema_definition: SchemaDefinitionStatus,
14    orphan_type_extensions: IndexMap<Name, Vec<ast::Definition>>,
15    pub(crate) errors: DiagnosticList,
16}
17
18#[derive(Clone)]
19enum SchemaDefinitionStatus {
20    Found,
21    NoneSoFar {
22        orphan_extensions: Vec<Node<ast::SchemaExtension>>,
23    },
24}
25
26impl Default for SchemaBuilder {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl SchemaBuilder {
33    pub(crate) fn built_in() -> &'static Self {
34        static BUILT_IN: std::sync::OnceLock<SchemaBuilder> = std::sync::OnceLock::new();
35        BUILT_IN.get_or_init(|| {
36            let mut builder = SchemaBuilder {
37                adopt_orphan_extensions: false,
38                ignore_builtin_redefinitions: false,
39                validate_default_values: true,
40                schema: Schema {
41                    sources: Default::default(),
42                    schema_definition: Node::new(SchemaDefinition {
43                        description: None,
44                        directives: DirectiveList::default(),
45                        query: None,
46                        mutation: None,
47                        subscription: None,
48                    }),
49                    directive_definitions: IndexMap::with_hasher(Default::default()),
50                    types: IndexMap::with_hasher(Default::default()),
51                    validate_default_values: true,
52                },
53                schema_definition: SchemaDefinitionStatus::NoneSoFar {
54                    orphan_extensions: Vec::new(),
55                },
56                orphan_type_extensions: IndexMap::with_hasher(Default::default()),
57                errors: DiagnosticList::new(Default::default()),
58            };
59            let input = include_str!("../built_in_types.graphql").to_owned();
60            let path = "built_in.graphql";
61            let id = FileId::BUILT_IN;
62            let ast = ast::Document::parser().parse_ast_inner(input, path, id, &mut builder.errors);
63            let executable_definitions_are_errors = true;
64            builder.add_ast_document(&ast, executable_definitions_are_errors);
65            assert!(builder.errors.is_empty());
66            builder
67        })
68    }
69
70    /// Returns a new schema builder initialized with built-in directives, built-in scalars,
71    /// and introspection types
72    pub fn new() -> Self {
73        Self::built_in().clone()
74    }
75
76    /// Configure the builder so that “orphan” schema extensions and type extensions
77    /// (without a corresponding definition) are “adopted”:
78    /// accepted as if extending an empty definition instead of being rejected as errors.
79    pub fn adopt_orphan_extensions(mut self) -> Self {
80        self.adopt_orphan_extensions = true;
81        self
82    }
83
84    /// Configure the builder to allow SDL to contain built-in type re-definitions.
85    /// Re-definitions are going to be effectively ignored and compiler will continue to use
86    /// built-in GraphQL spec definitions.
87    pub fn ignore_builtin_redefinitions(mut self) -> Self {
88        self.ignore_builtin_redefinitions = true;
89        self
90    }
91
92    /// Configure whether to validate default values of input fields and arguments
93    /// against their declared types during schema validation.
94    ///
95    /// Defaults to `true`. Set to `false` to accept schemas where default values
96    /// don't match their field or argument types.
97    pub fn validate_default_values(mut self, enabled: bool) -> Self {
98        self.validate_default_values = enabled;
99        self
100    }
101
102    /// Parse an input file with the default configuration as an additional input for this schema.
103    ///
104    /// Create a [`Parser`] to use different parser configuration.
105    pub fn parse(mut self, source_text: impl Into<String>, path: impl AsRef<Path>) -> Self {
106        Parser::new().parse_into_schema_builder(source_text, path, &mut self);
107        self
108    }
109
110    /// Add an AST document to the schema being built
111    ///
112    /// Executable definitions, if any, will be silently ignored.
113    pub fn add_ast(mut self, document: &ast::Document) -> Self {
114        let executable_definitions_are_errors = true;
115        self.add_ast_document(document, executable_definitions_are_errors);
116        self
117    }
118
119    pub(crate) fn add_ast_document(
120        &mut self,
121        document: &ast::Document,
122        executable_definitions_are_errors: bool,
123    ) {
124        Arc::make_mut(&mut self.errors.sources)
125            .extend(document.sources.iter().map(|(k, v)| (*k, v.clone())));
126        self.add_ast_document_not_adding_sources(document, executable_definitions_are_errors)
127    }
128
129    pub(crate) fn add_ast_document_not_adding_sources(
130        &mut self,
131        document: &ast::Document,
132        executable_definitions_are_errors: bool,
133    ) {
134        for definition in &document.definitions {
135            macro_rules! type_definition {
136                ($def: ident, $Type: ident, is_scalar = $is_scalar: literal) => {
137                    match self.schema.types.entry($def.name.clone()) {
138                        Entry::Vacant(entry) => {
139                            let extended_def = $Type::from_ast(
140                                &mut self.errors,
141                                $def,
142                                self.orphan_type_extensions
143                                    .shift_remove(&$def.name)
144                                    .unwrap_or_default(),
145                            );
146                            entry.insert(extended_def.into());
147                        }
148                        Entry::Occupied(entry) => {
149                            let previous = entry.get();
150                            if self.ignore_builtin_redefinitions && previous.is_built_in() {
151                                continue;
152                            }
153
154                            if $is_scalar && previous.is_built_in() {
155                                self.errors.push(
156                                    $def.location(),
157                                    BuildError::BuiltInScalarTypeRedefinition,
158                                )
159                            } else {
160                                self.errors.push(
161                                    $def.name.location(),
162                                    BuildError::TypeDefinitionCollision {
163                                        previous_location: previous.name().location(),
164                                        name: $def.name.clone(),
165                                    },
166                                )
167                            }
168                        }
169                    }
170                };
171            }
172            macro_rules! type_extension {
173                ($ext: ident, $Kind: ident) => {
174                    if let Some(ty) = self.schema.types.get_mut(&$ext.name) {
175                        if let ExtendedType::$Kind(ty) = ty {
176                            ty.make_mut().extend_ast(&mut self.errors, $ext)
177                        } else {
178                            self.errors.push(
179                                $ext.name.location(),
180                                BuildError::TypeExtensionKindMismatch {
181                                    name: $ext.name.clone(),
182                                    describe_ext: definition.describe(),
183                                    def_location: ty.name().location(),
184                                    describe_def: ty.describe(),
185                                },
186                            )
187                        }
188                    } else {
189                        self.orphan_type_extensions
190                            .entry($ext.name.clone())
191                            .or_default()
192                            .push(definition.clone())
193                    }
194                };
195            }
196            match definition {
197                ast::Definition::SchemaDefinition(def) => match &self.schema_definition {
198                    SchemaDefinitionStatus::NoneSoFar { orphan_extensions } => {
199                        self.schema.schema_definition =
200                            SchemaDefinition::from_ast(&mut self.errors, def, orphan_extensions);
201                        self.schema_definition = SchemaDefinitionStatus::Found;
202                    }
203                    SchemaDefinitionStatus::Found => self.errors.push(
204                        def.location(),
205                        BuildError::SchemaDefinitionCollision {
206                            previous_location: self.schema.schema_definition.location(),
207                        },
208                    ),
209                },
210                ast::Definition::DirectiveDefinition(def) => {
211                    match self.schema.directive_definitions.entry(def.name.clone()) {
212                        Entry::Vacant(entry) => {
213                            entry.insert(def.clone());
214                        }
215                        Entry::Occupied(mut entry) => {
216                            let previous = entry.get_mut();
217                            if previous.is_built_in() {
218                                // https://github.com/apollographql/apollo-rs/issues/656
219                                // Re-defining a built-in definition is allowed, but only once.
220                                // (`is_built_in` is based on file ID, not directive name,
221                                // so the new definition won’t be considered built-in.)
222                                *previous = def.clone()
223                            } else {
224                                self.errors.push(
225                                    def.name.location(),
226                                    BuildError::DirectiveDefinitionCollision {
227                                        previous_location: previous.name.location(),
228                                        name: def.name.clone(),
229                                    },
230                                )
231                            }
232                        }
233                    }
234                }
235                ast::Definition::ScalarTypeDefinition(def) => {
236                    type_definition!(def, ScalarType, is_scalar = true)
237                }
238                ast::Definition::ObjectTypeDefinition(def) => {
239                    type_definition!(def, ObjectType, is_scalar = false)
240                }
241                ast::Definition::InterfaceTypeDefinition(def) => {
242                    type_definition!(def, InterfaceType, is_scalar = false)
243                }
244                ast::Definition::UnionTypeDefinition(def) => {
245                    type_definition!(def, UnionType, is_scalar = false)
246                }
247                ast::Definition::EnumTypeDefinition(def) => {
248                    type_definition!(def, EnumType, is_scalar = false)
249                }
250                ast::Definition::InputObjectTypeDefinition(def) => {
251                    type_definition!(def, InputObjectType, is_scalar = false)
252                }
253                ast::Definition::SchemaExtension(ext) => match &mut self.schema_definition {
254                    SchemaDefinitionStatus::Found => self
255                        .schema
256                        .schema_definition
257                        .make_mut()
258                        .extend_ast(&mut self.errors, ext),
259                    SchemaDefinitionStatus::NoneSoFar { orphan_extensions } => {
260                        orphan_extensions.push(ext.clone())
261                    }
262                },
263                ast::Definition::ScalarTypeExtension(ext) => type_extension!(ext, Scalar),
264                ast::Definition::ObjectTypeExtension(ext) => type_extension!(ext, Object),
265                ast::Definition::InterfaceTypeExtension(ext) => type_extension!(ext, Interface),
266                ast::Definition::UnionTypeExtension(ext) => type_extension!(ext, Union),
267                ast::Definition::EnumTypeExtension(ext) => type_extension!(ext, Enum),
268                ast::Definition::InputObjectTypeExtension(ext) => type_extension!(ext, InputObject),
269                ast::Definition::OperationDefinition(_)
270                | ast::Definition::FragmentDefinition(_) => {
271                    if executable_definitions_are_errors {
272                        self.errors.push(
273                            definition.location(),
274                            BuildError::ExecutableDefinition {
275                                describe: definition.describe(),
276                            },
277                        )
278                    }
279                }
280            }
281        }
282    }
283
284    pub fn iter_orphan_extension_types(&self) -> impl Iterator<Item = &Name> {
285        self.orphan_type_extensions.keys()
286    }
287
288    /// Returns the schema built from all added documents
289    #[allow(clippy::result_large_err)] // Typically not called very often
290    pub fn build(self) -> Result<Schema, WithErrors<Schema>> {
291        let (schema, errors) = self.build_inner();
292        errors.into_result_with(schema)
293    }
294
295    pub(crate) fn build_inner(self) -> (Schema, DiagnosticList) {
296        let SchemaBuilder {
297            adopt_orphan_extensions,
298            ignore_builtin_redefinitions: _allow_builtin_redefinitions,
299            validate_default_values,
300            mut schema,
301            schema_definition,
302            orphan_type_extensions,
303            mut errors,
304        } = self;
305        schema.validate_default_values = validate_default_values;
306        schema.sources = errors.sources.clone();
307
308        // process orphan type extensions (https://github.com/apollographql/apollo-rs/pull/678) first,
309        // so they can be reflected on the implicit schema definition below
310        if adopt_orphan_extensions {
311            for (type_name, extensions) in orphan_type_extensions {
312                let type_def = adopt_type_extensions(&mut errors, &type_name, &extensions);
313                let previous = schema.types.insert(type_name, type_def);
314                assert!(previous.is_none());
315            }
316        } else {
317            for extensions in orphan_type_extensions.values() {
318                for ext in extensions {
319                    let name = ext.name().unwrap().clone();
320                    errors.push(name.location(), BuildError::OrphanTypeExtension { name })
321                }
322            }
323        }
324
325        match schema_definition {
326            SchemaDefinitionStatus::Found => {}
327            SchemaDefinitionStatus::NoneSoFar { orphan_extensions } => {
328                // This a macro rather than a closure to generate separate `static`s
329                let schema_def = schema.schema_definition.make_mut();
330                if adopt_orphan_extensions {
331                    // https://github.com/apollographql/apollo-rs/pull/678
332                    // In this opt-in mode we unconditionally assume
333                    // an implicit schema definition to extend
334                    for ext in &orphan_extensions {
335                        schema_def.extend_ast(&mut errors, ext)
336                    }
337                    if schema_def.query.is_none()
338                        && schema_def.mutation.is_none()
339                        && schema_def.subscription.is_none()
340                    {
341                        add_implicit_root_types(schema_def, &schema.types);
342                    }
343                } else {
344                    let has_implicit_root_operation =
345                        add_implicit_root_types(schema_def, &schema.types);
346                    if has_implicit_root_operation {
347                        // https://github.com/apollographql/apollo-rs/issues/682
348                        // If we have no explict `schema` definition but do have object type(s)
349                        // with a default type name for root operations,
350                        // an implicit schema definition is generated with those root operations.
351                        // That implict definition can be extended:
352                        for ext in &orphan_extensions {
353                            schema_def.extend_ast(&mut errors, ext)
354                        }
355                    } else {
356                        for ext in &orphan_extensions {
357                            errors.push(ext.location(), BuildError::OrphanSchemaExtension)
358                        }
359                    }
360                }
361            }
362        }
363        (schema, errors)
364    }
365}
366
367fn add_implicit_root_types(
368    schema_def: &mut SchemaDefinition,
369    types: &IndexMap<Name, ExtendedType>,
370) -> bool {
371    let mut has_implicit_root_operation = false;
372    for (operation_type, root_operation) in [
373        (OperationType::Query, &mut schema_def.query),
374        (OperationType::Mutation, &mut schema_def.mutation),
375        (OperationType::Subscription, &mut schema_def.subscription),
376    ] {
377        let name = operation_type.default_type_name();
378        if types.get(&name).is_some_and(|def| def.is_object()) {
379            *root_operation = Some(name.into());
380            has_implicit_root_operation = true
381        }
382    }
383    has_implicit_root_operation
384}
385
386fn adopt_type_extensions(
387    errors: &mut DiagnosticList,
388    type_name: &Name,
389    extensions: &[ast::Definition],
390) -> ExtendedType {
391    macro_rules! extend {
392        ($( $ExtensionVariant: path => $describe: literal $empty_def: expr )+) => {
393            match &extensions[0] {
394                $(
395                    $ExtensionVariant(first_ext) => {
396                        let mut def = $empty_def;
397                        for ext in extensions {
398                            if let $ExtensionVariant(ext) = ext {
399                                def.extend_ast(errors, ext)
400                            } else {
401                                let ext_name = ext.name().unwrap();
402                                errors.push(
403                                    ext_name.location(),
404                                    BuildError::TypeExtensionKindMismatch {
405                                        name: ext_name.clone(),
406                                        describe_ext: ext.describe(),
407                                        def_location: type_name.location(),
408                                        describe_def: $describe,
409                                    }
410                                )
411                            }
412                        }
413                        ExtendedType::from(first_ext.same_location(def))
414                    }
415                )+
416                _ => unreachable!(),
417            }
418        };
419    }
420    let name = type_name.clone();
421    extend! {
422        ast::Definition::ScalarTypeExtension => "a scalar type" ScalarType {
423            description: Default::default(),
424            name,
425            directives: Default::default(),
426        }
427        ast::Definition::ObjectTypeExtension => "an object type" ObjectType {
428            description: Default::default(),
429            name,
430            implements_interfaces: Default::default(),
431            directives: Default::default(),
432            fields: Default::default(),
433        }
434        ast::Definition::InterfaceTypeExtension => "an interface type" InterfaceType {
435            description: Default::default(),
436            name,
437            implements_interfaces: Default::default(),
438            directives: Default::default(),
439            fields: Default::default(),
440        }
441        ast::Definition::UnionTypeExtension => "a union type" UnionType {
442            description: Default::default(),
443            name,
444            directives: Default::default(),
445            members: Default::default(),
446        }
447        ast::Definition::EnumTypeExtension => "an enum type" EnumType {
448            description: Default::default(),
449            name,
450            directives: Default::default(),
451            values: Default::default(),
452        }
453        ast::Definition::InputObjectTypeExtension => "an input object type" InputObjectType {
454            description: Default::default(),
455            name,
456            directives: Default::default(),
457            fields: Default::default(),
458        }
459    }
460}
461
462impl SchemaDefinition {
463    fn from_ast(
464        errors: &mut DiagnosticList,
465        definition: &Node<ast::SchemaDefinition>,
466        extensions: &[Node<ast::SchemaExtension>],
467    ) -> Node<Self> {
468        let mut root = Self {
469            description: definition.description.clone(),
470            directives: definition
471                .directives
472                .iter()
473                .map(|d| d.to_component(ComponentOrigin::Definition))
474                .collect(),
475            query: None,
476            mutation: None,
477            subscription: None,
478        };
479        root.add_root_operations(
480            errors,
481            ComponentOrigin::Definition,
482            &definition.root_operations,
483        );
484        for ext in extensions {
485            root.extend_ast(errors, ext)
486        }
487        definition.same_location(root)
488    }
489
490    fn extend_ast(&mut self, errors: &mut DiagnosticList, extension: &Node<ast::SchemaExtension>) {
491        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
492        self.directives.extend(
493            extension
494                .directives
495                .iter()
496                .map(|d| d.to_component(origin.clone())),
497        );
498        self.add_root_operations(errors, origin, &extension.root_operations)
499    }
500
501    fn add_root_operations(
502        &mut self,
503        errors: &mut DiagnosticList,
504        origin: ComponentOrigin,
505        root_operations: &[Node<(OperationType, Name)>],
506    ) {
507        for op in root_operations {
508            let (operation_type, object_type_name) = &**op;
509            let entry = match operation_type {
510                OperationType::Query => &mut self.query,
511                OperationType::Mutation => &mut self.mutation,
512                OperationType::Subscription => &mut self.subscription,
513            };
514            match entry {
515                None => *entry = Some(object_type_name.to_component(origin.clone())),
516                Some(previous) => errors.push(
517                    op.location(),
518                    BuildError::DuplicateRootOperation {
519                        previous_location: previous.location(),
520                        operation_type: operation_type.name(),
521                    },
522                ),
523            }
524        }
525    }
526}
527
528impl ScalarType {
529    fn from_ast(
530        errors: &mut DiagnosticList,
531        definition: &Node<ast::ScalarTypeDefinition>,
532        extensions: Vec<ast::Definition>,
533    ) -> Node<Self> {
534        let mut ty = Self {
535            description: definition.description.clone(),
536            name: definition.name.clone(),
537            directives: definition
538                .directives
539                .iter()
540                .map(|d| d.to_component(ComponentOrigin::Definition))
541                .collect(),
542        };
543        for def in &extensions {
544            if let ast::Definition::ScalarTypeExtension(ext) = def {
545                ty.extend_ast(errors, ext)
546            }
547        }
548        definition.same_location(ty)
549    }
550
551    fn extend_ast(
552        &mut self,
553        _errors: &mut DiagnosticList,
554        extension: &Node<ast::ScalarTypeExtension>,
555    ) {
556        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
557        self.directives.extend(
558            extension
559                .directives
560                .iter()
561                .map(|d| d.to_component(origin.clone())),
562        );
563    }
564}
565
566impl ObjectType {
567    fn from_ast(
568        errors: &mut DiagnosticList,
569        definition: &Node<ast::ObjectTypeDefinition>,
570        extensions: Vec<ast::Definition>,
571    ) -> Node<Self> {
572        let mut ty = Self {
573            description: definition.description.clone(),
574            name: definition.name.clone(),
575            implements_interfaces: collect_sticky_set(
576                definition
577                    .implements_interfaces
578                    .iter()
579                    .map(|name| name.to_component(ComponentOrigin::Definition)),
580                |prev, dup| {
581                    errors.push(
582                        dup.location(),
583                        BuildError::DuplicateImplementsInterfaceInObject {
584                            name_at_previous_location: prev.name.clone(),
585                            type_name: definition.name.clone(),
586                        },
587                    )
588                },
589            ),
590            directives: definition
591                .directives
592                .iter()
593                .map(|d| d.to_component(ComponentOrigin::Definition))
594                .collect(),
595            fields: collect_sticky(
596                definition
597                    .fields
598                    .iter()
599                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
600                |prev_key, dup_value| {
601                    errors.push(
602                        dup_value.location(),
603                        BuildError::ObjectFieldNameCollision {
604                            name_at_previous_location: prev_key.clone(),
605                            type_name: definition.name.clone(),
606                        },
607                    )
608                },
609            ),
610        };
611        for def in &extensions {
612            if let ast::Definition::ObjectTypeExtension(ext) = def {
613                ty.extend_ast(errors, ext)
614            }
615        }
616        definition.same_location(ty)
617    }
618
619    fn extend_ast(
620        &mut self,
621        errors: &mut DiagnosticList,
622        extension: &Node<ast::ObjectTypeExtension>,
623    ) {
624        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
625        self.directives.extend(
626            extension
627                .directives
628                .iter()
629                .map(|d| d.to_component(origin.clone())),
630        );
631        extend_sticky_set(
632            &mut self.implements_interfaces,
633            extension
634                .implements_interfaces
635                .iter()
636                .map(|name| name.to_component(origin.clone())),
637            |prev, dup| {
638                errors.push(
639                    dup.location(),
640                    BuildError::DuplicateImplementsInterfaceInObject {
641                        name_at_previous_location: prev.name.clone(),
642                        type_name: extension.name.clone(),
643                    },
644                )
645            },
646        );
647        extend_sticky(
648            &mut self.fields,
649            extension
650                .fields
651                .iter()
652                .map(|field| (&field.name, field.to_component(origin.clone()))),
653            |prev_key, dup_value| {
654                errors.push(
655                    dup_value.location(),
656                    BuildError::ObjectFieldNameCollision {
657                        name_at_previous_location: prev_key.clone(),
658                        type_name: extension.name.clone(),
659                    },
660                )
661            },
662        );
663    }
664}
665
666impl InterfaceType {
667    fn from_ast(
668        errors: &mut DiagnosticList,
669        definition: &Node<ast::InterfaceTypeDefinition>,
670        extensions: Vec<ast::Definition>,
671    ) -> Node<Self> {
672        let mut ty = Self {
673            description: definition.description.clone(),
674            name: definition.name.clone(),
675            implements_interfaces: collect_sticky_set(
676                definition
677                    .implements_interfaces
678                    .iter()
679                    .map(|name| name.to_component(ComponentOrigin::Definition)),
680                |prev, dup| {
681                    errors.push(
682                        dup.location(),
683                        BuildError::DuplicateImplementsInterfaceInInterface {
684                            name_at_previous_location: prev.name.clone(),
685                            type_name: definition.name.clone(),
686                        },
687                    )
688                },
689            ),
690            directives: definition
691                .directives
692                .iter()
693                .map(|d| d.to_component(ComponentOrigin::Definition))
694                .collect(),
695            fields: collect_sticky(
696                definition
697                    .fields
698                    .iter()
699                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
700                |prev_key, dup_value| {
701                    errors.push(
702                        dup_value.location(),
703                        BuildError::InterfaceFieldNameCollision {
704                            name_at_previous_location: prev_key.clone(),
705                            type_name: definition.name.clone(),
706                        },
707                    )
708                },
709            ),
710        };
711        for def in &extensions {
712            if let ast::Definition::InterfaceTypeExtension(ext) = def {
713                ty.extend_ast(errors, ext)
714            }
715        }
716        definition.same_location(ty)
717    }
718
719    fn extend_ast(
720        &mut self,
721        errors: &mut DiagnosticList,
722        extension: &Node<ast::InterfaceTypeExtension>,
723    ) {
724        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
725        self.directives.extend(
726            extension
727                .directives
728                .iter()
729                .map(|d| d.to_component(origin.clone())),
730        );
731        extend_sticky_set(
732            &mut self.implements_interfaces,
733            extension
734                .implements_interfaces
735                .iter()
736                .map(|name| name.to_component(origin.clone())),
737            |prev, dup| {
738                errors.push(
739                    dup.location(),
740                    BuildError::DuplicateImplementsInterfaceInInterface {
741                        name_at_previous_location: prev.name.clone(),
742                        type_name: extension.name.clone(),
743                    },
744                )
745            },
746        );
747        extend_sticky(
748            &mut self.fields,
749            extension
750                .fields
751                .iter()
752                .map(|field| (&field.name, field.to_component(origin.clone()))),
753            |prev_key, dup_value| {
754                errors.push(
755                    dup_value.location(),
756                    BuildError::InterfaceFieldNameCollision {
757                        name_at_previous_location: prev_key.clone(),
758                        type_name: extension.name.clone(),
759                    },
760                )
761            },
762        );
763    }
764}
765
766impl UnionType {
767    fn from_ast(
768        errors: &mut DiagnosticList,
769        definition: &Node<ast::UnionTypeDefinition>,
770        extensions: Vec<ast::Definition>,
771    ) -> Node<Self> {
772        let mut ty = Self {
773            description: definition.description.clone(),
774            name: definition.name.clone(),
775            directives: definition
776                .directives
777                .iter()
778                .map(|d| d.to_component(ComponentOrigin::Definition))
779                .collect(),
780            members: collect_sticky_set(
781                definition
782                    .members
783                    .iter()
784                    .map(|name| name.to_component(ComponentOrigin::Definition)),
785                |prev, dup| {
786                    errors.push(
787                        dup.location(),
788                        BuildError::UnionMemberNameCollision {
789                            name_at_previous_location: prev.name.clone(),
790                            type_name: definition.name.clone(),
791                        },
792                    )
793                },
794            ),
795        };
796        for def in &extensions {
797            if let ast::Definition::UnionTypeExtension(ext) = def {
798                ty.extend_ast(errors, ext)
799            }
800        }
801        definition.same_location(ty)
802    }
803
804    fn extend_ast(
805        &mut self,
806        errors: &mut DiagnosticList,
807        extension: &Node<ast::UnionTypeExtension>,
808    ) {
809        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
810        self.directives.extend(
811            extension
812                .directives
813                .iter()
814                .map(|d| d.to_component(origin.clone())),
815        );
816        extend_sticky_set(
817            &mut self.members,
818            extension
819                .members
820                .iter()
821                .map(|name| name.to_component(origin.clone())),
822            |prev, dup| {
823                errors.push(
824                    dup.location(),
825                    BuildError::UnionMemberNameCollision {
826                        name_at_previous_location: prev.name.clone(),
827                        type_name: extension.name.clone(),
828                    },
829                )
830            },
831        );
832    }
833}
834
835impl EnumType {
836    fn from_ast(
837        errors: &mut DiagnosticList,
838        definition: &Node<ast::EnumTypeDefinition>,
839        extensions: Vec<ast::Definition>,
840    ) -> Node<Self> {
841        let mut ty = Self {
842            description: definition.description.clone(),
843            name: definition.name.clone(),
844            directives: definition
845                .directives
846                .iter()
847                .map(|d| d.to_component(ComponentOrigin::Definition))
848                .collect(),
849            values: collect_sticky(
850                definition.values.iter().map(|value_def| {
851                    (
852                        &value_def.value,
853                        value_def.to_component(ComponentOrigin::Definition),
854                    )
855                }),
856                |prev_key, dup_value| {
857                    errors.push(
858                        dup_value.location(),
859                        BuildError::EnumValueNameCollision {
860                            name_at_previous_location: prev_key.clone(),
861                            type_name: definition.name.clone(),
862                        },
863                    )
864                },
865            ),
866        };
867        for def in &extensions {
868            if let ast::Definition::EnumTypeExtension(ext) = def {
869                ty.extend_ast(errors, ext)
870            }
871        }
872        definition.same_location(ty)
873    }
874
875    fn extend_ast(
876        &mut self,
877        errors: &mut DiagnosticList,
878        extension: &Node<ast::EnumTypeExtension>,
879    ) {
880        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
881        self.directives.extend(
882            extension
883                .directives
884                .iter()
885                .map(|d| d.to_component(origin.clone())),
886        );
887        extend_sticky(
888            &mut self.values,
889            extension
890                .values
891                .iter()
892                .map(|value_def| (&value_def.value, value_def.to_component(origin.clone()))),
893            |prev_key, dup_value| {
894                errors.push(
895                    dup_value.location(),
896                    BuildError::EnumValueNameCollision {
897                        name_at_previous_location: prev_key.clone(),
898                        type_name: extension.name.clone(),
899                    },
900                )
901            },
902        )
903    }
904}
905
906impl InputObjectType {
907    fn from_ast(
908        errors: &mut DiagnosticList,
909        definition: &Node<ast::InputObjectTypeDefinition>,
910        extensions: Vec<ast::Definition>,
911    ) -> Node<Self> {
912        let mut ty = Self {
913            description: definition.description.clone(),
914            name: definition.name.clone(),
915            directives: definition
916                .directives
917                .iter()
918                .map(|d| d.to_component(ComponentOrigin::Definition))
919                .collect(),
920            fields: collect_sticky(
921                definition
922                    .fields
923                    .iter()
924                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
925                |prev_key, dup_value| {
926                    errors.push(
927                        dup_value.location(),
928                        BuildError::InputFieldNameCollision {
929                            name_at_previous_location: prev_key.clone(),
930                            type_name: definition.name.clone(),
931                        },
932                    )
933                },
934            ),
935        };
936        for def in &extensions {
937            if let ast::Definition::InputObjectTypeExtension(ext) = def {
938                ty.extend_ast(errors, ext)
939            }
940        }
941        definition.same_location(ty)
942    }
943
944    fn extend_ast(
945        &mut self,
946        errors: &mut DiagnosticList,
947        extension: &Node<ast::InputObjectTypeExtension>,
948    ) {
949        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
950        self.directives.extend(
951            extension
952                .directives
953                .iter()
954                .map(|d| d.to_component(origin.clone())),
955        );
956        extend_sticky(
957            &mut self.fields,
958            extension
959                .fields
960                .iter()
961                .map(|field| (&field.name, field.to_component(origin.clone()))),
962            |prev_key, dup_value| {
963                errors.push(
964                    dup_value.location(),
965                    BuildError::InputFieldNameCollision {
966                        name_at_previous_location: prev_key.clone(),
967                        type_name: extension.name.clone(),
968                    },
969                )
970            },
971        )
972    }
973}
974
975/// Like `IndexMap::extend`, but does not replace a value if an equivalent key is already in the map.
976///
977/// On collision, calls `duplicate` with the previous key and the value not inserted
978fn extend_sticky<'a, V>(
979    map: &mut IndexMap<Name, V>,
980    iter: impl IntoIterator<Item = (&'a Name, V)>,
981    mut duplicate: impl FnMut(&Name, V),
982) {
983    for (key, value) in iter.into_iter() {
984        match map.get_key_value(key) {
985            None => {
986                map.insert(key.clone(), value);
987            }
988            Some((prev_key, _)) => duplicate(prev_key, value),
989        }
990    }
991}
992
993/// Like `IndexMap::from_iterator`, but does not replace a value if an equivalent key is already in the map.
994///
995/// On collision, calls `duplicate` with the previous key and the value not inserted
996fn collect_sticky<'a, V>(
997    iter: impl IntoIterator<Item = (&'a Name, V)>,
998    duplicate: impl FnMut(&Name, V),
999) -> IndexMap<Name, V> {
1000    let mut map = IndexMap::with_hasher(Default::default());
1001    extend_sticky(&mut map, iter, duplicate);
1002    map
1003}
1004
1005fn extend_sticky_set(
1006    set: &mut IndexSet<ComponentName>,
1007    iter: impl IntoIterator<Item = ComponentName>,
1008    mut duplicate: impl FnMut(&ComponentName, ComponentName),
1009) {
1010    for value in iter.into_iter() {
1011        match set.get(&value) {
1012            None => {
1013                set.insert(value);
1014            }
1015            Some(previous) => duplicate(previous, value),
1016        }
1017    }
1018}
1019fn collect_sticky_set(
1020    iter: impl IntoIterator<Item = ComponentName>,
1021    duplicate: impl FnMut(&ComponentName, ComponentName),
1022) -> IndexSet<ComponentName> {
1023    let mut set = IndexSet::with_hasher(Default::default());
1024    extend_sticky_set(&mut set, iter, duplicate);
1025    set
1026}