aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go')
-rw-r--r--vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go706
1 files changed, 706 insertions, 0 deletions
diff --git a/vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go b/vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go
new file mode 100644
index 00000000..836fb43f
--- /dev/null
+++ b/vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go
@@ -0,0 +1,706 @@
+package graphql
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/graphql-go/graphql/language/ast"
+ "github.com/graphql-go/graphql/language/kinds"
+ "github.com/graphql-go/graphql/language/printer"
+ "github.com/graphql-go/graphql/language/visitor"
+)
+
+func fieldsConflictMessage(responseName string, reason conflictReason) string {
+ return fmt.Sprintf(`Fields "%v" conflict because %v. `+
+ `Use different aliases on the fields to fetch both if this was intentional.`,
+ responseName,
+ fieldsConflictReasonMessage(reason),
+ )
+}
+
+func fieldsConflictReasonMessage(message interface{}) string {
+ switch reason := message.(type) {
+ case string:
+ return reason
+ case conflictReason:
+ return fieldsConflictReasonMessage(reason.Message)
+ case []conflictReason:
+ messages := []string{}
+ for _, r := range reason {
+ messages = append(messages, fmt.Sprintf(
+ `subfields "%v" conflict because %v`,
+ r.Name,
+ fieldsConflictReasonMessage(r.Message),
+ ))
+ }
+ return strings.Join(messages, " and ")
+ }
+ return ""
+}
+
+// OverlappingFieldsCanBeMergedRule Overlapping fields can be merged
+//
+// A selection set is only valid if all fields (including spreading any
+// fragments) either correspond to distinct response names or can be merged
+// without ambiguity.
+func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRuleInstance {
+
+ // A memoization for when two fragments are compared "between" each other for
+ // conflicts. Two fragments may be compared many times, so memoizing this can
+ // dramatically improve the performance of this validator.
+ comparedSet := newPairSet()
+
+ // A cache for the "field map" and list of fragment names found in any given
+ // selection set. Selection sets may be asked for this information multiple
+ // times, so this improves the performance of this validator.
+ cacheMap := map[*ast.SelectionSet]*fieldsAndFragmentNames{}
+
+ visitorOpts := &visitor.VisitorOptions{
+ KindFuncMap: map[string]visitor.NamedVisitFuncs{
+ kinds.SelectionSet: {
+ Kind: func(p visitor.VisitFuncParams) (string, interface{}) {
+ if selectionSet, ok := p.Node.(*ast.SelectionSet); ok && selectionSet != nil {
+ parentType, _ := context.ParentType().(Named)
+
+ rule := &overlappingFieldsCanBeMergedRule{
+ context: context,
+ comparedSet: comparedSet,
+ cacheMap: cacheMap,
+ }
+ conflicts := rule.findConflictsWithinSelectionSet(parentType, selectionSet)
+ if len(conflicts) > 0 {
+ for _, c := range conflicts {
+ responseName := c.Reason.Name
+ reason := c.Reason
+ reportError(
+ context,
+ fieldsConflictMessage(responseName, reason),
+ append(c.FieldsLeft, c.FieldsRight...),
+ )
+ }
+ return visitor.ActionNoChange, nil
+ }
+ }
+ return visitor.ActionNoChange, nil
+ },
+ },
+ },
+ }
+ return &ValidationRuleInstance{
+ VisitorOpts: visitorOpts,
+ }
+}
+
+/**
+ * Algorithm:
+ *
+ * Conflicts occur when two fields exist in a query which will produce the same
+ * response name, but represent differing values, thus creating a conflict.
+ * The algorithm below finds all conflicts via making a series of comparisons
+ * between fields. In order to compare as few fields as possible, this makes
+ * a series of comparisons "within" sets of fields and "between" sets of fields.
+ *
+ * Given any selection set, a collection produces both a set of fields by
+ * also including all inline fragments, as well as a list of fragments
+ * referenced by fragment spreads.
+ *
+ * A) Each selection set represented in the document first compares "within" its
+ * collected set of fields, finding any conflicts between every pair of
+ * overlapping fields.
+ * Note: This is the *only time* that a the fields "within" a set are compared
+ * to each other. After this only fields "between" sets are compared.
+ *
+ * B) Also, if any fragment is referenced in a selection set, then a
+ * comparison is made "between" the original set of fields and the
+ * referenced fragment.
+ *
+ * C) Also, if multiple fragments are referenced, then comparisons
+ * are made "between" each referenced fragment.
+ *
+ * D) When comparing "between" a set of fields and a referenced fragment, first
+ * a comparison is made between each field in the original set of fields and
+ * each field in the the referenced set of fields.
+ *
+ * E) Also, if any fragment is referenced in the referenced selection set,
+ * then a comparison is made "between" the original set of fields and the
+ * referenced fragment (recursively referring to step D).
+ *
+ * F) When comparing "between" two fragments, first a comparison is made between
+ * each field in the first referenced set of fields and each field in the the
+ * second referenced set of fields.
+ *
+ * G) Also, any fragments referenced by the first must be compared to the
+ * second, and any fragments referenced by the second must be compared to the
+ * first (recursively referring to step F).
+ *
+ * H) When comparing two fields, if both have selection sets, then a comparison
+ * is made "between" both selection sets, first comparing the set of fields in
+ * the first selection set with the set of fields in the second.
+ *
+ * I) Also, if any fragment is referenced in either selection set, then a
+ * comparison is made "between" the other set of fields and the
+ * referenced fragment.
+ *
+ * J) Also, if two fragments are referenced in both selection sets, then a
+ * comparison is made "between" the two fragments.
+ *
+ */
+
+type overlappingFieldsCanBeMergedRule struct {
+ context *ValidationContext
+
+ // A memoization for when two fragments are compared "between" each other for
+ // conflicts. Two fragments may be compared many times, so memoizing this can
+ // dramatically improve the performance of this validator.
+ comparedSet *pairSet
+
+ // A cache for the "field map" and list of fragment names found in any given
+ // selection set. Selection sets may be asked for this information multiple
+ // times, so this improves the performance of this validator.
+ cacheMap map[*ast.SelectionSet]*fieldsAndFragmentNames
+}
+
+// Find all conflicts found "within" a selection set, including those found
+// via spreading in fragments. Called when visiting each SelectionSet in the
+// GraphQL Document.
+func (rule *overlappingFieldsCanBeMergedRule) findConflictsWithinSelectionSet(parentType Named, selectionSet *ast.SelectionSet) []conflict {
+ conflicts := []conflict{}
+
+ fieldsInfo := rule.getFieldsAndFragmentNames(parentType, selectionSet)
+
+ // (A) Find find all conflicts "within" the fields of this selection set.
+ // Note: this is the *only place* `collectConflictsWithin` is called.
+ conflicts = rule.collectConflictsWithin(conflicts, fieldsInfo)
+
+ // (B) Then collect conflicts between these fields and those represented by
+ // each spread fragment name found.
+ for i := 0; i < len(fieldsInfo.fragmentNames); i++ {
+
+ conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, false, fieldsInfo, fieldsInfo.fragmentNames[i])
+
+ // (C) Then compare this fragment with all other fragments found in this
+ // selection set to collect conflicts between fragments spread together.
+ // This compares each item in the list of fragment names to every other item
+ // in that same list (except for itself).
+ for k := i + 1; k < len(fieldsInfo.fragmentNames); k++ {
+ conflicts = rule.collectConflictsBetweenFragments(conflicts, false, fieldsInfo.fragmentNames[i], fieldsInfo.fragmentNames[k])
+ }
+ }
+ return conflicts
+}
+
+// Collect all conflicts found between a set of fields and a fragment reference
+// including via spreading in any nested fragments.
+func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetweenFieldsAndFragment(conflicts []conflict, areMutuallyExclusive bool, fieldsInfo *fieldsAndFragmentNames, fragmentName string) []conflict {
+ fragment := rule.context.Fragment(fragmentName)
+ if fragment == nil {
+ return conflicts
+ }
+
+ fieldsInfo2 := rule.getReferencedFieldsAndFragmentNames(fragment)
+
+ // (D) First collect any conflicts between the provided collection of fields
+ // and the collection of fields represented by the given fragment.
+ conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo, fieldsInfo2)
+
+ // (E) Then collect any conflicts between the provided collection of fields
+ // and any fragment names found in the given fragment.
+ for _, fragmentName2 := range fieldsInfo2.fragmentNames {
+ conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo2, fragmentName2)
+ }
+
+ return conflicts
+
+}
+
+// Collect all conflicts found between two fragments, including via spreading in
+// any nested fragments.
+func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetweenFragments(conflicts []conflict, areMutuallyExclusive bool, fragmentName1 string, fragmentName2 string) []conflict {
+ fragment1 := rule.context.Fragment(fragmentName1)
+ fragment2 := rule.context.Fragment(fragmentName2)
+
+ if fragment1 == nil || fragment2 == nil {
+ return conflicts
+ }
+
+ // No need to compare a fragment to itself.
+ if fragment1 == fragment2 {
+ return conflicts
+ }
+
+ // Memoize so two fragments are not compared for conflicts more than once.
+ if rule.comparedSet.Has(fragmentName1, fragmentName2, areMutuallyExclusive) {
+ return conflicts
+ }
+ rule.comparedSet.Add(fragmentName1, fragmentName2, areMutuallyExclusive)
+
+ fieldsInfo1 := rule.getReferencedFieldsAndFragmentNames(fragment1)
+ fieldsInfo2 := rule.getReferencedFieldsAndFragmentNames(fragment2)
+
+ // (F) First, collect all conflicts between these two collections of fields
+ // (not including any nested fragments).
+ conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo1, fieldsInfo2)
+
+ // (G) Then collect conflicts between the first fragment and any nested
+ // fragments spread in the second fragment.
+ for _, innerFragmentName2 := range fieldsInfo2.fragmentNames {
+ conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, fragmentName1, innerFragmentName2)
+ }
+
+ // (G) Then collect conflicts between the second fragment and any nested
+ // fragments spread in the first fragment.
+ for _, innerFragmentName1 := range fieldsInfo1.fragmentNames {
+ conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, innerFragmentName1, fragmentName2)
+ }
+
+ return conflicts
+}
+
+// Find all conflicts found between two selection sets, including those found
+// via spreading in fragments. Called when determining if conflicts exist
+// between the sub-fields of two overlapping fields.
+func (rule *overlappingFieldsCanBeMergedRule) findConflictsBetweenSubSelectionSets(areMutuallyExclusive bool, parentType1 Named, selectionSet1 *ast.SelectionSet, parentType2 Named, selectionSet2 *ast.SelectionSet) []conflict {
+ conflicts := []conflict{}
+
+ fieldsInfo1 := rule.getFieldsAndFragmentNames(parentType1, selectionSet1)
+ fieldsInfo2 := rule.getFieldsAndFragmentNames(parentType2, selectionSet2)
+
+ // (H) First, collect all conflicts between these two collections of field.
+ conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo1, fieldsInfo2)
+
+ // (I) Then collect conflicts between the first collection of fields and
+ // those referenced by each fragment name associated with the second.
+ for _, fragmentName2 := range fieldsInfo2.fragmentNames {
+ conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo1, fragmentName2)
+ }
+
+ // (I) Then collect conflicts between the second collection of fields and
+ // those referenced by each fragment name associated with the first.
+ for _, fragmentName1 := range fieldsInfo1.fragmentNames {
+ conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo2, fragmentName1)
+ }
+
+ // (J) Also collect conflicts between any fragment names by the first and
+ // fragment names by the second. This compares each item in the first set of
+ // names to each item in the second set of names.
+ for _, fragmentName1 := range fieldsInfo1.fragmentNames {
+ for _, fragmentName2 := range fieldsInfo2.fragmentNames {
+ conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, fragmentName1, fragmentName2)
+ }
+ }
+ return conflicts
+}
+
+// Collect all Conflicts "within" one collection of fields.
+func (rule *overlappingFieldsCanBeMergedRule) collectConflictsWithin(conflicts []conflict, fieldsInfo *fieldsAndFragmentNames) []conflict {
+ // A field map is a keyed collection, where each key represents a response
+ // name and the value at that key is a list of all fields which provide that
+ // response name. For every response name, if there are multiple fields, they
+ // must be compared to find a potential conflict.
+ for _, responseName := range fieldsInfo.fieldsOrder {
+ fields, ok := fieldsInfo.fieldMap[responseName]
+ if !ok {
+ continue
+ }
+ // This compares every field in the list to every other field in this list
+ // (except to itself). If the list only has one item, nothing needs to
+ // be compared.
+ if len(fields) <= 1 {
+ continue
+ }
+ for i := 0; i < len(fields); i++ {
+ for k := i + 1; k < len(fields); k++ {
+ // within one collection is never mutually exclusive
+ isMutuallyExclusive := false
+ conflict := rule.findConflict(isMutuallyExclusive, responseName, fields[i], fields[k])
+ if conflict != nil {
+ conflicts = append(conflicts, *conflict)
+ }
+ }
+ }
+ }
+ return conflicts
+}
+
+// Collect all Conflicts between two collections of fields. This is similar to,
+// but different from the `collectConflictsWithin` function above. This check
+// assumes that `collectConflictsWithin` has already been called on each
+// provided collection of fields. This is true because this validator traverses
+// each individual selection set.
+func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetween(conflicts []conflict, parentFieldsAreMutuallyExclusive bool,
+ fieldsInfo1 *fieldsAndFragmentNames,
+ fieldsInfo2 *fieldsAndFragmentNames) []conflict {
+ // A field map is a keyed collection, where each key represents a response
+ // name and the value at that key is a list of all fields which provide that
+ // response name. For any response name which appears in both provided field
+ // maps, each field from the first field map must be compared to every field
+ // in the second field map to find potential conflicts.
+ for _, responseName := range fieldsInfo1.fieldsOrder {
+ fields1, ok1 := fieldsInfo1.fieldMap[responseName]
+ fields2, ok2 := fieldsInfo2.fieldMap[responseName]
+ if !ok1 || !ok2 {
+ continue
+ }
+ for i := 0; i < len(fields1); i++ {
+ for k := 0; k < len(fields2); k++ {
+ conflict := rule.findConflict(parentFieldsAreMutuallyExclusive, responseName, fields1[i], fields2[k])
+ if conflict != nil {
+ conflicts = append(conflicts, *conflict)
+ }
+ }
+ }
+ }
+ return conflicts
+}
+
+// findConflict Determines if there is a conflict between two particular fields.
+func (rule *overlappingFieldsCanBeMergedRule) findConflict(parentFieldsAreMutuallyExclusive bool, responseName string, field *fieldDefPair, field2 *fieldDefPair) *conflict {
+
+ parentType1 := field.ParentType
+ ast1 := field.Field
+ def1 := field.FieldDef
+
+ parentType2 := field2.ParentType
+ ast2 := field2.Field
+ def2 := field2.FieldDef
+
+ // If it is known that two fields could not possibly apply at the same
+ // time, due to the parent types, then it is safe to permit them to diverge
+ // in aliased field or arguments used as they will not present any ambiguity
+ // by differing.
+ // It is known that two parent types could never overlap if they are
+ // different Object types. Interface or Union types might overlap - if not
+ // in the current state of the schema, then perhaps in some future version,
+ // thus may not safely diverge.
+ _, isParentType1Object := parentType1.(*Object)
+ _, isParentType2Object := parentType2.(*Object)
+ areMutuallyExclusive := parentFieldsAreMutuallyExclusive || parentType1 != parentType2 && isParentType1Object && isParentType2Object
+
+ // The return type for each field.
+ var type1 Type
+ var type2 Type
+ if def1 != nil {
+ type1 = def1.Type
+ }
+ if def2 != nil {
+ type2 = def2.Type
+ }
+
+ if !areMutuallyExclusive {
+ // Two aliases must refer to the same field.
+ name1 := ""
+ name2 := ""
+
+ if ast1.Name != nil {
+ name1 = ast1.Name.Value
+ }
+ if ast2.Name != nil {
+ name2 = ast2.Name.Value
+ }
+ if name1 != name2 {
+ return &conflict{
+ Reason: conflictReason{
+ Name: responseName,
+ Message: fmt.Sprintf(`%v and %v are different fields`, name1, name2),
+ },
+ FieldsLeft: []ast.Node{ast1},
+ FieldsRight: []ast.Node{ast2},
+ }
+ }
+
+ // Two field calls must have the same arguments.
+ if !sameArguments(ast1.Arguments, ast2.Arguments) {
+ return &conflict{
+ Reason: conflictReason{
+ Name: responseName,
+ Message: `they have differing arguments`,
+ },
+ FieldsLeft: []ast.Node{ast1},
+ FieldsRight: []ast.Node{ast2},
+ }
+ }
+ }
+
+ if type1 != nil && type2 != nil && doTypesConflict(type1, type2) {
+ return &conflict{
+ Reason: conflictReason{
+ Name: responseName,
+ Message: fmt.Sprintf(`they return conflicting types %v and %v`, type1, type2),
+ },
+ FieldsLeft: []ast.Node{ast1},
+ FieldsRight: []ast.Node{ast2},
+ }
+ }
+
+ // Collect and compare sub-fields. Use the same "visited fragment names" list
+ // for both collections so fields in a fragment reference are never
+ // compared to themselves.
+ selectionSet1 := ast1.SelectionSet
+ selectionSet2 := ast2.SelectionSet
+ if selectionSet1 != nil && selectionSet2 != nil {
+ conflicts := rule.findConflictsBetweenSubSelectionSets(areMutuallyExclusive, GetNamed(type1), selectionSet1, GetNamed(type2), selectionSet2)
+ return subfieldConflicts(conflicts, responseName, ast1, ast2)
+ }
+ return nil
+}
+
+// Given a selection set, return the collection of fields (a mapping of response
+// name to field ASTs and definitions) as well as a list of fragment names
+// referenced via fragment spreads.
+func (rule *overlappingFieldsCanBeMergedRule) getFieldsAndFragmentNames(parentType Named, selectionSet *ast.SelectionSet) *fieldsAndFragmentNames {
+ if cached, ok := rule.cacheMap[selectionSet]; ok && cached != nil {
+ return cached
+ }
+
+ astAndDefs := astAndDefCollection{}
+ fieldsOrder := []string{}
+ fragmentNames := []string{}
+ fragmentNamesMap := map[string]bool{}
+
+ var collectFieldsAndFragmentNames func(parentType Named, selectionSet *ast.SelectionSet)
+ collectFieldsAndFragmentNames = func(parentType Named, selectionSet *ast.SelectionSet) {
+ for _, selection := range selectionSet.Selections {
+ switch selection := selection.(type) {
+ case *ast.Field:
+ fieldName := ""
+ if selection.Name != nil {
+ fieldName = selection.Name.Value
+ }
+ var fieldDef *FieldDefinition
+ if parentType, ok := parentType.(*Object); ok && parentType != nil {
+ fieldDef, _ = parentType.Fields()[fieldName]
+ }
+ if parentType, ok := parentType.(*Interface); ok && parentType != nil {
+ fieldDef, _ = parentType.Fields()[fieldName]
+ }
+
+ responseName := fieldName
+ if selection.Alias != nil {
+ responseName = selection.Alias.Value
+ }
+
+ fieldDefPairs, ok := astAndDefs[responseName]
+ if !ok || fieldDefPairs == nil {
+ fieldDefPairs = []*fieldDefPair{}
+ fieldsOrder = append(fieldsOrder, responseName)
+ }
+
+ fieldDefPairs = append(fieldDefPairs, &fieldDefPair{
+ ParentType: parentType,
+ Field: selection,
+ FieldDef: fieldDef,
+ })
+ astAndDefs[responseName] = fieldDefPairs
+ case *ast.FragmentSpread:
+ fieldName := ""
+ if selection.Name != nil {
+ fieldName = selection.Name.Value
+ }
+ if val, ok := fragmentNamesMap[fieldName]; !ok || !val {
+ fragmentNamesMap[fieldName] = true
+ fragmentNames = append(fragmentNames, fieldName)
+ }
+ case *ast.InlineFragment:
+ typeCondition := selection.TypeCondition
+ inlineFragmentType := parentType
+ if typeCondition != nil {
+ ttype, err := typeFromAST(*(rule.context.Schema()), typeCondition)
+ if err == nil {
+ inlineFragmentType, _ = ttype.(Named)
+ }
+ }
+ collectFieldsAndFragmentNames(inlineFragmentType, selection.SelectionSet)
+ }
+ }
+ }
+ collectFieldsAndFragmentNames(parentType, selectionSet)
+
+ cached := &fieldsAndFragmentNames{
+ fieldMap: astAndDefs,
+ fieldsOrder: fieldsOrder,
+ fragmentNames: fragmentNames,
+ }
+
+ rule.cacheMap[selectionSet] = cached
+ return cached
+}
+
+func (rule *overlappingFieldsCanBeMergedRule) getReferencedFieldsAndFragmentNames(fragment *ast.FragmentDefinition) *fieldsAndFragmentNames {
+ // Short-circuit building a type from the AST if possible.
+ if cached, ok := rule.cacheMap[fragment.SelectionSet]; ok && cached != nil {
+ return cached
+ }
+ fragmentType, err := typeFromAST(*(rule.context.Schema()), fragment.TypeCondition)
+ if err != nil {
+ return nil
+ }
+ return rule.getFieldsAndFragmentNames(fragmentType, fragment.SelectionSet)
+}
+
+type conflictReason struct {
+ Name string
+ Message interface{} // conflictReason || []conflictReason
+}
+type conflict struct {
+ Reason conflictReason
+ FieldsLeft []ast.Node
+ FieldsRight []ast.Node
+}
+
+// a.k.a AstAndDef
+type fieldDefPair struct {
+ ParentType Named
+ Field *ast.Field
+ FieldDef *FieldDefinition
+}
+type astAndDefCollection map[string][]*fieldDefPair
+
+// cache struct for fields, its order and fragments names
+type fieldsAndFragmentNames struct {
+ fieldMap astAndDefCollection
+ fieldsOrder []string // stores the order of field names in fieldMap
+ fragmentNames []string
+}
+
+// pairSet A way to keep track of pairs of things when the ordering of the pair does
+// not matter. We do this by maintaining a sort of double adjacency sets.
+type pairSet struct {
+ data map[string]map[string]bool
+}
+
+func newPairSet() *pairSet {
+ return &pairSet{
+ data: map[string]map[string]bool{},
+ }
+}
+func (pair *pairSet) Has(a string, b string, areMutuallyExclusive bool) bool {
+ first, ok := pair.data[a]
+ if !ok || first == nil {
+ return false
+ }
+ res, ok := first[b]
+ if !ok {
+ return false
+ }
+ // areMutuallyExclusive being false is a superset of being true,
+ // hence if we want to know if this PairSet "has" these two with no
+ // exclusivity, we have to ensure it was added as such.
+ if !areMutuallyExclusive {
+ return res == false
+ }
+ return true
+}
+func (pair *pairSet) Add(a string, b string, areMutuallyExclusive bool) {
+ pair.data = pairSetAdd(pair.data, a, b, areMutuallyExclusive)
+ pair.data = pairSetAdd(pair.data, b, a, areMutuallyExclusive)
+}
+func pairSetAdd(data map[string]map[string]bool, a, b string, areMutuallyExclusive bool) map[string]map[string]bool {
+ set, ok := data[a]
+ if !ok || set == nil {
+ set = map[string]bool{}
+ }
+ set[b] = areMutuallyExclusive
+ data[a] = set
+ return data
+}
+
+func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool {
+ if len(args1) != len(args2) {
+ return false
+ }
+
+ for _, arg1 := range args1 {
+ arg1Name := ""
+ if arg1.Name != nil {
+ arg1Name = arg1.Name.Value
+ }
+
+ var foundArgs2 *ast.Argument
+ for _, arg2 := range args2 {
+ arg2Name := ""
+ if arg2.Name != nil {
+ arg2Name = arg2.Name.Value
+ }
+ if arg1Name == arg2Name {
+ foundArgs2 = arg2
+ }
+ break
+ }
+ if foundArgs2 == nil {
+ return false
+ }
+ if sameValue(arg1.Value, foundArgs2.Value) == false {
+ return false
+ }
+ }
+
+ return true
+}
+
+func sameValue(value1 ast.Value, value2 ast.Value) bool {
+ if value1 == nil && value2 == nil {
+ return true
+ }
+ val1 := printer.Print(value1)
+ val2 := printer.Print(value2)
+
+ return val1 == val2
+}
+
+// Two types conflict if both types could not apply to a value simultaneously.
+// Composite types are ignored as their individual field types will be compared
+// later recursively. However List and Non-Null types must match.
+func doTypesConflict(type1 Output, type2 Output) bool {
+ if type1, ok := type1.(*List); ok {
+ if type2, ok := type2.(*List); ok {
+ return doTypesConflict(type1.OfType, type2.OfType)
+ }
+ return true
+ }
+ if type2, ok := type2.(*List); ok {
+ if type1, ok := type1.(*List); ok {
+ return doTypesConflict(type1.OfType, type2.OfType)
+ }
+ return true
+ }
+ if type1, ok := type1.(*NonNull); ok {
+ if type2, ok := type2.(*NonNull); ok {
+ return doTypesConflict(type1.OfType, type2.OfType)
+ }
+ return true
+ }
+ if type2, ok := type2.(*NonNull); ok {
+ if type1, ok := type1.(*NonNull); ok {
+ return doTypesConflict(type1.OfType, type2.OfType)
+ }
+ return true
+ }
+ if IsLeafType(type1) || IsLeafType(type2) {
+ return type1 != type2
+ }
+ return false
+}
+
+// subfieldConflicts Given a series of Conflicts which occurred between two sub-fields, generate a single Conflict.
+func subfieldConflicts(conflicts []conflict, responseName string, ast1 *ast.Field, ast2 *ast.Field) *conflict {
+ if len(conflicts) > 0 {
+ conflictReasons := []conflictReason{}
+ conflictFieldsLeft := []ast.Node{ast1}
+ conflictFieldsRight := []ast.Node{ast2}
+ for _, c := range conflicts {
+ conflictReasons = append(conflictReasons, c.Reason)
+ conflictFieldsLeft = append(conflictFieldsLeft, c.FieldsLeft...)
+ conflictFieldsRight = append(conflictFieldsRight, c.FieldsRight...)
+ }
+
+ return &conflict{
+ Reason: conflictReason{
+ Name: responseName,
+ Message: conflictReasons,
+ },
+ FieldsLeft: conflictFieldsLeft,
+ FieldsRight: conflictFieldsRight,
+ }
+ }
+ return nil
+}