Repository: apollographql/apollo-rs
Pull request #465 (2023-03-01 10:24:06)

Leaf selection
implementation of https://github.com/apollographql/apollo-rs/issues/307

Contains https://github.com/apollographql/apollo-rs/pull/464

https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections


Patch:
diff --git a/crates/apollo-compiler/src/database/ast.rs b/crates/apollo-compiler/src/database/ast.rs
index 300175a..6e5fe94 100644
--- a/crates/apollo-compiler/src/database/ast.rs
+++ b/crates/apollo-compiler/src/database/ast.rs
@@ -46,7 +46,6 @@ fn syntax_errors(db: &dyn AstDatabase) -> Vec<ApolloDiagnostic> {
         .flat_map(|file_id| {
             db.ast(file_id)
                 .errors()
-                .into_iter()
                 .map(|err| {
                     ApolloDiagnostic::new(
                         db,
diff --git a/crates/apollo-compiler/src/database/hir_db.rs b/crates/apollo-compiler/src/database/hir_db.rs
index 4fbece8..5f09b47 100644
--- a/crates/apollo-compiler/src/database/hir_db.rs
+++ b/crates/apollo-compiler/src/database/hir_db.rs
@@ -677,7 +677,6 @@ fn enum_values_definition(
         Some(enum_values) => {
             let enum_values = enum_values
                 .enum_value_definitions()
-                .into_iter()
                 .filter_map(|e| enum_value_definition(e, file_id))
                 .collect();
             Arc::new(enum_values)
@@ -747,7 +746,6 @@ fn union_members(
         Some(members) => {
             let mems = members
                 .named_types()
-                .into_iter()
                 .filter_map(|u| union_member(u, file_id))
                 .collect();
             Arc::new(mems)
@@ -1096,7 +1094,6 @@ fn variable_definitions(
         Some(vars) => {
             let variable_definitions = vars
                 .variable_definitions()
-                .into_iter()
                 .filter_map(|v| variable_definition(v, file_id))
                 .collect();
             Arc::new(variable_definitions)
@@ -1171,7 +1168,6 @@ fn directive_locations(
         Some(directive_loc) => {
             let locations: Vec<DirectiveLocation> = directive_loc
                 .directive_locations()
-                .into_iter()
                 .map(|loc| loc.into())
                 .collect();
             Arc::new(locations)
@@ -1185,7 +1181,6 @@ fn directives(directives: Option<ast::Directives>, file_id: FileId) -> Arc<Vec<D
         Some(directives) => {
             let directives = directives
                 .directives()
-                .into_iter()
                 .filter_map(|d| directive(d, file_id))
                 .collect();
             Arc::new(directives)
@@ -1211,7 +1206,6 @@ fn arguments(arguments: Option<ast::Arguments>, file_id: FileId) -> Arc<Vec<Argu
         Some(arguments) => {
             let arguments = arguments
                 .arguments()
-                .into_iter()
                 .filter_map(|a| argument(a, file_id))
                 .collect();
             Arc::new(arguments)
@@ -1269,7 +1263,6 @@ fn selection_set(
     let selection_set = match selections {
         Some(sel) => sel
             .selections()
-            .into_iter()
             .filter_map(|sel| selection(db, sel, parent_obj_ty.as_ref().cloned(), file_id))
             .collect(),
         None => Vec::new(),
diff --git a/crates/apollo-compiler/src/diagnostics.rs b/crates/apollo-compiler/src/diagnostics.rs
index f1aa73f..0329521 100644
--- a/crates/apollo-compiler/src/diagnostics.rs
+++ b/crates/apollo-compiler/src/diagnostics.rs
@@ -265,6 +265,10 @@ pub enum DiagnosticData {
         /// Name of the field
         field: String,
     },
+    #[error("subselection set for scalar and enum types must be empty")]
+    DisallowedSubselection,
+    #[error("interface, union and object types must have a subselection set")]
+    MissingSubselection,
 }
 
 impl DiagnosticData {
diff --git a/crates/apollo-compiler/src/lib.rs b/crates/apollo-compiler/src/lib.rs
index abf6521..4fe72ed 100644
--- a/crates/apollo-compiler/src/lib.rs
+++ b/crates/apollo-compiler/src/lib.rs
@@ -574,7 +574,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 "#;
 
         let product_query = r#"query getProduct { topProducts { type } }"#;
-        let customer_query = r#"{ customer }"#;
+        let customer_query = r#"{ customer { id } }"#;
         let colliding_query = r#"query getProduct { topProducts { type, price } }"#;
 
         let mut compiler = ApolloCompiler::new();
diff --git a/crates/apollo-compiler/src/validation/field.rs b/crates/apollo-compiler/src/validation/field.rs
index f52f1a1..2159d17 100644
--- a/crates/apollo-compiler/src/validation/field.rs
+++ b/crates/apollo-compiler/src/validation/field.rs
@@ -75,25 +75,28 @@ pub fn validate_field(
     }
 
     let field_type = field.ty(db.upcast());
-    if field_type.is_some() {
-        diagnostics.extend(db.validate_selection_set(field.selection_set().clone()));
+    if let Some(field_type) = field_type {
+        match db.validate_leaf_field_selection(field.clone(), field_type) {
+            Err(diag) => diagnostics.push(diag),
+            Ok(_) => diagnostics.extend(db.validate_selection_set(field.selection_set().clone())),
+        }
     } else {
         let help = format!(
             "`{}` is not defined on `{}` type",
             field.name(),
             field.parent_obj.as_ref().unwrap(),
         );
-        let field_name = field.name();
+        let fname = field.name();
         let diagnostic = ApolloDiagnostic::new(
             db,
             field.loc().into(),
             DiagnosticData::UndefinedField {
-                field: field_name.into(),
+                field: fname.into(),
             },
         )
         .label(Label::new(
             field.loc(),
-            format!("`{field_name}` field is not defined"),
+            format!("`{fname}` field is not defined"),
         ))
         .help(help);
 
@@ -146,29 +149,35 @@ pub fn validate_field_definitions(
         // Fields must be unique.
         //
         // Returns Unique Field error.
-        let field_name = field.name();
+        let fname = field.name();
         let redefined_definition = field.loc();
 
-        if let Some(prev_field) = seen.get(field_name) {
+        if let Some(prev_field) = seen.get(fname) {
             let original_definition = prev_field.loc();
 
             diagnostics.push(
                 ApolloDiagnostic::new(
-                    db, original_definition.into(),
+                    db,
+                    original_definition.into(),
                     DiagnosticData::UniqueField {
-                        field: field_name.into(),
+                        field: fname.into(),
                         original_definition: original_definition.into(),
                         redefined_definition: redefined_definition.into(),
                     },
                 )
-                    .labels([
-                        Label::new(original_definition, format!("previous definition of `{field_name}` here")),
-                        Label::new(redefined_definition, format!("`{field_name}` redefined here")),
-                    ])
-                    .help(format!("`{field_name}` field must only be defined once in this input object definition."))
+                .labels([
+                    Label::new(
+                        original_definition,
+                        format!("previous definition of `{fname}` here"),
+                    ),
+                    Label::new(redefined_definition, format!("`{fname}` redefined here")),
+                ])
+                .help(format!(
+                    "`{fname}` field must only be defined once in this input object definition."
+                )),
             );
         } else {
-            seen.insert(field_name, field);
+            seen.insert(fname, field);
         }
 
         // Field types in Object Types must be of output type
@@ -210,3 +219,58 @@ pub fn validate_field_definitions(
 
     diagnostics
 }
+
+// TODO(@lrlna): don't allow this!!
+#[allow(clippy::result_large_err)]
+pub fn validate_leaf_field_selection(
+    db: &dyn ValidationDatabase,
+    field: Arc<hir::Field>,
+    field_type: hir::Type,
+) -> Result<(), ApolloDiagnostic> {
+    let is_leaf = field.selection_set.selection.is_empty();
+    let tname = field_type.name();
+    let fname = field.name.src.clone();
+
+    let type_def = match db.find_type_definition_by_name(tname.clone()) {
+        Some(type_def) => type_def,
+        None => return Ok(()),
+    };
+
+    let (label, diagnostic_data) = if is_leaf {
+        let label = match type_def {
+            hir::TypeDefinition::ObjectTypeDefinition(_) => {
+                format!("field `{fname}` type `{tname}` is an object and must select fields")
+            }
+            hir::TypeDefinition::InterfaceTypeDefinition(_) => {
+                format!("field `{fname}` type `{tname}` is an interface and must select fields")
+            }
+            hir::TypeDefinition::UnionTypeDefinition(_) => {
+                format!("field `{fname}` type `{tname}` is an union and must select fields")
+            }
+            _ => return Ok(()),
+        };
+        (label, DiagnosticData::MissingSubselection)
+    } else {
+        let label = match type_def {
+            hir::TypeDefinition::EnumTypeDefinition(_) => {
+                format!("field `{fname}` of type `{tname}` is an enum and cannot select any fields")
+            }
+            hir::TypeDefinition::ScalarTypeDefinition(_) => format!(
+                "field `{fname}` of type `{tname}` is a scalar and cannot select any fields"
+            ),
+            _ => return Ok(()),
+        };
+        (label, DiagnosticData::DisallowedSubselection)
+    };
+
+    let diagnostic = ApolloDiagnostic::new(db, field.loc.into(), diagnostic_data)
+        .label(Label::new(field.loc, label));
+
+    match type_def.loc() {
+        Some(type_def_loc) => {
+            let s = format!("`{tname}` declared here");
+            Err(diagnostic.label(Label::new(type_def_loc, s)))
+        }
+        None => Err(diagnostic),
+    }
+}
diff --git a/crates/apollo-compiler/src/validation/interface.rs b/crates/apollo-compiler/src/validation/interface.rs
index 5d679d8..2e0ceb3 100644
--- a/crates/apollo-compiler/src/validation/interface.rs
+++ b/crates/apollo-compiler/src/validation/interface.rs
@@ -360,7 +360,9 @@ interface Image implements Resource & Node {
     fn it_generates_diagnostics_for_non_output_field_types() {
         let input = r#"
 query mainPage {
-  name
+  name {
+    width
+  }
 }
 
 type Query {
diff --git a/crates/apollo-compiler/src/validation/object.rs b/crates/apollo-compiler/src/validation/object.rs
index 67cee15..e051ee2 100644
--- a/crates/apollo-compiler/src/validation/object.rs
+++ b/crates/apollo-compiler/src/validation/object.rs
@@ -98,8 +98,14 @@ mod test {
         let input = r#"
 query mainPage {
   width
-  result
-  entity
+  result {
+    ... on Person {
+      name
+    }
+  }
+  entity {
+    name
+  }
 }
 
 type Query {
diff --git a/crates/apollo-compiler/src/validation/selection.rs b/crates/apollo-compiler/src/validation/selection.rs
index 311018e..36d5233 100644
--- a/crates/apollo-compiler/src/validation/selection.rs
+++ b/crates/apollo-compiler/src/validation/selection.rs
@@ -35,8 +35,6 @@ pub fn validate_selection_set(
 ) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
-    // TODO handle Enums, Scalar (no selection set content allowed), Unions
-
     diagnostics.extend(db.validate_selection(selection_set.selection));
 
     diagnostics
diff --git a/crates/apollo-compiler/src/validation/validation_db.rs b/crates/apollo-compiler/src/validation/validation_db.rs
index 84af812..b076c8c 100644
--- a/crates/apollo-compiler/src/validation/validation_db.rs
+++ b/crates/apollo-compiler/src/validation/validation_db.rs
@@ -145,6 +145,13 @@ pub trait ValidationDatabase:
     #[salsa::invoke(field::validate_field)]
     fn validate_field(&self, field: Arc<Field>) -> Vec<ApolloDiagnostic>;
 
+    #[salsa::invoke(field::validate_leaf_field_selection)]
+    fn validate_leaf_field_selection(
+        &self,
+        field: Arc<Field>,
+        field_ty: Type,
+    ) -> Result<(), ApolloDiagnostic>;
+
     #[salsa::invoke(argument::validate_arguments_definition)]
     fn validate_arguments_definition(
         &self,


Test patch:
diff --git a/crates/apollo-compiler/test_data/diagnostics/0036_object_type_with_non_output_field_types.graphql b/crates/apollo-compiler/test_data/diagnostics/0036_object_type_with_non_output_field_types.graphql
index bf2e374..f6e9830 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0036_object_type_with_non_output_field_types.graphql
+++ b/crates/apollo-compiler/test_data/diagnostics/0036_object_type_with_non_output_field_types.graphql
@@ -1,7 +1,13 @@
 query mainPage {
   width
-  result
-  entity
+  result {
+    ... on Person {
+      name
+    }
+  }
+  entity {
+    name
+  }
 }
 
 type Query {
@@ -39,4 +45,4 @@ input Point2D {
   y: Float
 }
 
-scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
\ No newline at end of file
+scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
diff --git a/crates/apollo-compiler/test_data/diagnostics/0036_object_type_with_non_output_field_types.txt b/crates/apollo-compiler/test_data/diagnostics/0036_object_type_with_non_output_field_types.txt
index 5698d21..d9bc1df 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0036_object_type_with_non_output_field_types.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0036_object_type_with_non_output_field_types.txt
@@ -7,7 +7,7 @@
             file_id: FileId {
                 id: 36,
             },
-            offset: 367,
+            offset: 425,
             length: 5,
         },
         labels: [
@@ -16,7 +16,7 @@
                     file_id: FileId {
                         id: 36,
                     },
-                    offset: 367,
+                    offset: 425,
                     length: 5,
                 },
                 text: "not found in this scope",
@@ -35,7 +35,7 @@
             file_id: FileId {
                 id: 36,
             },
-            offset: 182,
+            offset: 240,
             length: 4,
         },
         labels: [
@@ -44,7 +44,7 @@
                     file_id: FileId {
                         id: 36,
                     },
-                    offset: 182,
+                    offset: 240,
                     length: 4,
                 },
                 text: "not found in this scope",
@@ -63,7 +63,7 @@
             file_id: FileId {
                 id: 36,
             },
-            offset: 189,
+            offset: 247,
             length: 20,
         },
         labels: [
@@ -72,7 +72,7 @@
                     file_id: FileId {
                         id: 36,
                     },
-                    offset: 189,
+                    offset: 247,
                     length: 20,
                 },
                 text: "this is of `InputObjectTypeDefinition` type",
@@ -94,7 +94,7 @@
             file_id: FileId {
                 id: 36,
             },
-            offset: 218,
+            offset: 276,
             length: 8,
         },
         labels: [
@@ -103,7 +103,7 @@
                     file_id: FileId {
                         id: 36,
                     },
-                    offset: 218,
+                    offset: 276,
                     length: 8,
                 },
                 text: "not found in this scope",
diff --git a/crates/apollo-compiler/test_data/diagnostics/0037_interface_with_non_output_fields.graphql b/crates/apollo-compiler/test_data/diagnostics/0037_interface_with_non_output_fields.graphql
index 6259fb5..9873601 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0037_interface_with_non_output_fields.graphql
+++ b/crates/apollo-compiler/test_data/diagnostics/0037_interface_with_non_output_fields.graphql
@@ -1,5 +1,7 @@
 query mainPage {
-  name
+  name {
+    width
+  }
 }
 
 type Query {
@@ -41,4 +43,4 @@ input Point2D {
   y: Float
 }
 
-scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
\ No newline at end of file
+scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
diff --git a/crates/apollo-compiler/test_data/diagnostics/0037_interface_with_non_output_fields.txt b/crates/apollo-compiler/test_data/diagnostics/0037_interface_with_non_output_fields.txt
index fd16a94..e3d1c77 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0037_interface_with_non_output_fields.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0037_interface_with_non_output_fields.txt
@@ -7,7 +7,7 @@
             file_id: FileId {
                 id: 37,
             },
-            offset: 399,
+            offset: 415,
             length: 5,
         },
         labels: [
@@ -16,7 +16,7 @@
                     file_id: FileId {
                         id: 37,
                     },
-                    offset: 399,
+                    offset: 415,
                     length: 5,
                 },
                 text: "not found in this scope",
@@ -35,7 +35,7 @@
             file_id: FileId {
                 id: 37,
             },
-            offset: 214,
+            offset: 230,
             length: 4,
         },
         labels: [
@@ -44,7 +44,7 @@
                     file_id: FileId {
                         id: 37,
                     },
-                    offset: 214,
+                    offset: 230,
                     length: 4,
                 },
                 text: "not found in this scope",
@@ -63,7 +63,7 @@
             file_id: FileId {
                 id: 37,
             },
-            offset: 221,
+            offset: 237,
             length: 20,
         },
         labels: [
@@ -72,7 +72,7 @@
                     file_id: FileId {
                         id: 37,
                     },
-                    offset: 221,
+                    offset: 237,
                     length: 20,
                 },
                 text: "this is of `InputObjectTypeDefinition` type",
@@ -94,7 +94,7 @@
             file_id: FileId {
                 id: 37,
             },
-            offset: 250,
+            offset: 266,
             length: 8,
         },
         labels: [
@@ -103,7 +103,7 @@
                     file_id: FileId {
                         id: 37,
                     },
-                    offset: 250,
+                    offset: 266,
                     length: 8,
                 },
                 text: "not found in this scope",
diff --git a/crates/apollo-compiler/test_data/diagnostics/0054_argument_not_provided.graphql b/crates/apollo-compiler/test_data/diagnostics/0054_argument_not_provided.graphql
index b0cadff..ca4a976 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0054_argument_not_provided.graphql
+++ b/crates/apollo-compiler/test_data/diagnostics/0054_argument_not_provided.graphql
@@ -16,7 +16,9 @@ fragment Y on ComplicatedArgs {
 }
 
 {
-  basic @skip @include(wrong: false)
+  basic @skip @include(wrong: false) {
+    a
+  }
 
   complicatedArgs {
     multipleReqs(req2: 1)
diff --git a/crates/apollo-compiler/test_data/diagnostics/0054_argument_not_provided.txt b/crates/apollo-compiler/test_data/diagnostics/0054_argument_not_provided.txt
index 0539f8f..61d4613 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0054_argument_not_provided.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0054_argument_not_provided.txt
@@ -91,7 +91,7 @@
             file_id: FileId {
                 id: 54,
             },
-            offset: 387,
+            offset: 399,
             length: 21,
         },
         labels: [
@@ -100,7 +100,7 @@
                     file_id: FileId {
                         id: 54,
                     },
-                    offset: 387,
+                    offset: 399,
                     length: 21,
                 },
                 text: "missing value for argument `req1`",
@@ -129,7 +129,7 @@
             file_id: FileId {
                 id: 54,
             },
-            offset: 413,
+            offset: 425,
             length: 26,
         },
         labels: [
@@ -138,7 +138,7 @@
                     file_id: FileId {
                         id: 54,
                     },
-                    offset: 413,
+                    offset: 425,
                     length: 26,
                 },
                 text: "missing value for argument `req2`",
@@ -167,7 +167,7 @@
             file_id: FileId {
                 id: 54,
             },
-            offset: 444,
+            offset: 456,
             length: 44,
         },
         labels: [
@@ -176,7 +176,7 @@
                     file_id: FileId {
                         id: 54,
                     },
-                    offset: 444,
+                    offset: 456,
                     length: 44,
                 },
                 text: "missing value for argument `req2`",
diff --git a/crates/apollo-compiler/test_data/diagnostics/0065_subselection_of_enum.graphql b/crates/apollo-compiler/test_data/diagnostics/0065_subselection_of_enum.graphql
new file mode 100644
index 0000000..d137436
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0065_subselection_of_enum.graphql
@@ -0,0 +1,23 @@
+query SelectionOfEnum {
+  pet1 { name }
+  pet2 { name }
+  pet3 { name }
+  pet4 { name }
+  pet5 { name }
+  pet6 { name }
+}
+
+type Query {
+  pet1: Pet,
+  pet2: Pet!,
+  pet3: [Pet],
+  pet4: [Pet!],
+  pet5: [Pet]!,
+  pet6: [Pet!]!,
+}
+
+enum Pet {
+    CAT
+    DOG
+    FOX
+}
diff --git a/crates/apollo-compiler/test_data/diagnostics/0065_subselection_of_enum.txt b/crates/apollo-compiler/test_data/diagnostics/0065_subselection_of_enum.txt
new file mode 100644
index 0000000..ef830ab
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0065_subselection_of_enum.txt
@@ -0,0 +1,218 @@
+[
+    ApolloDiagnostic {
+        cache: {
+            65: "0065_subselection_of_enum.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 65,
+            },
+            offset: 26,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 26,
+                    length: 13,
+                },
+                text: "field `pet1` of type `Pet` is an enum and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 230,
+                    length: 36,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            65: "0065_subselection_of_enum.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 65,
+            },
+            offset: 42,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 42,
+                    length: 13,
+                },
+                text: "field `pet2` of type `Pet` is an enum and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 230,
+                    length: 36,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            65: "0065_subselection_of_enum.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 65,
+            },
+            offset: 58,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 58,
+                    length: 13,
+                },
+                text: "field `pet3` of type `Pet` is an enum and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 230,
+                    length: 36,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            65: "0065_subselection_of_enum.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 65,
+            },
+            offset: 74,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 74,
+                    length: 13,
+                },
+                text: "field `pet4` of type `Pet` is an enum and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 230,
+                    length: 36,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            65: "0065_subselection_of_enum.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 65,
+            },
+            offset: 90,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 90,
+                    length: 13,
+                },
+                text: "field `pet5` of type `Pet` is an enum and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 230,
+                    length: 36,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            65: "0065_subselection_of_enum.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 65,
+            },
+            offset: 106,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 106,
+                    length: 13,
+                },
+                text: "field `pet6` of type `Pet` is an enum and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 65,
+                    },
+                    offset: 230,
+                    length: 36,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+]
diff --git a/crates/apollo-compiler/test_data/diagnostics/0066_subselection_of_scalar.graphql b/crates/apollo-compiler/test_data/diagnostics/0066_subselection_of_scalar.graphql
new file mode 100644
index 0000000..a19469f
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0066_subselection_of_scalar.graphql
@@ -0,0 +1,19 @@
+query SelectionOfScalar {
+  url1 { name }
+  url2 { name }
+  url3 { name }
+  url4 { name }
+  url5 { name }
+  url6 { name }
+}
+
+type Query {
+  url1: URL,
+  url2: URL!,
+  url3: [URL],
+  url4: [URL!],
+  url5: [URL]!,
+  url6: [URL!]!,
+}
+
+scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
diff --git a/crates/apollo-compiler/test_data/diagnostics/0066_subselection_of_scalar.txt b/crates/apollo-compiler/test_data/diagnostics/0066_subselection_of_scalar.txt
new file mode 100644
index 0000000..2c3e082
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0066_subselection_of_scalar.txt
@@ -0,0 +1,218 @@
+[
+    ApolloDiagnostic {
+        cache: {
+            66: "0066_subselection_of_scalar.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 66,
+            },
+            offset: 28,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 28,
+                    length: 13,
+                },
+                text: "field `url1` of type `URL` is a scalar and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 232,
+                    length: 67,
+                },
+                text: "`URL` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            66: "0066_subselection_of_scalar.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 66,
+            },
+            offset: 44,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 44,
+                    length: 13,
+                },
+                text: "field `url2` of type `URL` is a scalar and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 232,
+                    length: 67,
+                },
+                text: "`URL` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            66: "0066_subselection_of_scalar.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 66,
+            },
+            offset: 60,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 60,
+                    length: 13,
+                },
+                text: "field `url3` of type `URL` is a scalar and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 232,
+                    length: 67,
+                },
+                text: "`URL` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            66: "0066_subselection_of_scalar.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 66,
+            },
+            offset: 76,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 76,
+                    length: 13,
+                },
+                text: "field `url4` of type `URL` is a scalar and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 232,
+                    length: 67,
+                },
+                text: "`URL` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            66: "0066_subselection_of_scalar.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 66,
+            },
+            offset: 92,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 92,
+                    length: 13,
+                },
+                text: "field `url5` of type `URL` is a scalar and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 232,
+                    length: 67,
+                },
+                text: "`URL` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            66: "0066_subselection_of_scalar.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 66,
+            },
+            offset: 108,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 108,
+                    length: 13,
+                },
+                text: "field `url6` of type `URL` is a scalar and cannot select any fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 66,
+                    },
+                    offset: 232,
+                    length: 67,
+                },
+                text: "`URL` declared here",
+            },
+        ],
+        help: None,
+        data: DisallowedSubselection,
+    },
+]
diff --git a/crates/apollo-compiler/test_data/diagnostics/0067_subselection_of_interface.graphql b/crates/apollo-compiler/test_data/diagnostics/0067_subselection_of_interface.graphql
new file mode 100644
index 0000000..3097a9c
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0067_subselection_of_interface.graphql
@@ -0,0 +1,21 @@
+query SelectionOfInterface {
+  pet1
+  pet2
+  pet3
+  pet4
+  pet5
+  pet6
+}
+
+type Query {
+  pet1: Pet,
+  pet2: Pet!,
+  pet3: [Pet],
+  pet4: [Pet!],
+  pet5: [Pet]!,
+  pet6: [Pet!]!,
+}
+
+interface Pet {
+  id: String,
+}
diff --git a/crates/apollo-compiler/test_data/diagnostics/0067_subselection_of_interface.txt b/crates/apollo-compiler/test_data/diagnostics/0067_subselection_of_interface.txt
new file mode 100644
index 0000000..d7cecdd
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0067_subselection_of_interface.txt
@@ -0,0 +1,218 @@
+[
+    ApolloDiagnostic {
+        cache: {
+            67: "0067_subselection_of_interface.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 67,
+            },
+            offset: 31,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 31,
+                    length: 4,
+                },
+                text: "field `pet1` type `Pet` is an interface and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 181,
+                    length: 31,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            67: "0067_subselection_of_interface.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 67,
+            },
+            offset: 38,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 38,
+                    length: 4,
+                },
+                text: "field `pet2` type `Pet` is an interface and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 181,
+                    length: 31,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            67: "0067_subselection_of_interface.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 67,
+            },
+            offset: 45,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 45,
+                    length: 4,
+                },
+                text: "field `pet3` type `Pet` is an interface and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 181,
+                    length: 31,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            67: "0067_subselection_of_interface.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 67,
+            },
+            offset: 52,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 52,
+                    length: 4,
+                },
+                text: "field `pet4` type `Pet` is an interface and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 181,
+                    length: 31,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            67: "0067_subselection_of_interface.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 67,
+            },
+            offset: 59,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 59,
+                    length: 4,
+                },
+                text: "field `pet5` type `Pet` is an interface and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 181,
+                    length: 31,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            67: "0067_subselection_of_interface.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 67,
+            },
+            offset: 66,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 66,
+                    length: 4,
+                },
+                text: "field `pet6` type `Pet` is an interface and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 67,
+                    },
+                    offset: 181,
+                    length: 31,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+]
diff --git a/crates/apollo-compiler/test_data/diagnostics/0068_subselection_of_union.graphql b/crates/apollo-compiler/test_data/diagnostics/0068_subselection_of_union.graphql
new file mode 100644
index 0000000..1beb01b
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0068_subselection_of_union.graphql
@@ -0,0 +1,21 @@
+query SelectionOfUnion {
+  pet1
+  pet2
+  pet3
+  pet4
+  pet5
+  pet6
+}
+
+type Query {
+  pet1: CatOrDog,
+  pet2: CatOrDog!,
+  pet3: [CatOrDog],
+  pet4: [CatOrDog!],
+  pet5: [CatOrDog]!,
+  pet6: [CatOrDog!]!,
+}
+
+type Cat { id: String! }
+type Dog { id: String! }
+union CatOrDog = Cat | Dog
diff --git a/crates/apollo-compiler/test_data/diagnostics/0068_subselection_of_union.txt b/crates/apollo-compiler/test_data/diagnostics/0068_subselection_of_union.txt
new file mode 100644
index 0000000..5b3d842
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0068_subselection_of_union.txt
@@ -0,0 +1,218 @@
+[
+    ApolloDiagnostic {
+        cache: {
+            68: "0068_subselection_of_union.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 68,
+            },
+            offset: 27,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 27,
+                    length: 4,
+                },
+                text: "field `pet1` type `CatOrDog` is an union and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 257,
+                    length: 26,
+                },
+                text: "`CatOrDog` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            68: "0068_subselection_of_union.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 68,
+            },
+            offset: 34,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 34,
+                    length: 4,
+                },
+                text: "field `pet2` type `CatOrDog` is an union and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 257,
+                    length: 26,
+                },
+                text: "`CatOrDog` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            68: "0068_subselection_of_union.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 68,
+            },
+            offset: 41,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 41,
+                    length: 4,
+                },
+                text: "field `pet3` type `CatOrDog` is an union and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 257,
+                    length: 26,
+                },
+                text: "`CatOrDog` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            68: "0068_subselection_of_union.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 68,
+            },
+            offset: 48,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 48,
+                    length: 4,
+                },
+                text: "field `pet4` type `CatOrDog` is an union and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 257,
+                    length: 26,
+                },
+                text: "`CatOrDog` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            68: "0068_subselection_of_union.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 68,
+            },
+            offset: 55,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 55,
+                    length: 4,
+                },
+                text: "field `pet5` type `CatOrDog` is an union and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 257,
+                    length: 26,
+                },
+                text: "`CatOrDog` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            68: "0068_subselection_of_union.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 68,
+            },
+            offset: 62,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 62,
+                    length: 4,
+                },
+                text: "field `pet6` type `CatOrDog` is an union and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 68,
+                    },
+                    offset: 257,
+                    length: 26,
+                },
+                text: "`CatOrDog` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+]
diff --git a/crates/apollo-compiler/test_data/diagnostics/0069_subselection_of_object.graphql b/crates/apollo-compiler/test_data/diagnostics/0069_subselection_of_object.graphql
new file mode 100644
index 0000000..dcec02d
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0069_subselection_of_object.graphql
@@ -0,0 +1,21 @@
+query SelectionOfObject {
+  pet1
+  pet2
+  pet3
+  pet4
+  pet5
+  pet6
+}
+
+type Query {
+  pet1: Pet,
+  pet2: Pet!,
+  pet3: [Pet],
+  pet4: [Pet!],
+  pet5: [Pet]!,
+  pet6: [Pet!]!,
+}
+
+type Pet {
+  id: String,
+}
diff --git a/crates/apollo-compiler/test_data/diagnostics/0069_subselection_of_object.txt b/crates/apollo-compiler/test_data/diagnostics/0069_subselection_of_object.txt
new file mode 100644
index 0000000..a4d6bf6
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0069_subselection_of_object.txt
@@ -0,0 +1,218 @@
+[
+    ApolloDiagnostic {
+        cache: {
+            69: "0069_subselection_of_object.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 69,
+            },
+            offset: 28,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 28,
+                    length: 4,
+                },
+                text: "field `pet1` type `Pet` is an object and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 178,
+                    length: 26,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            69: "0069_subselection_of_object.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 69,
+            },
+            offset: 35,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 35,
+                    length: 4,
+                },
+                text: "field `pet2` type `Pet` is an object and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 178,
+                    length: 26,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            69: "0069_subselection_of_object.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 69,
+            },
+            offset: 42,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 42,
+                    length: 4,
+                },
+                text: "field `pet3` type `Pet` is an object and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 178,
+                    length: 26,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            69: "0069_subselection_of_object.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 69,
+            },
+            offset: 49,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 49,
+                    length: 4,
+                },
+                text: "field `pet4` type `Pet` is an object and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 178,
+                    length: 26,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            69: "0069_subselection_of_object.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 69,
+            },
+            offset: 56,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 56,
+                    length: 4,
+                },
+                text: "field `pet5` type `Pet` is an object and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 178,
+                    length: 26,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+    ApolloDiagnostic {
+        cache: {
+            69: "0069_subselection_of_object.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 69,
+            },
+            offset: 63,
+            length: 4,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 63,
+                    length: 4,
+                },
+                text: "field `pet6` type `Pet` is an object and must select fields",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 69,
+                    },
+                    offset: 178,
+                    length: 26,
+                },
+                text: "`Pet` declared here",
+            },
+        ],
+        help: None,
+        data: MissingSubselection,
+    },
+]