rules_overlapping_fields_can_be_merged.go

  1package graphql
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	"github.com/graphql-go/graphql/language/ast"
  8	"github.com/graphql-go/graphql/language/kinds"
  9	"github.com/graphql-go/graphql/language/printer"
 10	"github.com/graphql-go/graphql/language/visitor"
 11)
 12
 13func fieldsConflictMessage(responseName string, reason conflictReason) string {
 14	return fmt.Sprintf(`Fields "%v" conflict because %v. `+
 15		`Use different aliases on the fields to fetch both if this was intentional.`,
 16		responseName,
 17		fieldsConflictReasonMessage(reason),
 18	)
 19}
 20
 21func fieldsConflictReasonMessage(message interface{}) string {
 22	switch reason := message.(type) {
 23	case string:
 24		return reason
 25	case conflictReason:
 26		return fieldsConflictReasonMessage(reason.Message)
 27	case []conflictReason:
 28		messages := []string{}
 29		for _, r := range reason {
 30			messages = append(messages, fmt.Sprintf(
 31				`subfields "%v" conflict because %v`,
 32				r.Name,
 33				fieldsConflictReasonMessage(r.Message),
 34			))
 35		}
 36		return strings.Join(messages, " and ")
 37	}
 38	return ""
 39}
 40
 41// OverlappingFieldsCanBeMergedRule Overlapping fields can be merged
 42//
 43// A selection set is only valid if all fields (including spreading any
 44// fragments) either correspond to distinct response names or can be merged
 45// without ambiguity.
 46func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRuleInstance {
 47
 48	// A memoization for when two fragments are compared "between" each other for
 49	// conflicts. Two fragments may be compared many times, so memoizing this can
 50	// dramatically improve the performance of this validator.
 51	comparedSet := newPairSet()
 52
 53	// A cache for the "field map" and list of fragment names found in any given
 54	// selection set. Selection sets may be asked for this information multiple
 55	// times, so this improves the performance of this validator.
 56	cacheMap := map[*ast.SelectionSet]*fieldsAndFragmentNames{}
 57
 58	visitorOpts := &visitor.VisitorOptions{
 59		KindFuncMap: map[string]visitor.NamedVisitFuncs{
 60			kinds.SelectionSet: {
 61				Kind: func(p visitor.VisitFuncParams) (string, interface{}) {
 62					if selectionSet, ok := p.Node.(*ast.SelectionSet); ok && selectionSet != nil {
 63						parentType, _ := context.ParentType().(Named)
 64
 65						rule := &overlappingFieldsCanBeMergedRule{
 66							context:     context,
 67							comparedSet: comparedSet,
 68							cacheMap:    cacheMap,
 69						}
 70						conflicts := rule.findConflictsWithinSelectionSet(parentType, selectionSet)
 71						if len(conflicts) > 0 {
 72							for _, c := range conflicts {
 73								responseName := c.Reason.Name
 74								reason := c.Reason
 75								reportError(
 76									context,
 77									fieldsConflictMessage(responseName, reason),
 78									append(c.FieldsLeft, c.FieldsRight...),
 79								)
 80							}
 81							return visitor.ActionNoChange, nil
 82						}
 83					}
 84					return visitor.ActionNoChange, nil
 85				},
 86			},
 87		},
 88	}
 89	return &ValidationRuleInstance{
 90		VisitorOpts: visitorOpts,
 91	}
 92}
 93
 94/**
 95 * Algorithm:
 96 *
 97 * Conflicts occur when two fields exist in a query which will produce the same
 98 * response name, but represent differing values, thus creating a conflict.
 99 * The algorithm below finds all conflicts via making a series of comparisons
100 * between fields. In order to compare as few fields as possible, this makes
101 * a series of comparisons "within" sets of fields and "between" sets of fields.
102 *
103 * Given any selection set, a collection produces both a set of fields by
104 * also including all inline fragments, as well as a list of fragments
105 * referenced by fragment spreads.
106 *
107 * A) Each selection set represented in the document first compares "within" its
108 * collected set of fields, finding any conflicts between every pair of
109 * overlapping fields.
110 * Note: This is the *only time* that a the fields "within" a set are compared
111 * to each other. After this only fields "between" sets are compared.
112 *
113 * B) Also, if any fragment is referenced in a selection set, then a
114 * comparison is made "between" the original set of fields and the
115 * referenced fragment.
116 *
117 * C) Also, if multiple fragments are referenced, then comparisons
118 * are made "between" each referenced fragment.
119 *
120 * D) When comparing "between" a set of fields and a referenced fragment, first
121 * a comparison is made between each field in the original set of fields and
122 * each field in the the referenced set of fields.
123 *
124 * E) Also, if any fragment is referenced in the referenced selection set,
125 * then a comparison is made "between" the original set of fields and the
126 * referenced fragment (recursively referring to step D).
127 *
128 * F) When comparing "between" two fragments, first a comparison is made between
129 * each field in the first referenced set of fields and each field in the the
130 * second referenced set of fields.
131 *
132 * G) Also, any fragments referenced by the first must be compared to the
133 * second, and any fragments referenced by the second must be compared to the
134 * first (recursively referring to step F).
135 *
136 * H) When comparing two fields, if both have selection sets, then a comparison
137 * is made "between" both selection sets, first comparing the set of fields in
138 * the first selection set with the set of fields in the second.
139 *
140 * I) Also, if any fragment is referenced in either selection set, then a
141 * comparison is made "between" the other set of fields and the
142 * referenced fragment.
143 *
144 * J) Also, if two fragments are referenced in both selection sets, then a
145 * comparison is made "between" the two fragments.
146 *
147 */
148
149type overlappingFieldsCanBeMergedRule struct {
150	context *ValidationContext
151
152	// A memoization for when two fragments are compared "between" each other for
153	// conflicts. Two fragments may be compared many times, so memoizing this can
154	// dramatically improve the performance of this validator.
155	comparedSet *pairSet
156
157	// A cache for the "field map" and list of fragment names found in any given
158	// selection set. Selection sets may be asked for this information multiple
159	// times, so this improves the performance of this validator.
160	cacheMap map[*ast.SelectionSet]*fieldsAndFragmentNames
161}
162
163// Find all conflicts found "within" a selection set, including those found
164// via spreading in fragments. Called when visiting each SelectionSet in the
165// GraphQL Document.
166func (rule *overlappingFieldsCanBeMergedRule) findConflictsWithinSelectionSet(parentType Named, selectionSet *ast.SelectionSet) []conflict {
167	conflicts := []conflict{}
168
169	fieldsInfo := rule.getFieldsAndFragmentNames(parentType, selectionSet)
170
171	// (A) Find find all conflicts "within" the fields of this selection set.
172	// Note: this is the *only place* `collectConflictsWithin` is called.
173	conflicts = rule.collectConflictsWithin(conflicts, fieldsInfo)
174
175	// (B) Then collect conflicts between these fields and those represented by
176	// each spread fragment name found.
177	for i := 0; i < len(fieldsInfo.fragmentNames); i++ {
178
179		conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, false, fieldsInfo, fieldsInfo.fragmentNames[i])
180
181		// (C) Then compare this fragment with all other fragments found in this
182		// selection set to collect conflicts between fragments spread together.
183		// This compares each item in the list of fragment names to every other item
184		// in that same list (except for itself).
185		for k := i + 1; k < len(fieldsInfo.fragmentNames); k++ {
186			conflicts = rule.collectConflictsBetweenFragments(conflicts, false, fieldsInfo.fragmentNames[i], fieldsInfo.fragmentNames[k])
187		}
188	}
189	return conflicts
190}
191
192// Collect all conflicts found between a set of fields and a fragment reference
193// including via spreading in any nested fragments.
194func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetweenFieldsAndFragment(conflicts []conflict, areMutuallyExclusive bool, fieldsInfo *fieldsAndFragmentNames, fragmentName string) []conflict {
195	fragment := rule.context.Fragment(fragmentName)
196	if fragment == nil {
197		return conflicts
198	}
199
200	fieldsInfo2 := rule.getReferencedFieldsAndFragmentNames(fragment)
201
202	// (D) First collect any conflicts between the provided collection of fields
203	// and the collection of fields represented by the given fragment.
204	conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo, fieldsInfo2)
205
206	// (E) Then collect any conflicts between the provided collection of fields
207	// and any fragment names found in the given fragment.
208	for _, fragmentName2 := range fieldsInfo2.fragmentNames {
209		conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo2, fragmentName2)
210	}
211
212	return conflicts
213
214}
215
216// Collect all conflicts found between two fragments, including via spreading in
217// any nested fragments.
218func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetweenFragments(conflicts []conflict, areMutuallyExclusive bool, fragmentName1 string, fragmentName2 string) []conflict {
219	fragment1 := rule.context.Fragment(fragmentName1)
220	fragment2 := rule.context.Fragment(fragmentName2)
221
222	if fragment1 == nil || fragment2 == nil {
223		return conflicts
224	}
225
226	// No need to compare a fragment to itself.
227	if fragment1 == fragment2 {
228		return conflicts
229	}
230
231	// Memoize so two fragments are not compared for conflicts more than once.
232	if rule.comparedSet.Has(fragmentName1, fragmentName2, areMutuallyExclusive) {
233		return conflicts
234	}
235	rule.comparedSet.Add(fragmentName1, fragmentName2, areMutuallyExclusive)
236
237	fieldsInfo1 := rule.getReferencedFieldsAndFragmentNames(fragment1)
238	fieldsInfo2 := rule.getReferencedFieldsAndFragmentNames(fragment2)
239
240	// (F) First, collect all conflicts between these two collections of fields
241	// (not including any nested fragments).
242	conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo1, fieldsInfo2)
243
244	// (G) Then collect conflicts between the first fragment and any nested
245	// fragments spread in the second fragment.
246	for _, innerFragmentName2 := range fieldsInfo2.fragmentNames {
247		conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, fragmentName1, innerFragmentName2)
248	}
249
250	// (G) Then collect conflicts between the second fragment and any nested
251	// fragments spread in the first fragment.
252	for _, innerFragmentName1 := range fieldsInfo1.fragmentNames {
253		conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, innerFragmentName1, fragmentName2)
254	}
255
256	return conflicts
257}
258
259// Find all conflicts found between two selection sets, including those found
260// via spreading in fragments. Called when determining if conflicts exist
261// between the sub-fields of two overlapping fields.
262func (rule *overlappingFieldsCanBeMergedRule) findConflictsBetweenSubSelectionSets(areMutuallyExclusive bool, parentType1 Named, selectionSet1 *ast.SelectionSet, parentType2 Named, selectionSet2 *ast.SelectionSet) []conflict {
263	conflicts := []conflict{}
264
265	fieldsInfo1 := rule.getFieldsAndFragmentNames(parentType1, selectionSet1)
266	fieldsInfo2 := rule.getFieldsAndFragmentNames(parentType2, selectionSet2)
267
268	// (H) First, collect all conflicts between these two collections of field.
269	conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo1, fieldsInfo2)
270
271	// (I) Then collect conflicts between the first collection of fields and
272	// those referenced by each fragment name associated with the second.
273	for _, fragmentName2 := range fieldsInfo2.fragmentNames {
274		conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo1, fragmentName2)
275	}
276
277	// (I) Then collect conflicts between the second collection of fields and
278	// those referenced by each fragment name associated with the first.
279	for _, fragmentName1 := range fieldsInfo1.fragmentNames {
280		conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo2, fragmentName1)
281	}
282
283	// (J) Also collect conflicts between any fragment names by the first and
284	// fragment names by the second. This compares each item in the first set of
285	// names to each item in the second set of names.
286	for _, fragmentName1 := range fieldsInfo1.fragmentNames {
287		for _, fragmentName2 := range fieldsInfo2.fragmentNames {
288			conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, fragmentName1, fragmentName2)
289		}
290	}
291	return conflicts
292}
293
294// Collect all Conflicts "within" one collection of fields.
295func (rule *overlappingFieldsCanBeMergedRule) collectConflictsWithin(conflicts []conflict, fieldsInfo *fieldsAndFragmentNames) []conflict {
296	// A field map is a keyed collection, where each key represents a response
297	// name and the value at that key is a list of all fields which provide that
298	// response name. For every response name, if there are multiple fields, they
299	// must be compared to find a potential conflict.
300	for _, responseName := range fieldsInfo.fieldsOrder {
301		fields, ok := fieldsInfo.fieldMap[responseName]
302		if !ok {
303			continue
304		}
305		// This compares every field in the list to every other field in this list
306		// (except to itself). If the list only has one item, nothing needs to
307		// be compared.
308		if len(fields) <= 1 {
309			continue
310		}
311		for i := 0; i < len(fields); i++ {
312			for k := i + 1; k < len(fields); k++ {
313				// within one collection is never mutually exclusive
314				isMutuallyExclusive := false
315				conflict := rule.findConflict(isMutuallyExclusive, responseName, fields[i], fields[k])
316				if conflict != nil {
317					conflicts = append(conflicts, *conflict)
318				}
319			}
320		}
321	}
322	return conflicts
323}
324
325// Collect all Conflicts between two collections of fields. This is similar to,
326// but different from the `collectConflictsWithin` function above. This check
327// assumes that `collectConflictsWithin` has already been called on each
328// provided collection of fields. This is true because this validator traverses
329// each individual selection set.
330func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetween(conflicts []conflict, parentFieldsAreMutuallyExclusive bool,
331	fieldsInfo1 *fieldsAndFragmentNames,
332	fieldsInfo2 *fieldsAndFragmentNames) []conflict {
333	// A field map is a keyed collection, where each key represents a response
334	// name and the value at that key is a list of all fields which provide that
335	// response name. For any response name which appears in both provided field
336	// maps, each field from the first field map must be compared to every field
337	// in the second field map to find potential conflicts.
338	for _, responseName := range fieldsInfo1.fieldsOrder {
339		fields1, ok1 := fieldsInfo1.fieldMap[responseName]
340		fields2, ok2 := fieldsInfo2.fieldMap[responseName]
341		if !ok1 || !ok2 {
342			continue
343		}
344		for i := 0; i < len(fields1); i++ {
345			for k := 0; k < len(fields2); k++ {
346				conflict := rule.findConflict(parentFieldsAreMutuallyExclusive, responseName, fields1[i], fields2[k])
347				if conflict != nil {
348					conflicts = append(conflicts, *conflict)
349				}
350			}
351		}
352	}
353	return conflicts
354}
355
356// findConflict Determines if there is a conflict between two particular fields.
357func (rule *overlappingFieldsCanBeMergedRule) findConflict(parentFieldsAreMutuallyExclusive bool, responseName string, field *fieldDefPair, field2 *fieldDefPair) *conflict {
358
359	parentType1 := field.ParentType
360	ast1 := field.Field
361	def1 := field.FieldDef
362
363	parentType2 := field2.ParentType
364	ast2 := field2.Field
365	def2 := field2.FieldDef
366
367	// If it is known that two fields could not possibly apply at the same
368	// time, due to the parent types, then it is safe to permit them to diverge
369	// in aliased field or arguments used as they will not present any ambiguity
370	// by differing.
371	// It is known that two parent types could never overlap if they are
372	// different Object types. Interface or Union types might overlap - if not
373	// in the current state of the schema, then perhaps in some future version,
374	// thus may not safely diverge.
375	_, isParentType1Object := parentType1.(*Object)
376	_, isParentType2Object := parentType2.(*Object)
377	areMutuallyExclusive := parentFieldsAreMutuallyExclusive || parentType1 != parentType2 && isParentType1Object && isParentType2Object
378
379	// The return type for each field.
380	var type1 Type
381	var type2 Type
382	if def1 != nil {
383		type1 = def1.Type
384	}
385	if def2 != nil {
386		type2 = def2.Type
387	}
388
389	if !areMutuallyExclusive {
390		// Two aliases must refer to the same field.
391		name1 := ""
392		name2 := ""
393
394		if ast1.Name != nil {
395			name1 = ast1.Name.Value
396		}
397		if ast2.Name != nil {
398			name2 = ast2.Name.Value
399		}
400		if name1 != name2 {
401			return &conflict{
402				Reason: conflictReason{
403					Name:    responseName,
404					Message: fmt.Sprintf(`%v and %v are different fields`, name1, name2),
405				},
406				FieldsLeft:  []ast.Node{ast1},
407				FieldsRight: []ast.Node{ast2},
408			}
409		}
410
411		// Two field calls must have the same arguments.
412		if !sameArguments(ast1.Arguments, ast2.Arguments) {
413			return &conflict{
414				Reason: conflictReason{
415					Name:    responseName,
416					Message: `they have differing arguments`,
417				},
418				FieldsLeft:  []ast.Node{ast1},
419				FieldsRight: []ast.Node{ast2},
420			}
421		}
422	}
423
424	if type1 != nil && type2 != nil && doTypesConflict(type1, type2) {
425		return &conflict{
426			Reason: conflictReason{
427				Name:    responseName,
428				Message: fmt.Sprintf(`they return conflicting types %v and %v`, type1, type2),
429			},
430			FieldsLeft:  []ast.Node{ast1},
431			FieldsRight: []ast.Node{ast2},
432		}
433	}
434
435	// Collect and compare sub-fields. Use the same "visited fragment names" list
436	// for both collections so fields in a fragment reference are never
437	// compared to themselves.
438	selectionSet1 := ast1.SelectionSet
439	selectionSet2 := ast2.SelectionSet
440	if selectionSet1 != nil && selectionSet2 != nil {
441		conflicts := rule.findConflictsBetweenSubSelectionSets(areMutuallyExclusive, GetNamed(type1), selectionSet1, GetNamed(type2), selectionSet2)
442		return subfieldConflicts(conflicts, responseName, ast1, ast2)
443	}
444	return nil
445}
446
447// Given a selection set, return the collection of fields (a mapping of response
448// name to field ASTs and definitions) as well as a list of fragment names
449// referenced via fragment spreads.
450func (rule *overlappingFieldsCanBeMergedRule) getFieldsAndFragmentNames(parentType Named, selectionSet *ast.SelectionSet) *fieldsAndFragmentNames {
451	if cached, ok := rule.cacheMap[selectionSet]; ok && cached != nil {
452		return cached
453	}
454
455	astAndDefs := astAndDefCollection{}
456	fieldsOrder := []string{}
457	fragmentNames := []string{}
458	fragmentNamesMap := map[string]bool{}
459
460	var collectFieldsAndFragmentNames func(parentType Named, selectionSet *ast.SelectionSet)
461	collectFieldsAndFragmentNames = func(parentType Named, selectionSet *ast.SelectionSet) {
462		for _, selection := range selectionSet.Selections {
463			switch selection := selection.(type) {
464			case *ast.Field:
465				fieldName := ""
466				if selection.Name != nil {
467					fieldName = selection.Name.Value
468				}
469				var fieldDef *FieldDefinition
470				if parentType, ok := parentType.(*Object); ok && parentType != nil {
471					fieldDef, _ = parentType.Fields()[fieldName]
472				}
473				if parentType, ok := parentType.(*Interface); ok && parentType != nil {
474					fieldDef, _ = parentType.Fields()[fieldName]
475				}
476
477				responseName := fieldName
478				if selection.Alias != nil {
479					responseName = selection.Alias.Value
480				}
481
482				fieldDefPairs, ok := astAndDefs[responseName]
483				if !ok || fieldDefPairs == nil {
484					fieldDefPairs = []*fieldDefPair{}
485					fieldsOrder = append(fieldsOrder, responseName)
486				}
487
488				fieldDefPairs = append(fieldDefPairs, &fieldDefPair{
489					ParentType: parentType,
490					Field:      selection,
491					FieldDef:   fieldDef,
492				})
493				astAndDefs[responseName] = fieldDefPairs
494			case *ast.FragmentSpread:
495				fieldName := ""
496				if selection.Name != nil {
497					fieldName = selection.Name.Value
498				}
499				if val, ok := fragmentNamesMap[fieldName]; !ok || !val {
500					fragmentNamesMap[fieldName] = true
501					fragmentNames = append(fragmentNames, fieldName)
502				}
503			case *ast.InlineFragment:
504				typeCondition := selection.TypeCondition
505				inlineFragmentType := parentType
506				if typeCondition != nil {
507					ttype, err := typeFromAST(*(rule.context.Schema()), typeCondition)
508					if err == nil {
509						inlineFragmentType, _ = ttype.(Named)
510					}
511				}
512				collectFieldsAndFragmentNames(inlineFragmentType, selection.SelectionSet)
513			}
514		}
515	}
516	collectFieldsAndFragmentNames(parentType, selectionSet)
517
518	cached := &fieldsAndFragmentNames{
519		fieldMap:      astAndDefs,
520		fieldsOrder:   fieldsOrder,
521		fragmentNames: fragmentNames,
522	}
523
524	rule.cacheMap[selectionSet] = cached
525	return cached
526}
527
528func (rule *overlappingFieldsCanBeMergedRule) getReferencedFieldsAndFragmentNames(fragment *ast.FragmentDefinition) *fieldsAndFragmentNames {
529	// Short-circuit building a type from the AST if possible.
530	if cached, ok := rule.cacheMap[fragment.SelectionSet]; ok && cached != nil {
531		return cached
532	}
533	fragmentType, err := typeFromAST(*(rule.context.Schema()), fragment.TypeCondition)
534	if err != nil {
535		return nil
536	}
537	return rule.getFieldsAndFragmentNames(fragmentType, fragment.SelectionSet)
538}
539
540type conflictReason struct {
541	Name    string
542	Message interface{} // conflictReason || []conflictReason
543}
544type conflict struct {
545	Reason      conflictReason
546	FieldsLeft  []ast.Node
547	FieldsRight []ast.Node
548}
549
550// a.k.a AstAndDef
551type fieldDefPair struct {
552	ParentType Named
553	Field      *ast.Field
554	FieldDef   *FieldDefinition
555}
556type astAndDefCollection map[string][]*fieldDefPair
557
558// cache struct for fields, its order and fragments names
559type fieldsAndFragmentNames struct {
560	fieldMap      astAndDefCollection
561	fieldsOrder   []string // stores the order of field names in fieldMap
562	fragmentNames []string
563}
564
565// pairSet A way to keep track of pairs of things when the ordering of the pair does
566// not matter. We do this by maintaining a sort of double adjacency sets.
567type pairSet struct {
568	data map[string]map[string]bool
569}
570
571func newPairSet() *pairSet {
572	return &pairSet{
573		data: map[string]map[string]bool{},
574	}
575}
576func (pair *pairSet) Has(a string, b string, areMutuallyExclusive bool) bool {
577	first, ok := pair.data[a]
578	if !ok || first == nil {
579		return false
580	}
581	res, ok := first[b]
582	if !ok {
583		return false
584	}
585	// areMutuallyExclusive being false is a superset of being true,
586	// hence if we want to know if this PairSet "has" these two with no
587	// exclusivity, we have to ensure it was added as such.
588	if !areMutuallyExclusive {
589		return res == false
590	}
591	return true
592}
593func (pair *pairSet) Add(a string, b string, areMutuallyExclusive bool) {
594	pair.data = pairSetAdd(pair.data, a, b, areMutuallyExclusive)
595	pair.data = pairSetAdd(pair.data, b, a, areMutuallyExclusive)
596}
597func pairSetAdd(data map[string]map[string]bool, a, b string, areMutuallyExclusive bool) map[string]map[string]bool {
598	set, ok := data[a]
599	if !ok || set == nil {
600		set = map[string]bool{}
601	}
602	set[b] = areMutuallyExclusive
603	data[a] = set
604	return data
605}
606
607func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool {
608	if len(args1) != len(args2) {
609		return false
610	}
611
612	for _, arg1 := range args1 {
613		arg1Name := ""
614		if arg1.Name != nil {
615			arg1Name = arg1.Name.Value
616		}
617
618		var foundArgs2 *ast.Argument
619		for _, arg2 := range args2 {
620			arg2Name := ""
621			if arg2.Name != nil {
622				arg2Name = arg2.Name.Value
623			}
624			if arg1Name == arg2Name {
625				foundArgs2 = arg2
626			}
627			break
628		}
629		if foundArgs2 == nil {
630			return false
631		}
632		if sameValue(arg1.Value, foundArgs2.Value) == false {
633			return false
634		}
635	}
636
637	return true
638}
639
640func sameValue(value1 ast.Value, value2 ast.Value) bool {
641	if value1 == nil && value2 == nil {
642		return true
643	}
644	val1 := printer.Print(value1)
645	val2 := printer.Print(value2)
646
647	return val1 == val2
648}
649
650// Two types conflict if both types could not apply to a value simultaneously.
651// Composite types are ignored as their individual field types will be compared
652// later recursively. However List and Non-Null types must match.
653func doTypesConflict(type1 Output, type2 Output) bool {
654	if type1, ok := type1.(*List); ok {
655		if type2, ok := type2.(*List); ok {
656			return doTypesConflict(type1.OfType, type2.OfType)
657		}
658		return true
659	}
660	if type2, ok := type2.(*List); ok {
661		if type1, ok := type1.(*List); ok {
662			return doTypesConflict(type1.OfType, type2.OfType)
663		}
664		return true
665	}
666	if type1, ok := type1.(*NonNull); ok {
667		if type2, ok := type2.(*NonNull); ok {
668			return doTypesConflict(type1.OfType, type2.OfType)
669		}
670		return true
671	}
672	if type2, ok := type2.(*NonNull); ok {
673		if type1, ok := type1.(*NonNull); ok {
674			return doTypesConflict(type1.OfType, type2.OfType)
675		}
676		return true
677	}
678	if IsLeafType(type1) || IsLeafType(type2) {
679		return type1 != type2
680	}
681	return false
682}
683
684// subfieldConflicts Given a series of Conflicts which occurred between two sub-fields, generate a single Conflict.
685func subfieldConflicts(conflicts []conflict, responseName string, ast1 *ast.Field, ast2 *ast.Field) *conflict {
686	if len(conflicts) > 0 {
687		conflictReasons := []conflictReason{}
688		conflictFieldsLeft := []ast.Node{ast1}
689		conflictFieldsRight := []ast.Node{ast2}
690		for _, c := range conflicts {
691			conflictReasons = append(conflictReasons, c.Reason)
692			conflictFieldsLeft = append(conflictFieldsLeft, c.FieldsLeft...)
693			conflictFieldsRight = append(conflictFieldsRight, c.FieldsRight...)
694		}
695
696		return &conflict{
697			Reason: conflictReason{
698				Name:    responseName,
699				Message: conflictReasons,
700			},
701			FieldsLeft:  conflictFieldsLeft,
702			FieldsRight: conflictFieldsRight,
703		}
704	}
705	return nil
706}