Repository: apollographql/apollo-rs
Pull request #642 (2023-09-08 13:52:01)

fix(compiler): validate input value types
This is a pretty big thing that we missed until now, so I added it to the current validation implementation. Obviously will
also address this in the validation on top of the new AST. Found by @o0Ignition0o \ 😄 /

Input values (in `input object` declarations and in argument declarations) did not have full validation. Only their names were checked, but if you declared a `arg: NonExistentType`, it would happily truck on.

This PR checks that the declared type exists, and is an input type. If you declare an input value or argument with an output type, you get an error.

As a small cleanliness refactor, this moves the object field output type validation into `validate_field_definition`, as it doesn't need context about other fields in a type definition.

The input type validations for variables and arguments/input objects now use different diagnostics so the messages make more sense.


Patch:
diff --git a/crates/apollo-compiler/README.md b/crates/apollo-compiler/README.md
index 01bdce4..6e98671 100644
--- a/crates/apollo-compiler/README.md
+++ b/crates/apollo-compiler/README.md
@@ -173,6 +173,11 @@ fn main() -> Result<()> {
       weight: Int
     }
 
+    enum join__Graph {
+      INVENTORY,
+      PRODUCTS,
+    }
+    scalar join__FieldSet
     directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet) on FIELD_DEFINITION
     "#;
     let query_input = r#"
@@ -194,7 +199,11 @@ fn main() -> Result<()> {
     for diagnostic in &diagnostics {
         println!("{}", diagnostic);
     }
-    assert!(diagnostics.is_empty());
+    let error_diagnostics = diagnostics
+        .iter()
+        .filter(|diag| diag.data.is_error())
+        .collect::<Vec<_>>();
+    assert!(error_diagnostics.is_empty());
 
     let operations = compiler.db.operations(query_id);
     let get_product_op = operations
diff --git a/crates/apollo-compiler/src/diagnostics.rs b/crates/apollo-compiler/src/diagnostics.rs
index 945215b..bc3c737 100644
--- a/crates/apollo-compiler/src/diagnostics.rs
+++ b/crates/apollo-compiler/src/diagnostics.rs
@@ -263,11 +263,11 @@ pub enum DiagnosticData {
         // field type
         ty: &'static str,
     },
-    #[error("`${name}` variable must be of an input type")]
+    #[error("`{name}` field must be of an input type")]
     InputType {
-        // variable name
+        /// Field name.
         name: String,
-        // variable type
+        /// The kind of type that the field is declared with.
         ty: &'static str,
     },
     #[error(
@@ -278,6 +278,13 @@ pub enum DiagnosticData {
     QueryRootOperationType,
     #[error("built-in scalars must be omitted for brevity")]
     BuiltInScalarDefinition,
+    #[error("`${name}` variable must be of an input type")]
+    VariableInputType {
+        /// Varialbe name.
+        name: String,
+        /// The kind of type that the variable is declared with.
+        ty: &'static str,
+    },
     #[error("unused variable: `{name}`")]
     UnusedVariable { name: String },
     #[error("`{name}` field must return an object type")]
diff --git a/crates/apollo-compiler/src/lib.rs b/crates/apollo-compiler/src/lib.rs
index 0cc8a00..71900ed 100644
--- a/crates/apollo-compiler/src/lib.rs
+++ b/crates/apollo-compiler/src/lib.rs
@@ -522,7 +522,11 @@ type Product {
   weight: Int
 }
 
-directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet) on FIELD_DEFINITION
+directive @join__field(graph: join__Graph) on FIELD_DEFINITION
+enum join__Graph {
+  INVENTORY
+  PRODUCTS
+}
 "#;
 
         let mut compiler = ApolloCompiler::new();
@@ -701,6 +705,11 @@ schema {
   query: customPetQuery,
 }
 
+enum PetType {
+  CAT,
+  DOG,
+}
+
 type customPetQuery {
   name: String,
   age: Int
diff --git a/crates/apollo-compiler/src/validation/field.rs b/crates/apollo-compiler/src/validation/field.rs
index ae677f1..97d4923 100644
--- a/crates/apollo-compiler/src/validation/field.rs
+++ b/crates/apollo-compiler/src/validation/field.rs
@@ -156,10 +156,47 @@ pub fn validate_field_definition(
     );
 
     diagnostics.extend(db.validate_arguments_definition(
-        field.arguments,
+        field.arguments.clone(),
         hir::DirectiveLocation::ArgumentDefinition,
     ));
 
+    // Field types in Object Types must be of output type
+    let loc = field.loc().expect("undefined field definition location");
+    if let Some(field_ty) = field.ty().type_def(db.upcast()) {
+        if !field.ty().is_output_type(db.upcast()) {
+            diagnostics.push(
+                    ApolloDiagnostic::new(db, loc.into(), DiagnosticData::OutputType {
+                        name: field.name().into(),
+                        ty: field_ty.kind(),
+                    })
+                        .label(Label::new(loc, format!("this is of `{}` type", field_ty.kind())))
+                        .help(format!("Scalars, Objects, Interfaces, Unions and Enums are output types. Change `{}` field to return one of these output types.", field.name())),
+                );
+        }
+    } else if let Some(field_ty_loc) = field.ty().loc() {
+        diagnostics.push(
+            ApolloDiagnostic::new(
+                db,
+                field_ty_loc.into(),
+                DiagnosticData::UndefinedDefinition {
+                    name: field.name().into(),
+                },
+            )
+            .label(Label::new(field_ty_loc, "not found in this scope")),
+        );
+    } else {
+        diagnostics.push(
+            ApolloDiagnostic::new(
+                db,
+                loc.into(),
+                DiagnosticData::UndefinedDefinition {
+                    name: field.ty().name(),
+                },
+            )
+            .label(Label::new(loc, "not found in this scope")),
+        );
+    }
+
     diagnostics
 }
 
@@ -209,43 +246,6 @@ pub fn validate_field_definitions(
         } else {
             seen.insert(fname, field);
         }
-
-        // Field types in Object Types must be of output type
-        let loc = field.loc().expect("undefined field definition location");
-        if let Some(field_ty) = field.ty().type_def(db.upcast()) {
-            if !field.ty().is_output_type(db.upcast()) {
-                diagnostics.push(
-                    ApolloDiagnostic::new(db, loc.into(), DiagnosticData::OutputType {
-                        name: field.name().into(),
-                        ty: field_ty.kind(),
-                    })
-                        .label(Label::new(loc, format!("this is of `{}` type", field_ty.kind())))
-                        .help(format!("Scalars, Objects, Interfaces, Unions and Enums are output types. Change `{}` field to return one of these output types.", field.name())),
-                );
-            }
-        } else if let Some(field_ty_loc) = field.ty().loc() {
-            diagnostics.push(
-                ApolloDiagnostic::new(
-                    db,
-                    field_ty_loc.into(),
-                    DiagnosticData::UndefinedDefinition {
-                        name: field.name().into(),
-                    },
-                )
-                .label(Label::new(field_ty_loc, "not found in this scope")),
-            );
-        } else {
-            diagnostics.push(
-                ApolloDiagnostic::new(
-                    db,
-                    loc.into(),
-                    DiagnosticData::UndefinedDefinition {
-                        name: field.ty().name(),
-                    },
-                )
-                .label(Label::new(loc, "not found in this scope")),
-            );
-        }
     }
 
     diagnostics
diff --git a/crates/apollo-compiler/src/validation/input_object.rs b/crates/apollo-compiler/src/validation/input_object.rs
index 748edbb..d440dc5 100644
--- a/crates/apollo-compiler/src/validation/input_object.rs
+++ b/crates/apollo-compiler/src/validation/input_object.rs
@@ -183,6 +183,45 @@ pub fn validate_input_values(
         } else {
             seen.insert(name, input_value);
         }
+
+        // Input values must only contain input types.
+        let loc = input_value
+            .loc()
+            .expect("undefined input value definition location");
+        if let Some(field_ty) = input_value.ty().type_def(db.upcast()) {
+            if !input_value.ty().is_input_type(db.upcast()) {
+                diagnostics.push(
+                    ApolloDiagnostic::new(db, loc.into(), DiagnosticData::InputType {
+                        name: input_value.name().into(),
+                        ty: field_ty.kind(),
+                    })
+                        .label(Label::new(loc, format!("this is of `{}` type", field_ty.kind())))
+                        .help(format!("Scalars, Enums, and Input Objects are input types. Change `{}` field to take one of these input types.", input_value.name())),
+                );
+            }
+        } else if let Some(field_ty_loc) = input_value.ty().loc() {
+            diagnostics.push(
+                ApolloDiagnostic::new(
+                    db,
+                    field_ty_loc.into(),
+                    DiagnosticData::UndefinedDefinition {
+                        name: input_value.name().into(),
+                    },
+                )
+                .label(Label::new(field_ty_loc, "not found in this scope")),
+            );
+        } else {
+            diagnostics.push(
+                ApolloDiagnostic::new(
+                    db,
+                    loc.into(),
+                    DiagnosticData::UndefinedDefinition {
+                        name: input_value.ty().name(),
+                    },
+                )
+                .label(Label::new(loc, "not found in this scope")),
+            );
+        }
     }
 
     diagnostics
diff --git a/crates/apollo-compiler/src/validation/variable.rs b/crates/apollo-compiler/src/validation/variable.rs
index 8d48d6f..0138296 100644
--- a/crates/apollo-compiler/src/validation/variable.rs
+++ b/crates/apollo-compiler/src/validation/variable.rs
@@ -34,7 +34,7 @@ pub fn validate_variable_definitions(
                 if let Some(type_def) = type_def {
                     let ty_name = type_def.kind();
                     diagnostics.push(
-                    ApolloDiagnostic::new(db, variable.loc().into(), DiagnosticData::InputType {
+                    ApolloDiagnostic::new(db, variable.loc().into(), DiagnosticData::VariableInputType {
                         name: variable.name().into(),
                         ty: ty_name,
                     })


Test patch:
diff --git a/crates/apollo-compiler/test_data/diagnostics/0002_multiple_anonymous_operations.txt b/crates/apollo-compiler/test_data/diagnostics/0002_multiple_anonymous_operations.txt
index ffcfb04..9ee82ed 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0002_multiple_anonymous_operations.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0002_multiple_anonymous_operations.txt
@@ -1,4 +1,33 @@
 [
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            2: "0002_multiple_anonymous_operations.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 2,
+            },
+            offset: 156,
+            length: 7,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 2,
+                    },
+                    offset: 156,
+                    length: 7,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "petType",
+        },
+    },
     ApolloDiagnostic {
         cache: {
             0: "built_in_types.graphql",
diff --git a/crates/apollo-compiler/test_data/diagnostics/0003_anonymous_and_named_operation.txt b/crates/apollo-compiler/test_data/diagnostics/0003_anonymous_and_named_operation.txt
index 77ddffd..b43a432 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0003_anonymous_and_named_operation.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0003_anonymous_and_named_operation.txt
@@ -1,4 +1,33 @@
 [
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            3: "0003_anonymous_and_named_operation.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 3,
+            },
+            offset: 147,
+            length: 7,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 3,
+                    },
+                    offset: 147,
+                    length: 7,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "petType",
+        },
+    },
     ApolloDiagnostic {
         cache: {
             0: "built_in_types.graphql",
diff --git a/crates/apollo-compiler/test_data/diagnostics/0040_operation_with_undefined_fields_on_reference_type.txt b/crates/apollo-compiler/test_data/diagnostics/0040_operation_with_undefined_fields_on_reference_type.txt
index 2b3d934..923ce3e 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0040_operation_with_undefined_fields_on_reference_type.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0040_operation_with_undefined_fields_on_reference_type.txt
@@ -1,4 +1,91 @@
 [
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            40: "0040_operation_with_undefined_fields_on_reference_type.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 40,
+            },
+            offset: 235,
+            length: 11,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 40,
+                    },
+                    offset: 235,
+                    length: 11,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "graph",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            40: "0040_operation_with_undefined_fields_on_reference_type.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 40,
+            },
+            offset: 258,
+            length: 14,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 40,
+                    },
+                    offset: 258,
+                    length: 14,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "requires",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            40: "0040_operation_with_undefined_fields_on_reference_type.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 40,
+            },
+            offset: 284,
+            length: 14,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 40,
+                    },
+                    offset: 284,
+                    length: 14,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "provides",
+        },
+    },
     ApolloDiagnostic {
         cache: {
             0: "built_in_types.graphql",
diff --git a/crates/apollo-compiler/test_data/diagnostics/0042_mutation_operation_with_undefined_fields.txt b/crates/apollo-compiler/test_data/diagnostics/0042_mutation_operation_with_undefined_fields.txt
index 3f77f1f..266e05b 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0042_mutation_operation_with_undefined_fields.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0042_mutation_operation_with_undefined_fields.txt
@@ -1,4 +1,36 @@
 [
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            42: "0042_mutation_operation_with_undefined_fields.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 42,
+            },
+            offset: 141,
+            length: 16,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 42,
+                    },
+                    offset: 141,
+                    length: 16,
+                },
+                text: "this is of `UnionTypeDefinition` type",
+            },
+        ],
+        help: Some(
+            "Scalars, Enums, and Input Objects are input types. Change `petType` field to take one of these input types.",
+        ),
+        data: InputType {
+            name: "petType",
+            ty: "UnionTypeDefinition",
+        },
+    },
     ApolloDiagnostic {
         cache: {
             0: "built_in_types.graphql",
diff --git a/crates/apollo-compiler/test_data/diagnostics/0056_variables_are_input_types.txt b/crates/apollo-compiler/test_data/diagnostics/0056_variables_are_input_types.txt
index c975aed..dc6b362 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0056_variables_are_input_types.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0056_variables_are_input_types.txt
@@ -26,7 +26,7 @@
         help: Some(
             "objects, unions, and interfaces cannot be used because variables can only be of input type",
         ),
-        data: InputType {
+        data: VariableInputType {
             name: "cat",
             ty: "ObjectTypeDefinition",
         },
@@ -98,7 +98,7 @@
         help: Some(
             "objects, unions, and interfaces cannot be used because variables can only be of input type",
         ),
-        data: InputType {
+        data: VariableInputType {
             name: "dog",
             ty: "ObjectTypeDefinition",
         },
@@ -170,7 +170,7 @@
         help: Some(
             "objects, unions, and interfaces cannot be used because variables can only be of input type",
         ),
-        data: InputType {
+        data: VariableInputType {
             name: "pets",
             ty: "InterfaceTypeDefinition",
         },
@@ -242,7 +242,7 @@
         help: Some(
             "objects, unions, and interfaces cannot be used because variables can only be of input type",
         ),
-        data: InputType {
+        data: VariableInputType {
             name: "catOrDog",
             ty: "UnionTypeDefinition",
         },
diff --git a/crates/apollo-compiler/test_data/diagnostics/0083_type_introspection_from_disallowed_type.txt b/crates/apollo-compiler/test_data/diagnostics/0083_type_introspection_from_disallowed_type.txt
index 5c7b96c..4e48382 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0083_type_introspection_from_disallowed_type.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0083_type_introspection_from_disallowed_type.txt
@@ -1,4 +1,91 @@
 [
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            80: "0083_type_introspection_from_disallowed_type.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 80,
+            },
+            offset: 473,
+            length: 11,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 80,
+                    },
+                    offset: 473,
+                    length: 11,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "graph",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            80: "0083_type_introspection_from_disallowed_type.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 80,
+            },
+            offset: 496,
+            length: 14,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 80,
+                    },
+                    offset: 496,
+                    length: 14,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "requires",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            80: "0083_type_introspection_from_disallowed_type.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 80,
+            },
+            offset: 522,
+            length: 14,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 80,
+                    },
+                    offset: 522,
+                    length: 14,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "provides",
+        },
+    },
     ApolloDiagnostic {
         cache: {
             0: "built_in_types.graphql",
diff --git a/crates/apollo-compiler/test_data/diagnostics/0103_invalid_type_in_arg.graphql b/crates/apollo-compiler/test_data/diagnostics/0103_invalid_type_in_arg.graphql
new file mode 100644
index 0000000..845df04
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0103_invalid_type_in_arg.graphql
@@ -0,0 +1,20 @@
+scalar AnInputType @specifiedBy(url: "http://example.com")
+type OutputType {
+  not: AnInputType
+}
+
+input InputObjectWithOutputType {
+  thisIsWrong: OutputType
+}
+
+type Root {
+  id: ID!
+  undefinedTypes(a: number, b: number): OperationResult!
+  outputTypes(a: OutputType, b: OperationResult): ID!
+}
+
+union OperationResult = Operation
+
+type Operation {
+  id: ID!
+}
diff --git a/crates/apollo-compiler/test_data/diagnostics/0103_invalid_type_in_arg.txt b/crates/apollo-compiler/test_data/diagnostics/0103_invalid_type_in_arg.txt
new file mode 100644
index 0000000..8560f4b
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0103_invalid_type_in_arg.txt
@@ -0,0 +1,156 @@
+[
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            100: "0103_invalid_type_in_arg.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 100,
+            },
+            offset: 135,
+            length: 23,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 100,
+                    },
+                    offset: 135,
+                    length: 23,
+                },
+                text: "this is of `ObjectTypeDefinition` type",
+            },
+        ],
+        help: Some(
+            "Scalars, Enums, and Input Objects are input types. Change `thisIsWrong` field to take one of these input types.",
+        ),
+        data: InputType {
+            name: "thisIsWrong",
+            ty: "ObjectTypeDefinition",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            100: "0103_invalid_type_in_arg.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 100,
+            },
+            offset: 204,
+            length: 6,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 100,
+                    },
+                    offset: 204,
+                    length: 6,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "a",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            100: "0103_invalid_type_in_arg.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 100,
+            },
+            offset: 215,
+            length: 6,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 100,
+                    },
+                    offset: 215,
+                    length: 6,
+                },
+                text: "not found in this scope",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "b",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            100: "0103_invalid_type_in_arg.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 100,
+            },
+            offset: 255,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 100,
+                    },
+                    offset: 255,
+                    length: 13,
+                },
+                text: "this is of `ObjectTypeDefinition` type",
+            },
+        ],
+        help: Some(
+            "Scalars, Enums, and Input Objects are input types. Change `a` field to take one of these input types.",
+        ),
+        data: InputType {
+            name: "a",
+            ty: "ObjectTypeDefinition",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            0: "built_in_types.graphql",
+            100: "0103_invalid_type_in_arg.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 100,
+            },
+            offset: 270,
+            length: 18,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 100,
+                    },
+                    offset: 270,
+                    length: 18,
+                },
+                text: "this is of `UnionTypeDefinition` type",
+            },
+        ],
+        help: Some(
+            "Scalars, Enums, and Input Objects are input types. Change `b` field to take one of these input types.",
+        ),
+        data: InputType {
+            name: "b",
+            ty: "UnionTypeDefinition",
+        },
+    },
+]
diff --git a/crates/apollo-compiler/test_data/ok/0003_schema_definition_with_custom_operation_types.graphql b/crates/apollo-compiler/test_data/ok/0003_schema_definition_with_custom_operation_types.graphql
index 125870b..e7f4702 100644
--- a/crates/apollo-compiler/test_data/ok/0003_schema_definition_with_custom_operation_types.graphql
+++ b/crates/apollo-compiler/test_data/ok/0003_schema_definition_with_custom_operation_types.graphql
@@ -4,6 +4,11 @@ schema {
   mutation: customPetMutation
 }
 
+enum PetType {
+  CAT,
+  DOG,
+}
+
 type customPetQuery {
   name: String,
   age: Int
@@ -19,4 +24,4 @@ type customPetMutation {
 
 type Result {
   id: String
-}
\ No newline at end of file
+}
diff --git a/crates/apollo-compiler/test_data/ok/0003_schema_definition_with_custom_operation_types.txt b/crates/apollo-compiler/test_data/ok/0003_schema_definition_with_custom_operation_types.txt
index 55c5f4f..0ca2be7 100644
--- a/crates/apollo-compiler/test_data/ok/0003_schema_definition_with_custom_operation_types.txt
+++ b/crates/apollo-compiler/test_data/ok/0003_schema_definition_with_custom_operation_types.txt
@@ -1,4 +1,4 @@
-- DOCUMENT@0..328
+- DOCUMENT@0..361
     - SCHEMA_DEFINITION@0..103
         - schema_KW@0..6 "schema"
         - WHITESPACE@6..7 " "
@@ -34,119 +34,143 @@
         - WHITESPACE@101..102 "\n"
         - R_CURLY@102..103 "}"
     - WHITESPACE@103..105 "\n\n"
-    - OBJECT_TYPE_DEFINITION@105..155
-        - type_KW@105..109 "type"
+    - ENUM_TYPE_DEFINITION@105..135
+        - enum_KW@105..109 "enum"
         - WHITESPACE@109..110 " "
-        - NAME@110..124
-            - IDENT@110..124 "customPetQuery"
-        - WHITESPACE@124..125 " "
-        - FIELDS_DEFINITION@125..155
-            - L_CURLY@125..126 "{"
+        - NAME@110..117
+            - IDENT@110..117 "PetType"
+        - WHITESPACE@117..118 " "
+        - ENUM_VALUES_DEFINITION@118..135
+            - L_CURLY@118..119 "{"
+            - WHITESPACE@119..122 "\n  "
+            - ENUM_VALUE_DEFINITION@122..125
+                - ENUM_VALUE@122..125
+                    - NAME@122..125
+                        - IDENT@122..125 "CAT"
+            - COMMA@125..126 ","
             - WHITESPACE@126..129 "\n  "
-            - FIELD_DEFINITION@129..141
-                - NAME@129..133
-                    - IDENT@129..133 "name"
-                - COLON@133..134 ":"
-                - WHITESPACE@134..135 " "
-                - NAMED_TYPE@135..141
-                    - NAME@135..141
-                        - IDENT@135..141 "String"
-            - COMMA@141..142 ","
-            - WHITESPACE@142..145 "\n  "
-            - FIELD_DEFINITION@145..153
-                - NAME@145..148
-                    - IDENT@145..148 "age"
-                - COLON@148..149 ":"
-                - WHITESPACE@149..150 " "
-                - NAMED_TYPE@150..153
-                    - NAME@150..153
-                        - IDENT@150..153 "Int"
-            - WHITESPACE@153..154 "\n"
-            - R_CURLY@154..155 "}"
-    - WHITESPACE@155..157 "\n\n"
-    - OBJECT_TYPE_DEFINITION@157..218
-        - type_KW@157..161 "type"
-        - WHITESPACE@161..162 " "
-        - NAME@162..183
-            - IDENT@162..183 "customPetSubscription"
-        - WHITESPACE@183..184 " "
-        - FIELDS_DEFINITION@184..218
-            - L_CURLY@184..185 "{"
-            - WHITESPACE@185..188 "\n  "
-            - FIELD_DEFINITION@188..216
-                - NAME@188..208
-                    - IDENT@188..208 "changeInPetHousehold"
-                - COLON@208..209 ":"
-                - WHITESPACE@209..210 " "
-                - NAMED_TYPE@210..216
-                    - NAME@210..216
-                        - IDENT@210..216 "Result"
-            - WHITESPACE@216..217 "\n"
-            - R_CURLY@217..218 "}"
-    - WHITESPACE@218..220 "\n\n"
-    - OBJECT_TYPE_DEFINITION@220..298
-        - type_KW@220..224 "type"
-        - WHITESPACE@224..225 " "
-        - NAME@225..242
-            - IDENT@225..242 "customPetMutation"
-        - WHITESPACE@242..243 " "
-        - FIELDS_DEFINITION@243..298
-            - L_CURLY@243..244 "{"
-            - WHITESPACE@244..247 "\n  "
-            - FIELD_DEFINITION@247..296
-                - NAME@247..253
-                    - IDENT@247..253 "addPet"
-                - WHITESPACE@253..254 " "
-                - ARGUMENTS_DEFINITION@254..287
-                    - L_PAREN@254..255 "("
-                    - INPUT_VALUE_DEFINITION@255..268
-                        - NAME@255..259
-                            - IDENT@255..259 "name"
-                        - COLON@259..260 ":"
-                        - WHITESPACE@260..261 " "
-                        - NON_NULL_TYPE@261..268
-                            - NAMED_TYPE@261..267
-                                - NAME@261..267
-                                    - IDENT@261..267 "String"
-                            - BANG@267..268 "!"
-                    - COMMA@268..269 ","
-                    - WHITESPACE@269..270 " "
-                    - INPUT_VALUE_DEFINITION@270..286
-                        - NAME@270..277
-                            - IDENT@270..277 "petType"
-                        - COLON@277..278 ":"
-                        - WHITESPACE@278..279 " "
-                        - NAMED_TYPE@279..286
-                            - NAME@279..286
-                                - IDENT@279..286 "PetType"
-                    - R_PAREN@286..287 ")"
-                - COLON@287..288 ":"
-                - WHITESPACE@288..289 " "
-                - NON_NULL_TYPE@289..296
-                    - NAMED_TYPE@289..295
-                        - NAME@289..295
-                            - IDENT@289..295 "Result"
-                    - BANG@295..296 "!"
-            - WHITESPACE@296..297 "\n"
-            - R_CURLY@297..298 "}"
-    - WHITESPACE@298..300 "\n\n"
-    - OBJECT_TYPE_DEFINITION@300..328
-        - type_KW@300..304 "type"
-        - WHITESPACE@304..305 " "
-        - NAME@305..311
-            - IDENT@305..311 "Result"
-        - WHITESPACE@311..312 " "
-        - FIELDS_DEFINITION@312..328
-            - L_CURLY@312..313 "{"
-            - WHITESPACE@313..316 "\n  "
-            - FIELD_DEFINITION@316..326
-                - NAME@316..318
-                    - IDENT@316..318 "id"
-                - COLON@318..319 ":"
-                - WHITESPACE@319..320 " "
-                - NAMED_TYPE@320..326
-                    - NAME@320..326
-                        - IDENT@320..326 "String"
-            - WHITESPACE@326..327 "\n"
-            - R_CURLY@327..328 "}"
+            - ENUM_VALUE_DEFINITION@129..132
+                - ENUM_VALUE@129..132
+                    - NAME@129..132
+                        - IDENT@129..132 "DOG"
+            - COMMA@132..133 ","
+            - WHITESPACE@133..134 "\n"
+            - R_CURLY@134..135 "}"
+    - WHITESPACE@135..137 "\n\n"
+    - OBJECT_TYPE_DEFINITION@137..187
+        - type_KW@137..141 "type"
+        - WHITESPACE@141..142 " "
+        - NAME@142..156
+            - IDENT@142..156 "customPetQuery"
+        - WHITESPACE@156..157 " "
+        - FIELDS_DEFINITION@157..187
+            - L_CURLY@157..158 "{"
+            - WHITESPACE@158..161 "\n  "
+            - FIELD_DEFINITION@161..173
+                - NAME@161..165
+                    - IDENT@161..165 "name"
+                - COLON@165..166 ":"
+                - WHITESPACE@166..167 " "
+                - NAMED_TYPE@167..173
+                    - NAME@167..173
+                        - IDENT@167..173 "String"
+            - COMMA@173..174 ","
+            - WHITESPACE@174..177 "\n  "
+            - FIELD_DEFINITION@177..185
+                - NAME@177..180
+                    - IDENT@177..180 "age"
+                - COLON@180..181 ":"
+                - WHITESPACE@181..182 " "
+                - NAMED_TYPE@182..185
+                    - NAME@182..185
+                        - IDENT@182..185 "Int"
+            - WHITESPACE@185..186 "\n"
+            - R_CURLY@186..187 "}"
+    - WHITESPACE@187..189 "\n\n"
+    - OBJECT_TYPE_DEFINITION@189..250
+        - type_KW@189..193 "type"
+        - WHITESPACE@193..194 " "
+        - NAME@194..215
+            - IDENT@194..215 "customPetSubscription"
+        - WHITESPACE@215..216 " "
+        - FIELDS_DEFINITION@216..250
+            - L_CURLY@216..217 "{"
+            - WHITESPACE@217..220 "\n  "
+            - FIELD_DEFINITION@220..248
+                - NAME@220..240
+                    - IDENT@220..240 "changeInPetHousehold"
+                - COLON@240..241 ":"
+                - WHITESPACE@241..242 " "
+                - NAMED_TYPE@242..248
+                    - NAME@242..248
+                        - IDENT@242..248 "Result"
+            - WHITESPACE@248..249 "\n"
+            - R_CURLY@249..250 "}"
+    - WHITESPACE@250..252 "\n\n"
+    - OBJECT_TYPE_DEFINITION@252..330
+        - type_KW@252..256 "type"
+        - WHITESPACE@256..257 " "
+        - NAME@257..274
+            - IDENT@257..274 "customPetMutation"
+        - WHITESPACE@274..275 " "
+        - FIELDS_DEFINITION@275..330
+            - L_CURLY@275..276 "{"
+            - WHITESPACE@276..279 "\n  "
+            - FIELD_DEFINITION@279..328
+                - NAME@279..285
+                    - IDENT@279..285 "addPet"
+                - WHITESPACE@285..286 " "
+                - ARGUMENTS_DEFINITION@286..319
+                    - L_PAREN@286..287 "("
+                    - INPUT_VALUE_DEFINITION@287..300
+                        - NAME@287..291
+                            - IDENT@287..291 "name"
+                        - COLON@291..292 ":"
+                        - WHITESPACE@292..293 " "
+                        - NON_NULL_TYPE@293..300
+                            - NAMED_TYPE@293..299
+                                - NAME@293..299
+                                    - IDENT@293..299 "String"
+                            - BANG@299..300 "!"
+                    - COMMA@300..301 ","
+                    - WHITESPACE@301..302 " "
+                    - INPUT_VALUE_DEFINITION@302..318
+                        - NAME@302..309
+                            - IDENT@302..309 "petType"
+                        - COLON@309..310 ":"
+                        - WHITESPACE@310..311 " "
+                        - NAMED_TYPE@311..318
+                            - NAME@311..318
+                                - IDENT@311..318 "PetType"
+                    - R_PAREN@318..319 ")"
+                - COLON@319..320 ":"
+                - WHITESPACE@320..321 " "
+                - NON_NULL_TYPE@321..328
+                    - NAMED_TYPE@321..327
+                        - NAME@321..327
+                            - IDENT@321..327 "Result"
+                    - BANG@327..328 "!"
+            - WHITESPACE@328..329 "\n"
+            - R_CURLY@329..330 "}"
+    - WHITESPACE@330..332 "\n\n"
+    - OBJECT_TYPE_DEFINITION@332..360
+        - type_KW@332..336 "type"
+        - WHITESPACE@336..337 " "
+        - NAME@337..343
+            - IDENT@337..343 "Result"
+        - WHITESPACE@343..344 " "
+        - FIELDS_DEFINITION@344..360
+            - L_CURLY@344..345 "{"
+            - WHITESPACE@345..348 "\n  "
+            - FIELD_DEFINITION@348..358
+                - NAME@348..350
+                    - IDENT@348..350 "id"
+                - COLON@350..351 ":"
+                - WHITESPACE@351..352 " "
+                - NAMED_TYPE@352..358
+                    - NAME@352..358
+                        - IDENT@352..358 "String"
+            - WHITESPACE@358..359 "\n"
+            - R_CURLY@359..360 "}"
+    - WHITESPACE@360..361 "\n"
 recursion limit: 4096, high: 0
diff --git a/crates/apollo-compiler/test_data/ok/0010_operation_with_defined_fields.graphql b/crates/apollo-compiler/test_data/ok/0010_operation_with_defined_fields.graphql
index 1381ec6..9ef0395 100644
--- a/crates/apollo-compiler/test_data/ok/0010_operation_with_defined_fields.graphql
+++ b/crates/apollo-compiler/test_data/ok/0010_operation_with_defined_fields.graphql
@@ -21,4 +21,8 @@ type Product {
   weight: Int
 }
 
-directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet) on FIELD_DEFINITION
\ No newline at end of file
+directive @join__field(graph: join__Graph) on FIELD_DEFINITION
+enum join__Graph {
+  INVENTORY,
+  PRODUCTS,
+}
diff --git a/crates/apollo-compiler/test_data/ok/0010_operation_with_defined_fields.txt b/crates/apollo-compiler/test_data/ok/0010_operation_with_defined_fields.txt
index 76a1c34..1a00614 100644
--- a/crates/apollo-compiler/test_data/ok/0010_operation_with_defined_fields.txt
+++ b/crates/apollo-compiler/test_data/ok/0010_operation_with_defined_fields.txt
@@ -1,4 +1,4 @@
-- DOCUMENT@0..429
+- DOCUMENT@0..424
     - OPERATION_DEFINITION@0..68
         - OPERATION_TYPE@0..5
             - query_KW@0..5 "query"
@@ -171,13 +171,13 @@
             - WHITESPACE@311..312 "\n"
             - R_CURLY@312..313 "}"
     - WHITESPACE@313..315 "\n\n"
-    - DIRECTIVE_DEFINITION@315..429
+    - DIRECTIVE_DEFINITION@315..377
         - directive_KW@315..324 "directive"
         - WHITESPACE@324..325 " "
         - AT@325..326 "@"
         - NAME@326..337
             - IDENT@326..337 "join__field"
-        - ARGUMENTS_DEFINITION@337..409
+        - ARGUMENTS_DEFINITION@337..357
             - L_PAREN@337..338 "("
             - INPUT_VALUE_DEFINITION@338..356
                 - NAME@338..343
@@ -187,31 +187,35 @@
                 - NAMED_TYPE@345..356
                     - NAME@345..356
                         - IDENT@345..356 "join__Graph"
-            - COMMA@356..357 ","
-            - WHITESPACE@357..358 " "
-            - INPUT_VALUE_DEFINITION@358..382
-                - NAME@358..366
-                    - IDENT@358..366 "requires"
-                - COLON@366..367 ":"
-                - WHITESPACE@367..368 " "
-                - NAMED_TYPE@368..382
-                    - NAME@368..382
-                        - IDENT@368..382 "join__FieldSet"
-            - COMMA@382..383 ","
-            - WHITESPACE@383..384 " "
-            - INPUT_VALUE_DEFINITION@384..408
-                - NAME@384..392
-                    - IDENT@384..392 "provides"
-                - COLON@392..393 ":"
-                - WHITESPACE@393..394 " "
-                - NAMED_TYPE@394..408
-                    - NAME@394..408
-                        - IDENT@394..408 "join__FieldSet"
-            - R_PAREN@408..409 ")"
-        - WHITESPACE@409..410 " "
-        - on_KW@410..412 "on"
-        - WHITESPACE@412..413 " "
-        - DIRECTIVE_LOCATIONS@413..429
-            - DIRECTIVE_LOCATION@413..429
-                - FIELD_DEFINITION_KW@413..429 "FIELD_DEFINITION"
+            - R_PAREN@356..357 ")"
+        - WHITESPACE@357..358 " "
+        - on_KW@358..360 "on"
+        - WHITESPACE@360..361 " "
+        - DIRECTIVE_LOCATIONS@361..377
+            - DIRECTIVE_LOCATION@361..377
+                - FIELD_DEFINITION_KW@361..377 "FIELD_DEFINITION"
+    - WHITESPACE@377..378 "\n"
+    - ENUM_TYPE_DEFINITION@378..423
+        - enum_KW@378..382 "enum"
+        - WHITESPACE@382..383 " "
+        - NAME@383..394
+            - IDENT@383..394 "join__Graph"
+        - WHITESPACE@394..395 " "
+        - ENUM_VALUES_DEFINITION@395..423
+            - L_CURLY@395..396 "{"
+            - WHITESPACE@396..399 "\n  "
+            - ENUM_VALUE_DEFINITION@399..408
+                - ENUM_VALUE@399..408
+                    - NAME@399..408
+                        - IDENT@399..408 "INVENTORY"
+            - COMMA@408..409 ","
+            - WHITESPACE@409..412 "\n  "
+            - ENUM_VALUE_DEFINITION@412..420
+                - ENUM_VALUE@412..420
+                    - NAME@412..420
+                        - IDENT@412..420 "PRODUCTS"
+            - COMMA@420..421 ","
+            - WHITESPACE@421..422 "\n"
+            - R_CURLY@422..423 "}"
+    - WHITESPACE@423..424 "\n"
 recursion limit: 4096, high: 2