@@ -1245,16 +1245,53 @@ func sameType(typeA, typeB Type) bool {
12451245 return false
12461246}
12471247
1248+ // Two types conflict if both types could not apply to a value simultaneously.
1249+ // Composite types are ignored as their individual field types will be compared
1250+ // later recursively. However List and Non-Null types must match.
1251+ func doTypesConflict (type1 Output , type2 Output ) bool {
1252+ if type1 , ok := type1 .(* List ); ok {
1253+ if type2 , ok := type2 .(* List ); ok {
1254+ return doTypesConflict (type1 .OfType , type2 .OfType )
1255+ }
1256+ return true
1257+ }
1258+ if type2 , ok := type2 .(* List ); ok {
1259+ if type1 , ok := type1 .(* List ); ok {
1260+ return doTypesConflict (type1 .OfType , type2 .OfType )
1261+ }
1262+ return true
1263+ }
1264+ if type1 , ok := type1 .(* NonNull ); ok {
1265+ if type2 , ok := type2 .(* NonNull ); ok {
1266+ return doTypesConflict (type1 .OfType , type2 .OfType )
1267+ }
1268+ return true
1269+ }
1270+ if type2 , ok := type2 .(* NonNull ); ok {
1271+ if type1 , ok := type1 .(* NonNull ); ok {
1272+ return doTypesConflict (type1 .OfType , type2 .OfType )
1273+ }
1274+ return true
1275+ }
1276+ if IsLeafType (type1 ) || IsLeafType (type2 ) {
1277+ return type1 != type2
1278+ }
1279+ return false
1280+ }
1281+
12481282// OverlappingFieldsCanBeMergedRule Overlapping fields can be merged
12491283//
12501284// A selection set is only valid if all fields (including spreading any
12511285// fragments) either correspond to distinct response names or can be merged
12521286// without ambiguity.
12531287func OverlappingFieldsCanBeMergedRule (context * ValidationContext ) * ValidationRuleInstance {
12541288
1289+ var getSubfieldMap func (ast1 * ast.Field , type1 Output , ast2 * ast.Field , type2 Output ) map [string ][]* fieldDefPair
1290+ var subfieldConflicts func (conflicts []* conflict , responseName string , ast1 * ast.Field , ast2 * ast.Field ) * conflict
1291+ var findConflicts func (parentFieldsAreMutuallyExclusive bool , fieldMap map [string ][]* fieldDefPair ) (conflicts []* conflict )
1292+
12551293 comparedSet := newPairSet ()
1256- var findConflicts func (fieldMap map [string ][]* fieldDefPair ) (conflicts []* conflict )
1257- findConflict := func (responseName string , field * fieldDefPair , field2 * fieldDefPair ) * conflict {
1294+ findConflict := func (parentFieldsAreMutuallyExclusive bool , responseName string , field * fieldDefPair , field2 * fieldDefPair ) * conflict {
12581295
12591296 parentType1 := field .ParentType
12601297 ast1 := field .Field
@@ -1269,46 +1306,21 @@ func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRul
12691306 return nil
12701307 }
12711308
1272- // If the statically known parent types could not possibly apply at the same
1273- // time, then it is safe to permit them to diverge as they will not present
1274- // any ambiguity by differing.
1275- // It is known that two parent types could never overlap if they are
1276- // different Object types. Interface or Union types might overlap - if not
1277- // in the current state of the schema, then perhaps in some future version,
1278- // thus may not safely diverge.
1279- if parentType1 != parentType2 {
1280- _ , ok1 := parentType1 .(* Object )
1281- _ , ok2 := parentType2 .(* Object )
1282- if ok1 && ok2 {
1283- return nil
1284- }
1285- }
1286-
12871309 // Memoize, do not report the same issue twice.
1310+ // Note: Two overlapping ASTs could be encountered both when
1311+ // `parentFieldsAreMutuallyExclusive` is true and is false, which could
1312+ // produce different results (when `true` being a subset of `false`).
1313+ // However we do not need to include this piece of information when
1314+ // memoizing since this rule visits leaf fields before their parent fields,
1315+ // ensuring that `parentFieldsAreMutuallyExclusive` is `false` the first
1316+ // time two overlapping fields are encountered, ensuring that the full
1317+ // set of validation rules are always checked when necessary.
12881318 if comparedSet .Has (ast1 , ast2 ) {
12891319 return nil
12901320 }
12911321 comparedSet .Add (ast1 , ast2 )
12921322
1293- name1 := ""
1294- if ast1 .Name != nil {
1295- name1 = ast1 .Name .Value
1296- }
1297- name2 := ""
1298- if ast2 .Name != nil {
1299- name2 = ast2 .Name .Value
1300- }
1301- if name1 != name2 {
1302- return & conflict {
1303- Reason : conflictReason {
1304- Name : responseName ,
1305- Message : fmt .Sprintf (`%v and %v are different fields` , name1 , name2 ),
1306- },
1307- FieldsLeft : []ast.Node {ast1 },
1308- FieldsRight : []ast.Node {ast2 },
1309- }
1310- }
1311-
1323+ // The return type for each field.
13121324 var type1 Type
13131325 var type2 Type
13141326 if def1 != nil {
@@ -1318,27 +1330,74 @@ func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRul
13181330 type2 = def2 .Type
13191331 }
13201332
1321- if type1 != nil && type2 != nil && ! sameType (type1 , type2 ) {
1322- return & conflict {
1323- Reason : conflictReason {
1324- Name : responseName ,
1325- Message : fmt .Sprintf (`they return differing types %v and %v` , type1 , type2 ),
1326- },
1327- FieldsLeft : []ast.Node {ast1 },
1328- FieldsRight : []ast.Node {ast2 },
1333+ // If it is known that two fields could not possibly apply at the same
1334+ // time, due to the parent types, then it is safe to permit them to diverge
1335+ // in aliased field or arguments used as they will not present any ambiguity
1336+ // by differing.
1337+ // It is known that two parent types could never overlap if they are
1338+ // different Object types. Interface or Union types might overlap - if not
1339+ // in the current state of the schema, then perhaps in some future version,
1340+ // thus may not safely diverge.
1341+ _ , isParentType1Object := parentType1 .(* Object )
1342+ _ , isParentType2Object := parentType2 .(* Object )
1343+ fieldsAreMutuallyExclusive := parentFieldsAreMutuallyExclusive || parentType1 != parentType2 && isParentType1Object && isParentType2Object
1344+
1345+ if ! fieldsAreMutuallyExclusive {
1346+ // Two aliases must refer to the same field.
1347+ name1 := ""
1348+ name2 := ""
1349+
1350+ if ast1 .Name != nil {
1351+ name1 = ast1 .Name .Value
1352+ }
1353+ if ast2 .Name != nil {
1354+ name2 = ast2 .Name .Value
1355+ }
1356+ if name1 != name2 {
1357+ return & conflict {
1358+ Reason : conflictReason {
1359+ Name : responseName ,
1360+ Message : fmt .Sprintf (`%v and %v are different fields` , name1 , name2 ),
1361+ },
1362+ FieldsLeft : []ast.Node {ast1 },
1363+ FieldsRight : []ast.Node {ast2 },
1364+ }
1365+ }
1366+
1367+ // Two field calls must have the same arguments.
1368+ if ! sameArguments (ast1 .Arguments , ast2 .Arguments ) {
1369+ return & conflict {
1370+ Reason : conflictReason {
1371+ Name : responseName ,
1372+ Message : `they have differing arguments` ,
1373+ },
1374+ FieldsLeft : []ast.Node {ast1 },
1375+ FieldsRight : []ast.Node {ast2 },
1376+ }
13291377 }
13301378 }
1331- if ! sameArguments (ast1 .Arguments , ast2 .Arguments ) {
1379+
1380+ if type1 != nil && type2 != nil && doTypesConflict (type1 , type2 ) {
13321381 return & conflict {
13331382 Reason : conflictReason {
13341383 Name : responseName ,
1335- Message : `they have differing arguments` ,
1384+ Message : fmt . Sprintf ( `they return conflicting types %v and %v` , type1 , type2 ) ,
13361385 },
13371386 FieldsLeft : []ast.Node {ast1 },
13381387 FieldsRight : []ast.Node {ast2 },
13391388 }
13401389 }
13411390
1391+ subFieldMap := getSubfieldMap (ast1 , type1 , ast2 , type2 )
1392+ if subFieldMap != nil {
1393+ conflicts := findConflicts (fieldsAreMutuallyExclusive , subFieldMap )
1394+ return subfieldConflicts (conflicts , responseName , ast1 , ast2 )
1395+ }
1396+
1397+ return nil
1398+ }
1399+
1400+ getSubfieldMap = func (ast1 * ast.Field , type1 Output , ast2 * ast.Field , type2 Output ) map [string ][]* fieldDefPair {
13421401 selectionSet1 := ast1 .SelectionSet
13431402 selectionSet2 := ast2 .SelectionSet
13441403 if selectionSet1 != nil && selectionSet2 != nil {
@@ -1357,32 +1416,34 @@ func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRul
13571416 visitedFragmentNames ,
13581417 subfieldMap ,
13591418 )
1360- conflicts := findConflicts (subfieldMap )
1361- if len (conflicts ) > 0 {
1362-
1363- conflictReasons := []conflictReason {}
1364- conflictFieldsLeft := []ast.Node {ast1 }
1365- conflictFieldsRight := []ast.Node {ast2 }
1366- for _ , c := range conflicts {
1367- conflictReasons = append (conflictReasons , c .Reason )
1368- conflictFieldsLeft = append (conflictFieldsLeft , c .FieldsLeft ... )
1369- conflictFieldsRight = append (conflictFieldsRight , c .FieldsRight ... )
1370- }
1419+ return subfieldMap
1420+ }
1421+ return nil
1422+ }
13711423
1372- return & conflict {
1373- Reason : conflictReason {
1374- Name : responseName ,
1375- Message : conflictReasons ,
1376- },
1377- FieldsLeft : conflictFieldsLeft ,
1378- FieldsRight : conflictFieldsRight ,
1379- }
1424+ subfieldConflicts = func (conflicts []* conflict , responseName string , ast1 * ast.Field , ast2 * ast.Field ) * conflict {
1425+ if len (conflicts ) > 0 {
1426+ conflictReasons := []conflictReason {}
1427+ conflictFieldsLeft := []ast.Node {ast1 }
1428+ conflictFieldsRight := []ast.Node {ast2 }
1429+ for _ , c := range conflicts {
1430+ conflictReasons = append (conflictReasons , c .Reason )
1431+ conflictFieldsLeft = append (conflictFieldsLeft , c .FieldsLeft ... )
1432+ conflictFieldsRight = append (conflictFieldsRight , c .FieldsRight ... )
1433+ }
1434+
1435+ return & conflict {
1436+ Reason : conflictReason {
1437+ Name : responseName ,
1438+ Message : conflictReasons ,
1439+ },
1440+ FieldsLeft : conflictFieldsLeft ,
1441+ FieldsRight : conflictFieldsRight ,
13801442 }
13811443 }
13821444 return nil
13831445 }
1384-
1385- findConflicts = func (fieldMap map [string ][]* fieldDefPair ) (conflicts []* conflict ) {
1446+ findConflicts = func (parentFieldsAreMutuallyExclusive bool , fieldMap map [string ][]* fieldDefPair ) (conflicts []* conflict ) {
13861447
13871448 // ensure field traversal
13881449 orderedName := sort.StringSlice {}
@@ -1395,7 +1456,7 @@ func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRul
13951456 fields , _ := fieldMap [responseName ]
13961457 for _ , fieldA := range fields {
13971458 for _ , fieldB := range fields {
1398- c := findConflict (responseName , fieldA , fieldB )
1459+ c := findConflict (parentFieldsAreMutuallyExclusive , responseName , fieldA , fieldB )
13991460 if c != nil {
14001461 conflicts = append (conflicts , c )
14011462 }
@@ -1429,6 +1490,9 @@ func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRul
14291490 visitorOpts := & visitor.VisitorOptions {
14301491 KindFuncMap : map [string ]visitor.NamedVisitFuncs {
14311492 kinds .SelectionSet : visitor.NamedVisitFuncs {
1493+ // Note: we validate on the reverse traversal so deeper conflicts will be
1494+ // caught first, for correct calculation of mutual exclusivity and for
1495+ // clearer error messages.
14321496 Leave : func (p visitor.VisitFuncParams ) (string , interface {}) {
14331497 if selectionSet , ok := p .Node .(* ast.SelectionSet ); ok && selectionSet != nil {
14341498 parentType , _ := context .ParentType ().(Named )
@@ -1439,7 +1503,7 @@ func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRul
14391503 nil ,
14401504 nil ,
14411505 )
1442- conflicts := findConflicts (fieldMap )
1506+ conflicts := findConflicts (false , fieldMap )
14431507 if len (conflicts ) > 0 {
14441508 for _ , c := range conflicts {
14451509 responseName := c .Reason .Name
0 commit comments