Repository: apollographql/apollo-rs
Pull request #455 (2023-02-09 18:45:35)

feat(compiler): validate unique variables, validate variables are of input type
partial of #312, will validate variables match their defined type system type in a separate PR, as the work there is a bit more extensive.


Patch:
diff --git a/crates/apollo-compiler/README.md b/crates/apollo-compiler/README.md
index 4ce8dde..8439d65 100644
--- a/crates/apollo-compiler/README.md
+++ b/crates/apollo-compiler/README.md
@@ -142,7 +142,7 @@ fn main() -> Result<()> {
     }
     assert!(diagnostics.is_empty());
 
-    let op = compiler.db.find_operation_by_name(query_id, String::from("getProduct"))
+    let op = compiler.db.find_operation(query_id, Some("getProduct".into()))
         .expect("getProduct query does not exist");
     let fragment_in_op: Vec<hir::FragmentDefinition> = op.selection_set().selection().iter().filter_map(|sel| match sel {
         hir::Selection::FragmentSpread(frag) => {
diff --git a/crates/apollo-compiler/src/database/document.rs b/crates/apollo-compiler/src/database/document.rs
index 91c3fd7..b81f087 100644
--- a/crates/apollo-compiler/src/database/document.rs
+++ b/crates/apollo-compiler/src/database/document.rs
@@ -38,25 +38,20 @@ pub(crate) fn find_type_definition_by_name(
     db.types_definitions_by_name().get(&name).cloned()
 }
 
-pub(crate) fn find_operation_by_name(
+pub(crate) fn find_operation(
     db: &dyn HirDatabase,
     file_id: FileId,
-    name: String,
+    name: Option<String>,
 ) -> Option<Arc<OperationDefinition>> {
-    db.operations(file_id)
-        .iter()
-        .find(|def| def.name() == Some(&*name))
-        .cloned()
-}
-
-pub(crate) fn find_anonymous_operation(
-    db: &dyn HirDatabase,
-    file_id: FileId,
-) -> Option<Arc<OperationDefinition>> {
-    db.operations(file_id)
-        .iter()
-        .find(|def| def.name().is_none())
-        .cloned()
+    let ops = db.operations(file_id);
+
+    if let Some(name) = name {
+        ops.iter().find(|def| def.name() == Some(&*name)).cloned()
+    } else if ops.len() == 1 {
+        Some(ops[0].clone())
+    } else {
+        None
+    }
 }
 
 pub(crate) fn find_fragment_by_name(
@@ -323,6 +318,44 @@ mod tests {
     use crate::ApolloCompiler;
     use crate::HirDatabase;
 
+    #[test]
+    fn find_operations() {
+        let type_system = r#"
+type Query {
+  name: String
+}
+        "#;
+        let op = r#"{ name }"#;
+        let named_op = r#"query getName { name } "#;
+        let several_named_op = r#"query getName { name } query getAnotherName { name }"#;
+        let noop = r#""#;
+
+        let mut compiler = ApolloCompiler::new();
+        compiler.add_type_system(type_system, "ts.graphql");
+        let op_id = compiler.add_executable(op, "op.graphql");
+        let op = compiler.db.find_operation(op_id, None);
+        assert!(op.is_some());
+
+        compiler.update_executable(op_id, named_op);
+        let op = compiler.db.find_operation(op_id, Some("getName".into()));
+        assert!(op.is_some());
+        let op = compiler.db.find_operation(op_id, None);
+        assert!(op.is_some());
+
+        compiler.update_executable(op_id, several_named_op);
+        let op = compiler.db.find_operation(op_id, Some("getName".into()));
+        assert!(op.is_some());
+        let op = compiler.db.find_operation(op_id, None);
+        assert!(op.is_none());
+
+        compiler.update_executable(op_id, noop);
+        let op = compiler.db.find_operation(op_id, Some("getName".into()));
+        assert!(op.is_none());
+
+        let op = compiler.db.find_operation(op_id, None);
+        assert!(op.is_none());
+    }
+
     #[test]
     fn find_definitions_with_directive() {
         let schema = r#"
diff --git a/crates/apollo-compiler/src/database/hir.rs b/crates/apollo-compiler/src/database/hir.rs
index 55a0be1..6018376 100644
--- a/crates/apollo-compiler/src/database/hir.rs
+++ b/crates/apollo-compiler/src/database/hir.rs
@@ -2183,7 +2183,7 @@ mod tests {
 
         let db = compiler.db;
         let type_introspect: Arc<OperationDefinition> = db
-            .find_operation_by_name(query_id, String::from("TypeIntrospect"))
+            .find_operation(query_id, Some(String::from("TypeIntrospect")))
             .expect("TypeIntrospect operation does not exist");
 
         assert!(type_introspect.is_introspection(&db));
@@ -2219,11 +2219,11 @@ mod tests {
 
         let db = compiler.db;
         let check_stock: Arc<OperationDefinition> = db
-            .find_operation_by_name(query_id, String::from("CheckStock"))
+            .find_operation(query_id, Some("CheckStock".into()))
             .expect("CheckStock operation does not exist");
 
         let purchase_operation: Arc<OperationDefinition> = db
-            .find_operation_by_name(mutation_id, String::from("PurchaseBasket"))
+            .find_operation(mutation_id, Some("PurchaseBasket".into()))
             .expect("CheckStock operation does not exist");
 
         assert!(!check_stock.is_introspection(&db));
@@ -2296,15 +2296,15 @@ mod tests {
 
         let db = compiler.db;
         let deep_introspect: Arc<OperationDefinition> = db
-            .find_operation_by_name(query_id, String::from("IntrospectDeepFragments"))
+            .find_operation(query_id, Some("IntrospectDeepFragments".into()))
             .expect("IntrospectDeepFragments operation does not exist");
 
         assert!(deep_introspect.is_introspection(&db));
 
         let deep_introspect: Arc<OperationDefinition> = db
-            .find_operation_by_name(
+            .find_operation(
                 query_id_not_introspect,
-                String::from("IntrospectDeepFragments"),
+                Some("IntrospectDeepFragments".into()),
             )
             .expect("IntrospectDeepFragments operation does not exist");
         assert!(!deep_introspect.is_introspection(&db));
diff --git a/crates/apollo-compiler/src/database/hir_db.rs b/crates/apollo-compiler/src/database/hir_db.rs
index 59536da..7b035ce 100644
--- a/crates/apollo-compiler/src/database/hir_db.rs
+++ b/crates/apollo-compiler/src/database/hir_db.rs
@@ -65,16 +65,18 @@ pub trait HirDatabase: InputDatabase + AstDatabase {
     fn input_objects(&self) -> ByName<InputObjectTypeDefinition>;
 
     // Derived from above queries:
+
     /// Return an operation definition corresponding to the name and file id.
-    fn find_operation_by_name(
+    /// If `name` is `None`, and there is only one operation, that operation will
+    /// be returned.
+    /// If `name` is `None`, and there is more than one operation, `None` will
+    /// be returned.
+    fn find_operation(
         &self,
         file_id: FileId,
-        name: String,
+        name: Option<String>,
     ) -> Option<Arc<OperationDefinition>>;
 
-    /// Return an operation definition without a name, corresponding to the file id.
-    fn find_anonymous_operation(&self, file_id: FileId) -> Option<Arc<OperationDefinition>>;
-
     /// Return an fragment definition corresponding to the name and file id.
     /// Result of this query is not cached internally.
     #[salsa::transparent]
diff --git a/crates/apollo-compiler/src/diagnostics.rs b/crates/apollo-compiler/src/diagnostics.rs
index d8805f8..95617aa 100644
--- a/crates/apollo-compiler/src/diagnostics.rs
+++ b/crates/apollo-compiler/src/diagnostics.rs
@@ -208,6 +208,13 @@ pub enum DiagnosticData {
         // field type
         ty: &'static str,
     },
+    #[error("`${name}` variable must be of an input type")]
+    InputType {
+        // variable name
+        name: String,
+        // variable type
+        ty: &'static str,
+    },
     #[error(
         "custom scalars should provide a scalar specification URL via the @specifiedBy directive"
     )]
@@ -246,10 +253,7 @@ impl DiagnosticData {
         !self.is_warning() && !self.is_advice()
     }
     pub fn is_warning(&self) -> bool {
-        matches!(
-            self,
-            Self::CapitalizedValue { .. } | Self::UnusedVariable { .. }
-        )
+        matches!(self, Self::CapitalizedValue { .. })
     }
     pub fn is_advice(&self) -> bool {
         matches!(self, Self::ScalarSpecificationURL)
diff --git a/crates/apollo-compiler/src/lib.rs b/crates/apollo-compiler/src/lib.rs
index 4d1a44d..abf6521 100644
--- a/crates/apollo-compiler/src/lib.rs
+++ b/crates/apollo-compiler/src/lib.rs
@@ -634,7 +634,7 @@ fragment vipCustomer on User {
 
         let op = compiler
             .db
-            .find_operation_by_name(query_id, String::from("getProduct"));
+            .find_operation(query_id, Some("getProduct".into()));
         let fragment_in_op: Vec<crate::hir::FragmentDefinition> = op
             .unwrap()
             .selection_set()
@@ -1229,7 +1229,7 @@ type Query {
                     compiler.set_type_system_hir(cloned);
                     compiler
                         .db
-                        .find_anonymous_operation(query_id)
+                        .find_operation(query_id, None)
                         .unwrap()
                         .fields(&compiler.db)[0]
                         .ty(&compiler.db)
diff --git a/crates/apollo-compiler/src/validation/enum_.rs b/crates/apollo-compiler/src/validation/enum_.rs
index f95ed45..aef71c0 100644
--- a/crates/apollo-compiler/src/validation/enum_.rs
+++ b/crates/apollo-compiler/src/validation/enum_.rs
@@ -30,7 +30,7 @@ pub fn validate_enum_definition(
 
         // An Enum type must define one or more unique enum values.
         //
-        // Return a Unique Value error in case of a duplicate value.
+        // Return a Unique Definition error in case of a duplicate value.
         let value = enum_val.enum_value();
         if let Some(prev_def) = seen.get(&value) {
             let original_definition = prev_def.loc();
diff --git a/crates/apollo-compiler/src/validation/input_object.rs b/crates/apollo-compiler/src/validation/input_object.rs
index 5c62701..6e8623a 100644
--- a/crates/apollo-compiler/src/validation/input_object.rs
+++ b/crates/apollo-compiler/src/validation/input_object.rs
@@ -68,7 +68,7 @@ pub fn validate_input_object_definition(
 
     // Fields in an Input Object Definition must be unique
     //
-    // Returns Unique Value error.
+    // Returns Unique Definition error.
     diagnostics.extend(db.validate_input_values(
         input_obj.input_fields_definition.clone(),
         hir::DirectiveLocation::InputFieldDefinition,
diff --git a/crates/apollo-compiler/src/validation/operation.rs b/crates/apollo-compiler/src/validation/operation.rs
index 006557d..bcb783b 100644
--- a/crates/apollo-compiler/src/validation/operation.rs
+++ b/crates/apollo-compiler/src/validation/operation.rs
@@ -22,6 +22,7 @@ pub fn validate_operation_definitions(
         if def.object_type(db.upcast()).is_some() {
             diagnostics.extend(db.validate_selection_set(def.selection_set().clone()));
         }
+        diagnostics.extend(db.validate_unused_variable(def.clone()));
     }
 
     let subscription_operations = db.upcast().subscription_operations(file_id);
diff --git a/crates/apollo-compiler/src/validation/union_.rs b/crates/apollo-compiler/src/validation/union_.rs
index 9f65273..2eb2dc6 100644
--- a/crates/apollo-compiler/src/validation/union_.rs
+++ b/crates/apollo-compiler/src/validation/union_.rs
@@ -32,7 +32,7 @@ pub fn validate_union_definition(
         let redefined_definition = union_member.loc();
         // A Union type must include one or more unique member types.
         //
-        // Return a Unique Value error in case of a duplicate member.
+        // Return a Unique Definition error in case of a duplicate member.
         if let Some(prev_def) = seen.get(&name) {
             let original_definition = prev_def.loc();
 
diff --git a/crates/apollo-compiler/src/validation/validation_db.rs b/crates/apollo-compiler/src/validation/validation_db.rs
index 8c3fd37..7c2dc09 100644
--- a/crates/apollo-compiler/src/validation/validation_db.rs
+++ b/crates/apollo-compiler/src/validation/validation_db.rs
@@ -164,7 +164,7 @@ pub trait ValidationDatabase:
         -> Vec<ApolloDiagnostic>;
 
     #[salsa::invoke(variable::validate_unused_variables)]
-    fn validate_unused_variable(&self, file_id: FileId) -> Vec<ApolloDiagnostic>;
+    fn validate_unused_variable(&self, op: Arc<OperationDefinition>) -> Vec<ApolloDiagnostic>;
 }
 
 pub fn validate(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
@@ -202,7 +202,6 @@ pub fn validate_executable(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<
 
     diagnostics.extend(db.validate_operation_definitions(file_id));
     diagnostics.extend(db.validate_fragment_definitions(file_id));
-    diagnostics.extend(db.validate_unused_variable(file_id));
 
     diagnostics
 }
diff --git a/crates/apollo-compiler/src/validation/variable.rs b/crates/apollo-compiler/src/validation/variable.rs
index 858c5ef..53f1e52 100644
--- a/crates/apollo-compiler/src/validation/variable.rs
+++ b/crates/apollo-compiler/src/validation/variable.rs
@@ -1,10 +1,13 @@
-use std::collections::HashSet;
+use std::{
+    collections::{HashMap, HashSet},
+    sync::Arc,
+};
 
 use crate::{
     diagnostics::{ApolloDiagnostic, DiagnosticData, Label},
-    hir,
+    hir::{self, VariableDefinition},
     validation::ValidationSet,
-    FileId, ValidationDatabase,
+    ValidationDatabase,
 };
 
 pub fn validate_variable_definitions(
@@ -13,81 +16,128 @@ pub fn validate_variable_definitions(
 ) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
+    let mut seen: HashMap<&str, &VariableDefinition> = HashMap::new();
     for variable in variables.iter() {
         diagnostics.extend(db.validate_directives(
             variable.directives().to_vec(),
             hir::DirectiveLocation::VariableDefinition,
         ));
+
+        let ty = variable.ty();
+        if !ty.is_input_type(db.upcast()) {
+            let type_def = ty.type_def(db.upcast());
+            if let Some(type_def) = type_def {
+                let ty_name = type_def.kind();
+                diagnostics.push(
+                    ApolloDiagnostic::new(db, variable.loc().into(), DiagnosticData::InputType {
+                        name: variable.name().into(),
+                        ty: ty_name,
+                    })
+                    .label(Label::new(ty.loc().unwrap(), format!("this is of `{ty_name}` type")))
+                    .help("objects, unions, and interfaces cannot be used because variables can only be of input type"),
+                );
+            } else {
+                diagnostics.push(
+                    ApolloDiagnostic::new(
+                        db,
+                        variable.loc.into(),
+                        DiagnosticData::UndefinedDefinition { name: ty.name() },
+                    )
+                    .label(Label::new(variable.loc, "not found in the type system")),
+                )
+            }
+        }
+
+        // Variable definitions must be unique.
+        //
+        // Return a Unique Definition error in case of a duplicate value.
+        let name = variable.name();
+        if let Some(prev_def) = seen.get(&name) {
+            let original_definition = prev_def.loc();
+            let redefined_definition = variable.loc();
+            diagnostics.push(
+                ApolloDiagnostic::new(
+                    db,
+                    redefined_definition.into(),
+                    DiagnosticData::UniqueDefinition {
+                        ty: "enum",
+                        name: name.into(),
+                        original_definition: original_definition.into(),
+                        redefined_definition: redefined_definition.into(),
+                    },
+                )
+                .labels([
+                    Label::new(
+                        original_definition,
+                        format!("previous definition of `{name}` here"),
+                    ),
+                    Label::new(redefined_definition, format!("`{name}` redefined here")),
+                ])
+                .help(format!("{name} must only be defined once in this enum.")),
+            );
+        } else {
+            seen.insert(name, variable);
+        }
     }
 
     diagnostics
 }
 
-// check in scope
-// check in use
-// compare the two
 pub fn validate_unused_variables(
     db: &dyn ValidationDatabase,
-    file_id: FileId,
+    op: Arc<hir::OperationDefinition>,
 ) -> Vec<ApolloDiagnostic> {
-    db.operations(file_id)
+    let defined_vars: HashSet<ValidationSet> = op
+        .variables()
+        .iter()
+        .map(|var| ValidationSet {
+            name: var.name().into(),
+            loc: var.loc(),
+        })
+        .collect();
+    let used_vars: HashSet<ValidationSet> = op
+        .selection_set
+        .selection()
         .iter()
-        .flat_map(|op| {
-            let defined_vars: HashSet<ValidationSet> = op
-                .variables()
+        .flat_map(|sel| {
+            let vars: HashSet<ValidationSet> = sel
+                .variables(db.upcast())
                 .iter()
                 .map(|var| ValidationSet {
                     name: var.name().into(),
                     loc: var.loc(),
                 })
                 .collect();
-            let used_vars: HashSet<ValidationSet> = op
-                .selection_set
-                .clone()
-                .selection()
-                .iter()
-                .flat_map(|sel| {
-                    let vars: HashSet<ValidationSet> = sel
-                        .variables(db.upcast())
-                        .iter()
-                        .map(|var| ValidationSet {
-                            name: var.name().into(),
-                            loc: var.loc(),
-                        })
-                        .collect();
-                    vars
-                })
-                .collect();
-            let undefined_vars = used_vars.difference(&defined_vars);
-            let mut diagnostics: Vec<ApolloDiagnostic> = undefined_vars
-                .map(|undefined_var| {
-                    ApolloDiagnostic::new(
-                        db,
-                        undefined_var.loc.into(),
-                        DiagnosticData::UndefinedDefinition {
-                            name: undefined_var.name.clone(),
-                        },
-                    )
-                    .label(Label::new(undefined_var.loc, "not found in this scope"))
-                })
-                .collect();
-
-            let unused_vars = defined_vars.difference(&used_vars);
-            let warnings = unused_vars.map(|unused_var| {
-                ApolloDiagnostic::new(
-                    db,
-                    unused_var.loc.into(),
-                    DiagnosticData::UnusedVariable {
-                        name: unused_var.name.clone(),
-                    },
-                )
-                .label(Label::new(unused_var.loc, "this variable is never used"))
-            });
-
-            diagnostics.extend(warnings);
-            diagnostics
+            vars
+        })
+        .collect();
+    let undefined_vars = used_vars.difference(&defined_vars);
+    let mut diagnostics: Vec<ApolloDiagnostic> = undefined_vars
+        .map(|undefined_var| {
+            ApolloDiagnostic::new(
+                db,
+                undefined_var.loc.into(),
+                DiagnosticData::UndefinedDefinition {
+                    name: undefined_var.name.clone(),
+                },
+            )
+            .label(Label::new(undefined_var.loc, "not found in this scope"))
         })
-        .collect()
+        .collect();
+
+    let unused_vars = defined_vars.difference(&used_vars);
+    diagnostics.extend(unused_vars.map(|unused_var| {
+        ApolloDiagnostic::new(
+            db,
+            unused_var.loc.into(),
+            DiagnosticData::UnusedVariable {
+                name: unused_var.name.clone(),
+            },
+        )
+        .label(Label::new(unused_var.loc, "this variable is never used"))
+    }));
+
+    diagnostics
 }
 
 #[cfg(test)]


Test patch:
diff --git a/crates/apollo-compiler/test_data/diagnostics/0055_duplicate_variable_definitions.graphql b/crates/apollo-compiler/test_data/diagnostics/0055_duplicate_variable_definitions.graphql
new file mode 100644
index 0000000..97d2f66
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0055_duplicate_variable_definitions.graphql
@@ -0,0 +1,16 @@
+query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) {
+  dog {
+    isHouseTrained(atOtherHomes: $atOtherHomes)
+  }
+}
+
+type Query {
+  dog: Dog
+}
+
+type Dog {
+  name: String!
+  nickname: String
+  barkVolume: Int
+  isHouseTrained(atOtherHomes: Boolean): Boolean!
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/diagnostics/0055_duplicate_variable_definitions.txt b/crates/apollo-compiler/test_data/diagnostics/0055_duplicate_variable_definitions.txt
new file mode 100644
index 0000000..dfec175
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0055_duplicate_variable_definitions.txt
@@ -0,0 +1,57 @@
+[
+    ApolloDiagnostic {
+        cache: {
+            55: "0055_duplicate_variable_definitions.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 55,
+            },
+            offset: 48,
+            length: 22,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 55,
+                    },
+                    offset: 24,
+                    length: 22,
+                },
+                text: "previous definition of `atOtherHomes` here",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 55,
+                    },
+                    offset: 48,
+                    length: 22,
+                },
+                text: "`atOtherHomes` redefined here",
+            },
+        ],
+        help: Some(
+            "atOtherHomes must only be defined once in this enum.",
+        ),
+        data: UniqueDefinition {
+            ty: "enum",
+            name: "atOtherHomes",
+            original_definition: DiagnosticLocation {
+                file_id: FileId {
+                    id: 55,
+                },
+                offset: 24,
+                length: 22,
+            },
+            redefined_definition: DiagnosticLocation {
+                file_id: FileId {
+                    id: 55,
+                },
+                offset: 48,
+                length: 22,
+            },
+        },
+    },
+]
diff --git a/crates/apollo-compiler/test_data/diagnostics/0056_variables_are_input_types.graphql b/crates/apollo-compiler/test_data/diagnostics/0056_variables_are_input_types.graphql
new file mode 100644
index 0000000..7b23ccf
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0056_variables_are_input_types.graphql
@@ -0,0 +1,86 @@
+query takesCat($cat: Cat) {
+  dog {
+    isHouseTrained(atOtherHomes: $cat)
+  }
+}
+
+query takesDogBang($dog: Dog!) {
+  dog {
+    isHouseTrained(atOtherHomes: $dog)
+  }
+}
+
+query takesListOfPet($pets: [Pet]) {
+  booleanList(booleanListArg: $pets)
+}
+
+query takesCatOrDog($catOrDog: CatOrDog) {
+  dog {
+    isHouseTrained(atOtherHomes: $catOrDog)
+  }
+}
+
+query takesCatOrDog($catOrDog: Dragon) {
+  dog {
+    isHouseTrained(atOtherHomes: $catOrDog)
+  }
+}
+
+type Query {
+  dog: Dog
+  findDog(complex: ComplexInput): Dog
+  booleanList(booleanListArg: [Boolean!]): Boolean
+}
+
+input ComplexInput {
+  name: String
+  owner: String
+}
+
+enum DogCommand {
+  SIT
+  DOWN
+  HEEL
+}
+
+type Dog implements Pet {
+  name: String!
+  nickname: String
+  barkVolume: Int
+  doesKnowCommand(dogCommand: DogCommand!): Boolean!
+  isHouseTrained(atOtherHomes: Boolean): Boolean!
+  owner: Human
+}
+
+interface Sentient {
+  name: String!
+}
+
+interface Pet {
+  name: String!
+}
+
+type Alien implements Sentient {
+  name: String!
+  homePlanet: String
+}
+
+type Human implements Sentient {
+  name: String!
+  pets: [Pet!]
+}
+
+enum CatCommand {
+  JUMP
+}
+
+type Cat implements Pet {
+  name: String!
+  nickname: String
+  doesKnowCommand(catCommand: CatCommand!): Boolean!
+  meowVolume: Int
+}
+
+union CatOrDog = Cat | Dog
+union DogOrHuman = Dog | Human
+union HumanOrAlien = Human | Alien
\ No newline at end of file
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
new file mode 100644
index 0000000..20b80d6
--- /dev/null
+++ b/crates/apollo-compiler/test_data/diagnostics/0056_variables_are_input_types.txt
@@ -0,0 +1,209 @@
+[
+    ApolloDiagnostic {
+        cache: {
+            56: "0056_variables_are_input_types.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 56,
+            },
+            offset: 15,
+            length: 9,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 56,
+                    },
+                    offset: 21,
+                    length: 3,
+                },
+                text: "this is of `ObjectTypeDefinition` type",
+            },
+        ],
+        help: Some(
+            "objects, unions, and interfaces cannot be used because variables can only be of input type",
+        ),
+        data: InputType {
+            name: "cat",
+            ty: "ObjectTypeDefinition",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            56: "0056_variables_are_input_types.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 56,
+            },
+            offset: 101,
+            length: 10,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 56,
+                    },
+                    offset: 107,
+                    length: 3,
+                },
+                text: "this is of `ObjectTypeDefinition` type",
+            },
+        ],
+        help: Some(
+            "objects, unions, and interfaces cannot be used because variables can only be of input type",
+        ),
+        data: InputType {
+            name: "dog",
+            ty: "ObjectTypeDefinition",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            56: "0056_variables_are_input_types.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 56,
+            },
+            offset: 190,
+            length: 12,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 56,
+                    },
+                    offset: 197,
+                    length: 5,
+                },
+                text: "this is of `InterfaceTypeDefinition` type",
+            },
+        ],
+        help: Some(
+            "objects, unions, and interfaces cannot be used because variables can only be of input type",
+        ),
+        data: InputType {
+            name: "pets",
+            ty: "InterfaceTypeDefinition",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            56: "0056_variables_are_input_types.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 56,
+            },
+            offset: 266,
+            length: 19,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 56,
+                    },
+                    offset: 277,
+                    length: 8,
+                },
+                text: "this is of `UnionTypeDefinition` type",
+            },
+        ],
+        help: Some(
+            "objects, unions, and interfaces cannot be used because variables can only be of input type",
+        ),
+        data: InputType {
+            name: "catOrDog",
+            ty: "UnionTypeDefinition",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            56: "0056_variables_are_input_types.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 56,
+            },
+            offset: 368,
+            length: 17,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 56,
+                    },
+                    offset: 368,
+                    length: 17,
+                },
+                text: "not found in the type system",
+            },
+        ],
+        help: None,
+        data: UndefinedDefinition {
+            name: "Dragon",
+        },
+    },
+    ApolloDiagnostic {
+        cache: {
+            56: "0056_variables_are_input_types.graphql",
+        },
+        location: DiagnosticLocation {
+            file_id: FileId {
+                id: 56,
+            },
+            offset: 354,
+            length: 13,
+        },
+        labels: [
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 56,
+                    },
+                    offset: 252,
+                    length: 13,
+                },
+                text: "previous definition of `takesCatOrDog` here",
+            },
+            Label {
+                location: DiagnosticLocation {
+                    file_id: FileId {
+                        id: 56,
+                    },
+                    offset: 354,
+                    length: 13,
+                },
+                text: "`takesCatOrDog` redefined here",
+            },
+        ],
+        help: Some(
+            "`takesCatOrDog` must only be defined once in this document.",
+        ),
+        data: UniqueDefinition {
+            ty: "operation",
+            name: "takesCatOrDog",
+            original_definition: DiagnosticLocation {
+                file_id: FileId {
+                    id: 56,
+                },
+                offset: 252,
+                length: 13,
+            },
+            redefined_definition: DiagnosticLocation {
+                file_id: FileId {
+                    id: 56,
+                },
+                offset: 354,
+                length: 13,
+            },
+        },
+    },
+]
diff --git a/crates/apollo-compiler/test_data/ok/0016_same_variables_in_multiple_operations.graphql b/crates/apollo-compiler/test_data/ok/0016_same_variables_in_multiple_operations.graphql
new file mode 100644
index 0000000..11dd529
--- /dev/null
+++ b/crates/apollo-compiler/test_data/ok/0016_same_variables_in_multiple_operations.graphql
@@ -0,0 +1,24 @@
+query A($atOtherHomes: Boolean) {
+  ...HouseTrainedFragment
+}
+
+query B($atOtherHomes: Boolean) {
+  ...HouseTrainedFragment
+}
+
+fragment HouseTrainedFragment on Query {
+  dog {
+    isHouseTrained(atOtherHomes: $atOtherHomes)
+  }
+}
+
+type Query {
+  dog: Dog
+}
+
+type Dog {
+  name: String!
+  nickname: String
+  barkVolume: Int
+  isHouseTrained(atOtherHomes: Boolean): Boolean!
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/ok/0016_same_variables_in_multiple_operations.txt b/crates/apollo-compiler/test_data/ok/0016_same_variables_in_multiple_operations.txt
new file mode 100644
index 0000000..5f4c319
--- /dev/null
+++ b/crates/apollo-compiler/test_data/ok/0016_same_variables_in_multiple_operations.txt
@@ -0,0 +1,189 @@
+- DOCUMENT@0..372
+    - OPERATION_DEFINITION@0..61
+        - OPERATION_TYPE@0..5
+            - query_KW@0..5 "query"
+        - WHITESPACE@5..6 " "
+        - NAME@6..7
+            - IDENT@6..7 "A"
+        - VARIABLE_DEFINITIONS@7..31
+            - L_PAREN@7..8 "("
+            - VARIABLE_DEFINITION@8..30
+                - VARIABLE@8..21
+                    - DOLLAR@8..9 "$"
+                    - NAME@9..21
+                        - IDENT@9..21 "atOtherHomes"
+                - COLON@21..22 ":"
+                - WHITESPACE@22..23 " "
+                - NAMED_TYPE@23..30
+                    - NAME@23..30
+                        - IDENT@23..30 "Boolean"
+            - R_PAREN@30..31 ")"
+        - WHITESPACE@31..32 " "
+        - SELECTION_SET@32..61
+            - L_CURLY@32..33 "{"
+            - WHITESPACE@33..36 "\n  "
+            - FRAGMENT_SPREAD@36..59
+                - SPREAD@36..39 "..."
+                - FRAGMENT_NAME@39..59
+                    - NAME@39..59
+                        - IDENT@39..59 "HouseTrainedFragment"
+            - WHITESPACE@59..60 "\n"
+            - R_CURLY@60..61 "}"
+    - WHITESPACE@61..63 "\n\n"
+    - OPERATION_DEFINITION@63..124
+        - OPERATION_TYPE@63..68
+            - query_KW@63..68 "query"
+        - WHITESPACE@68..69 " "
+        - NAME@69..70
+            - IDENT@69..70 "B"
+        - VARIABLE_DEFINITIONS@70..94
+            - L_PAREN@70..71 "("
+            - VARIABLE_DEFINITION@71..93
+                - VARIABLE@71..84
+                    - DOLLAR@71..72 "$"
+                    - NAME@72..84
+                        - IDENT@72..84 "atOtherHomes"
+                - COLON@84..85 ":"
+                - WHITESPACE@85..86 " "
+                - NAMED_TYPE@86..93
+                    - NAME@86..93
+                        - IDENT@86..93 "Boolean"
+            - R_PAREN@93..94 ")"
+        - WHITESPACE@94..95 " "
+        - SELECTION_SET@95..124
+            - L_CURLY@95..96 "{"
+            - WHITESPACE@96..99 "\n  "
+            - FRAGMENT_SPREAD@99..122
+                - SPREAD@99..102 "..."
+                - FRAGMENT_NAME@102..122
+                    - NAME@102..122
+                        - IDENT@102..122 "HouseTrainedFragment"
+            - WHITESPACE@122..123 "\n"
+            - R_CURLY@123..124 "}"
+    - WHITESPACE@124..126 "\n\n"
+    - FRAGMENT_DEFINITION@126..228
+        - fragment_KW@126..134 "fragment"
+        - WHITESPACE@134..135 " "
+        - FRAGMENT_NAME@135..155
+            - NAME@135..155
+                - IDENT@135..155 "HouseTrainedFragment"
+        - WHITESPACE@155..156 " "
+        - TYPE_CONDITION@156..164
+            - on_KW@156..158 "on"
+            - WHITESPACE@158..159 " "
+            - NAMED_TYPE@159..164
+                - NAME@159..164
+                    - IDENT@159..164 "Query"
+        - WHITESPACE@164..165 " "
+        - SELECTION_SET@165..228
+            - L_CURLY@165..166 "{"
+            - WHITESPACE@166..169 "\n  "
+            - FIELD@169..226
+                - NAME@169..172
+                    - IDENT@169..172 "dog"
+                - WHITESPACE@172..173 " "
+                - SELECTION_SET@173..226
+                    - L_CURLY@173..174 "{"
+                    - WHITESPACE@174..179 "\n    "
+                    - FIELD@179..222
+                        - NAME@179..193
+                            - IDENT@179..193 "isHouseTrained"
+                        - ARGUMENTS@193..222
+                            - L_PAREN@193..194 "("
+                            - ARGUMENT@194..221
+                                - NAME@194..206
+                                    - IDENT@194..206 "atOtherHomes"
+                                - COLON@206..207 ":"
+                                - WHITESPACE@207..208 " "
+                                - VARIABLE@208..221
+                                    - DOLLAR@208..209 "$"
+                                    - NAME@209..221
+                                        - IDENT@209..221 "atOtherHomes"
+                            - R_PAREN@221..222 ")"
+                    - WHITESPACE@222..225 "\n  "
+                    - R_CURLY@225..226 "}"
+            - WHITESPACE@226..227 "\n"
+            - R_CURLY@227..228 "}"
+    - WHITESPACE@228..230 "\n\n"
+    - OBJECT_TYPE_DEFINITION@230..255
+        - type_KW@230..234 "type"
+        - WHITESPACE@234..235 " "
+        - NAME@235..240
+            - IDENT@235..240 "Query"
+        - WHITESPACE@240..241 " "
+        - FIELDS_DEFINITION@241..255
+            - L_CURLY@241..242 "{"
+            - WHITESPACE@242..245 "\n  "
+            - FIELD_DEFINITION@245..253
+                - NAME@245..248
+                    - IDENT@245..248 "dog"
+                - COLON@248..249 ":"
+                - WHITESPACE@249..250 " "
+                - NAMED_TYPE@250..253
+                    - NAME@250..253
+                        - IDENT@250..253 "Dog"
+            - WHITESPACE@253..254 "\n"
+            - R_CURLY@254..255 "}"
+    - WHITESPACE@255..257 "\n\n"
+    - OBJECT_TYPE_DEFINITION@257..372
+        - type_KW@257..261 "type"
+        - WHITESPACE@261..262 " "
+        - NAME@262..265
+            - IDENT@262..265 "Dog"
+        - WHITESPACE@265..266 " "
+        - FIELDS_DEFINITION@266..372
+            - L_CURLY@266..267 "{"
+            - WHITESPACE@267..270 "\n  "
+            - FIELD_DEFINITION@270..283
+                - NAME@270..274
+                    - IDENT@270..274 "name"
+                - COLON@274..275 ":"
+                - WHITESPACE@275..276 " "
+                - NON_NULL_TYPE@276..283
+                    - NAMED_TYPE@276..282
+                        - NAME@276..282
+                            - IDENT@276..282 "String"
+                    - BANG@282..283 "!"
+            - WHITESPACE@283..286 "\n  "
+            - FIELD_DEFINITION@286..302
+                - NAME@286..294
+                    - IDENT@286..294 "nickname"
+                - COLON@294..295 ":"
+                - WHITESPACE@295..296 " "
+                - NAMED_TYPE@296..302
+                    - NAME@296..302
+                        - IDENT@296..302 "String"
+            - WHITESPACE@302..305 "\n  "
+            - FIELD_DEFINITION@305..320
+                - NAME@305..315
+                    - IDENT@305..315 "barkVolume"
+                - COLON@315..316 ":"
+                - WHITESPACE@316..317 " "
+                - NAMED_TYPE@317..320
+                    - NAME@317..320
+                        - IDENT@317..320 "Int"
+            - WHITESPACE@320..323 "\n  "
+            - FIELD_DEFINITION@323..370
+                - NAME@323..337
+                    - IDENT@323..337 "isHouseTrained"
+                - ARGUMENTS_DEFINITION@337..360
+                    - L_PAREN@337..338 "("
+                    - INPUT_VALUE_DEFINITION@338..359
+                        - NAME@338..350
+                            - IDENT@338..350 "atOtherHomes"
+                        - COLON@350..351 ":"
+                        - WHITESPACE@351..352 " "
+                        - NAMED_TYPE@352..359
+                            - NAME@352..359
+                                - IDENT@352..359 "Boolean"
+                    - R_PAREN@359..360 ")"
+                - COLON@360..361 ":"
+                - WHITESPACE@361..362 " "
+                - NON_NULL_TYPE@362..370
+                    - NAMED_TYPE@362..369
+                        - NAME@362..369
+                            - IDENT@362..369 "Boolean"
+                    - BANG@369..370 "!"
+            - WHITESPACE@370..371 "\n"
+            - R_CURLY@371..372 "}"
+recursion limit: 4096, high: 4
diff --git a/crates/apollo-compiler/test_data/ok/0017_variables_are_input_types.graphql b/crates/apollo-compiler/test_data/ok/0017_variables_are_input_types.graphql
new file mode 100644
index 0000000..880dd28
--- /dev/null
+++ b/crates/apollo-compiler/test_data/ok/0017_variables_are_input_types.graphql
@@ -0,0 +1,34 @@
+query takesBoolean($atOtherHomes: Boolean) {
+  dog {
+    isHouseTrained(atOtherHomes: $atOtherHomes)
+  }
+}
+
+query takesComplexInput($complexInput: ComplexInput) {
+  findDog(complex: $complexInput) {
+    name
+  }
+}
+
+query TakesListOfBooleanBang($booleans: [Boolean!]) {
+  booleanList(booleanListArg: $booleans)
+}
+
+
+type Dog {
+  name: String!
+  nickname: String
+  barkVolume: Int
+  isHouseTrained(atOtherHomes: Boolean): Boolean!
+}
+
+input ComplexInput {
+  name: String
+  owner: String
+}
+
+type Query {
+  dog: Dog
+  findDog(complex: ComplexInput): Dog
+  booleanList(booleanListArg: [Boolean!]): Boolean
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/ok/0017_variables_are_input_types.txt b/crates/apollo-compiler/test_data/ok/0017_variables_are_input_types.txt
new file mode 100644
index 0000000..515fa83
--- /dev/null
+++ b/crates/apollo-compiler/test_data/ok/0017_variables_are_input_types.txt
@@ -0,0 +1,303 @@
+- DOCUMENT@0..600
+    - OPERATION_DEFINITION@0..106
+        - OPERATION_TYPE@0..5
+            - query_KW@0..5 "query"
+        - WHITESPACE@5..6 " "
+        - NAME@6..18
+            - IDENT@6..18 "takesBoolean"
+        - VARIABLE_DEFINITIONS@18..42
+            - L_PAREN@18..19 "("
+            - VARIABLE_DEFINITION@19..41
+                - VARIABLE@19..32
+                    - DOLLAR@19..20 "$"
+                    - NAME@20..32
+                        - IDENT@20..32 "atOtherHomes"
+                - COLON@32..33 ":"
+                - WHITESPACE@33..34 " "
+                - NAMED_TYPE@34..41
+                    - NAME@34..41
+                        - IDENT@34..41 "Boolean"
+            - R_PAREN@41..42 ")"
+        - WHITESPACE@42..43 " "
+        - SELECTION_SET@43..106
+            - L_CURLY@43..44 "{"
+            - WHITESPACE@44..47 "\n  "
+            - FIELD@47..104
+                - NAME@47..50
+                    - IDENT@47..50 "dog"
+                - WHITESPACE@50..51 " "
+                - SELECTION_SET@51..104
+                    - L_CURLY@51..52 "{"
+                    - WHITESPACE@52..57 "\n    "
+                    - FIELD@57..100
+                        - NAME@57..71
+                            - IDENT@57..71 "isHouseTrained"
+                        - ARGUMENTS@71..100
+                            - L_PAREN@71..72 "("
+                            - ARGUMENT@72..99
+                                - NAME@72..84
+                                    - IDENT@72..84 "atOtherHomes"
+                                - COLON@84..85 ":"
+                                - WHITESPACE@85..86 " "
+                                - VARIABLE@86..99
+                                    - DOLLAR@86..87 "$"
+                                    - NAME@87..99
+                                        - IDENT@87..99 "atOtherHomes"
+                            - R_PAREN@99..100 ")"
+                    - WHITESPACE@100..103 "\n  "
+                    - R_CURLY@103..104 "}"
+            - WHITESPACE@104..105 "\n"
+            - R_CURLY@105..106 "}"
+    - WHITESPACE@106..108 "\n\n"
+    - OPERATION_DEFINITION@108..213
+        - OPERATION_TYPE@108..113
+            - query_KW@108..113 "query"
+        - WHITESPACE@113..114 " "
+        - NAME@114..131
+            - IDENT@114..131 "takesComplexInput"
+        - VARIABLE_DEFINITIONS@131..160
+            - L_PAREN@131..132 "("
+            - VARIABLE_DEFINITION@132..159
+                - VARIABLE@132..145
+                    - DOLLAR@132..133 "$"
+                    - NAME@133..145
+                        - IDENT@133..145 "complexInput"
+                - COLON@145..146 ":"
+                - WHITESPACE@146..147 " "
+                - NAMED_TYPE@147..159
+                    - NAME@147..159
+                        - IDENT@147..159 "ComplexInput"
+            - R_PAREN@159..160 ")"
+        - WHITESPACE@160..161 " "
+        - SELECTION_SET@161..213
+            - L_CURLY@161..162 "{"
+            - WHITESPACE@162..165 "\n  "
+            - FIELD@165..211
+                - NAME@165..172
+                    - IDENT@165..172 "findDog"
+                - ARGUMENTS@172..196
+                    - L_PAREN@172..173 "("
+                    - ARGUMENT@173..195
+                        - NAME@173..180
+                            - IDENT@173..180 "complex"
+                        - COLON@180..181 ":"
+                        - WHITESPACE@181..182 " "
+                        - VARIABLE@182..195
+                            - DOLLAR@182..183 "$"
+                            - NAME@183..195
+                                - IDENT@183..195 "complexInput"
+                    - R_PAREN@195..196 ")"
+                - WHITESPACE@196..197 " "
+                - SELECTION_SET@197..211
+                    - L_CURLY@197..198 "{"
+                    - WHITESPACE@198..203 "\n    "
+                    - FIELD@203..207
+                        - NAME@203..207
+                            - IDENT@203..207 "name"
+                    - WHITESPACE@207..210 "\n  "
+                    - R_CURLY@210..211 "}"
+            - WHITESPACE@211..212 "\n"
+            - R_CURLY@212..213 "}"
+    - WHITESPACE@213..215 "\n\n"
+    - OPERATION_DEFINITION@215..311
+        - OPERATION_TYPE@215..220
+            - query_KW@215..220 "query"
+        - WHITESPACE@220..221 " "
+        - NAME@221..243
+            - IDENT@221..243 "TakesListOfBooleanBang"
+        - VARIABLE_DEFINITIONS@243..266
+            - L_PAREN@243..244 "("
+            - VARIABLE_DEFINITION@244..265
+                - VARIABLE@244..253
+                    - DOLLAR@244..245 "$"
+                    - NAME@245..253
+                        - IDENT@245..253 "booleans"
+                - COLON@253..254 ":"
+                - WHITESPACE@254..255 " "
+                - LIST_TYPE@255..265
+                    - L_BRACK@255..256 "["
+                    - NON_NULL_TYPE@256..264
+                        - NAMED_TYPE@256..263
+                            - NAME@256..263
+                                - IDENT@256..263 "Boolean"
+                        - BANG@263..264 "!"
+                    - R_BRACK@264..265 "]"
+            - R_PAREN@265..266 ")"
+        - WHITESPACE@266..267 " "
+        - SELECTION_SET@267..311
+            - L_CURLY@267..268 "{"
+            - WHITESPACE@268..271 "\n  "
+            - FIELD@271..309
+                - NAME@271..282
+                    - IDENT@271..282 "booleanList"
+                - ARGUMENTS@282..309
+                    - L_PAREN@282..283 "("
+                    - ARGUMENT@283..308
+                        - NAME@283..297
+                            - IDENT@283..297 "booleanListArg"
+                        - COLON@297..298 ":"
+                        - WHITESPACE@298..299 " "
+                        - VARIABLE@299..308
+                            - DOLLAR@299..300 "$"
+                            - NAME@300..308
+                                - IDENT@300..308 "booleans"
+                    - R_PAREN@308..309 ")"
+            - WHITESPACE@309..310 "\n"
+            - R_CURLY@310..311 "}"
+    - WHITESPACE@311..314 "\n\n\n"
+    - OBJECT_TYPE_DEFINITION@314..429
+        - type_KW@314..318 "type"
+        - WHITESPACE@318..319 " "
+        - NAME@319..322
+            - IDENT@319..322 "Dog"
+        - WHITESPACE@322..323 " "
+        - FIELDS_DEFINITION@323..429
+            - L_CURLY@323..324 "{"
+            - WHITESPACE@324..327 "\n  "
+            - FIELD_DEFINITION@327..340
+                - NAME@327..331
+                    - IDENT@327..331 "name"
+                - COLON@331..332 ":"
+                - WHITESPACE@332..333 " "
+                - NON_NULL_TYPE@333..340
+                    - NAMED_TYPE@333..339
+                        - NAME@333..339
+                            - IDENT@333..339 "String"
+                    - BANG@339..340 "!"
+            - WHITESPACE@340..343 "\n  "
+            - FIELD_DEFINITION@343..359
+                - NAME@343..351
+                    - IDENT@343..351 "nickname"
+                - COLON@351..352 ":"
+                - WHITESPACE@352..353 " "
+                - NAMED_TYPE@353..359
+                    - NAME@353..359
+                        - IDENT@353..359 "String"
+            - WHITESPACE@359..362 "\n  "
+            - FIELD_DEFINITION@362..377
+                - NAME@362..372
+                    - IDENT@362..372 "barkVolume"
+                - COLON@372..373 ":"
+                - WHITESPACE@373..374 " "
+                - NAMED_TYPE@374..377
+                    - NAME@374..377
+                        - IDENT@374..377 "Int"
+            - WHITESPACE@377..380 "\n  "
+            - FIELD_DEFINITION@380..427
+                - NAME@380..394
+                    - IDENT@380..394 "isHouseTrained"
+                - ARGUMENTS_DEFINITION@394..417
+                    - L_PAREN@394..395 "("
+                    - INPUT_VALUE_DEFINITION@395..416
+                        - NAME@395..407
+                            - IDENT@395..407 "atOtherHomes"
+                        - COLON@407..408 ":"
+                        - WHITESPACE@408..409 " "
+                        - NAMED_TYPE@409..416
+                            - NAME@409..416
+                                - IDENT@409..416 "Boolean"
+                    - R_PAREN@416..417 ")"
+                - COLON@417..418 ":"
+                - WHITESPACE@418..419 " "
+                - NON_NULL_TYPE@419..427
+                    - NAMED_TYPE@419..426
+                        - NAME@419..426
+                            - IDENT@419..426 "Boolean"
+                    - BANG@426..427 "!"
+            - WHITESPACE@427..428 "\n"
+            - R_CURLY@428..429 "}"
+    - WHITESPACE@429..431 "\n\n"
+    - INPUT_OBJECT_TYPE_DEFINITION@431..484
+        - input_KW@431..436 "input"
+        - WHITESPACE@436..437 " "
+        - NAME@437..449
+            - IDENT@437..449 "ComplexInput"
+        - WHITESPACE@449..450 " "
+        - INPUT_FIELDS_DEFINITION@450..484
+            - L_CURLY@450..451 "{"
+            - WHITESPACE@451..454 "\n  "
+            - INPUT_VALUE_DEFINITION@454..466
+                - NAME@454..458
+                    - IDENT@454..458 "name"
+                - COLON@458..459 ":"
+                - WHITESPACE@459..460 " "
+                - NAMED_TYPE@460..466
+                    - NAME@460..466
+                        - IDENT@460..466 "String"
+            - WHITESPACE@466..469 "\n  "
+            - INPUT_VALUE_DEFINITION@469..482
+                - NAME@469..474
+                    - IDENT@469..474 "owner"
+                - COLON@474..475 ":"
+                - WHITESPACE@475..476 " "
+                - NAMED_TYPE@476..482
+                    - NAME@476..482
+                        - IDENT@476..482 "String"
+            - WHITESPACE@482..483 "\n"
+            - R_CURLY@483..484 "}"
+    - WHITESPACE@484..486 "\n\n"
+    - OBJECT_TYPE_DEFINITION@486..600
+        - type_KW@486..490 "type"
+        - WHITESPACE@490..491 " "
+        - NAME@491..496
+            - IDENT@491..496 "Query"
+        - WHITESPACE@496..497 " "
+        - FIELDS_DEFINITION@497..600
+            - L_CURLY@497..498 "{"
+            - WHITESPACE@498..501 "\n  "
+            - FIELD_DEFINITION@501..509
+                - NAME@501..504
+                    - IDENT@501..504 "dog"
+                - COLON@504..505 ":"
+                - WHITESPACE@505..506 " "
+                - NAMED_TYPE@506..509
+                    - NAME@506..509
+                        - IDENT@506..509 "Dog"
+            - WHITESPACE@509..512 "\n  "
+            - FIELD_DEFINITION@512..547
+                - NAME@512..519
+                    - IDENT@512..519 "findDog"
+                - ARGUMENTS_DEFINITION@519..542
+                    - L_PAREN@519..520 "("
+                    - INPUT_VALUE_DEFINITION@520..541
+                        - NAME@520..527
+                            - IDENT@520..527 "complex"
+                        - COLON@527..528 ":"
+                        - WHITESPACE@528..529 " "
+                        - NAMED_TYPE@529..541
+                            - NAME@529..541
+                                - IDENT@529..541 "ComplexInput"
+                    - R_PAREN@541..542 ")"
+                - COLON@542..543 ":"
+                - WHITESPACE@543..544 " "
+                - NAMED_TYPE@544..547
+                    - NAME@544..547
+                        - IDENT@544..547 "Dog"
+            - WHITESPACE@547..550 "\n  "
+            - FIELD_DEFINITION@550..598
+                - NAME@550..561
+                    - IDENT@550..561 "booleanList"
+                - ARGUMENTS_DEFINITION@561..589
+                    - L_PAREN@561..562 "("
+                    - INPUT_VALUE_DEFINITION@562..588
+                        - NAME@562..576
+                            - IDENT@562..576 "booleanListArg"
+                        - COLON@576..577 ":"
+                        - WHITESPACE@577..578 " "
+                        - LIST_TYPE@578..588
+                            - L_BRACK@578..579 "["
+                            - NON_NULL_TYPE@579..587
+                                - NAMED_TYPE@579..586
+                                    - NAME@579..586
+                                        - IDENT@579..586 "Boolean"
+                                - BANG@586..587 "!"
+                            - R_BRACK@587..588 "]"
+                    - R_PAREN@588..589 ")"
+                - COLON@589..590 ":"
+                - WHITESPACE@590..591 " "
+                - NAMED_TYPE@591..598
+                    - NAME@591..598
+                        - IDENT@591..598 "Boolean"
+            - WHITESPACE@598..599 "\n"
+            - R_CURLY@599..600 "}"
+recursion limit: 4096, high: 4