Repository: apollographql/apollo-rs
Pull request #436 (2023-01-20 18:22:51)

chore(compiler): simplify validation database
This change simplifies the validation database to (try) to walk the tree as much as it's possible. We are able to take advantage of salsa's caching for a lot of the queries, so when the input to a validation rule doesn't change, we don't have to re-validate.

lrlna: @goto-bus-stop i think it's at a good stage for feedback! Would love to hear what you think about this approach so far.

I wanted to have as much validation as it's currently possible in salsa queries which should be helpful for re-running validation on updated inputs. I'd love to still have a bit more caching for all the queries that current iterate on plurals: operations, objects, interfaces etc, but we gotta make our `ByName` IndexMap a `Hash` first. Figured we could do this at a later stage.

There are still quite a few things wrong that I have to fix, especially with object, interface and operation validations. And I think I should probably deal with all the new vecs all over the place now rather than later
goto-bus-stop: I like the overall direction, I think if we also Arc a lot of stuff here to reduce the cost of cloning, we'll be good to go. Though we may want to just merge it asap as everything else will conflict with this and address that in followup PRs.

TBH making `IndexMap` impl `Hash` sounds wrong, is it because we want to use it as an input somewhere? can we not instead retrieve the `ByName` maps with db queries wherever needed?

Patch:
diff --git a/crates/apollo-compiler/examples/extend_db.rs b/crates/apollo-compiler/examples/extend_db.rs
index d63b006..92232fb 100644
--- a/crates/apollo-compiler/examples/extend_db.rs
+++ b/crates/apollo-compiler/examples/extend_db.rs
@@ -194,6 +194,6 @@ scalar url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
     // Display lints.
     for lint in &lints {
-        println!("{}", lint)
+        println!("{lint}")
     }
 }
diff --git a/crates/apollo-compiler/examples/file_watcher.rs b/crates/apollo-compiler/examples/file_watcher.rs
index ebe22e1..a592ce3 100644
--- a/crates/apollo-compiler/examples/file_watcher.rs
+++ b/crates/apollo-compiler/examples/file_watcher.rs
@@ -131,7 +131,7 @@ impl FileWatcher {
     fn validate(&self) {
         let diagnostics = self.compiler.validate();
         for diag in diagnostics {
-            println!("{}", diag)
+            println!("{diag}")
         }
     }
 }
diff --git a/crates/apollo-compiler/examples/multi_source_validation.rs b/crates/apollo-compiler/examples/multi_source_validation.rs
index dcd4526..5f465d0 100644
--- a/crates/apollo-compiler/examples/multi_source_validation.rs
+++ b/crates/apollo-compiler/examples/multi_source_validation.rs
@@ -24,7 +24,7 @@ fn compile_from_dir() -> io::Result<()> {
 
     let diagnostics = compiler.validate();
     for diagnostic in &diagnostics {
-        println!("{}", diagnostic);
+        println!("{diagnostic}");
     }
     assert_eq!(diagnostics.len(), 1);
 
@@ -52,7 +52,7 @@ fn compile_schema_and_query_files() -> io::Result<()> {
 
     let diagnostics = compiler.validate();
     for diagnostic in &diagnostics {
-        println!("{}", diagnostic);
+        println!("{diagnostic}");
     }
     assert_eq!(diagnostics.len(), 1);
 
@@ -137,7 +137,7 @@ query getDogName {
 
     let diagnostics = compiler.validate();
     for diagnostic in &diagnostics {
-        println!("{}", diagnostic);
+        println!("{diagnostic}");
     }
     assert_eq!(diagnostics.len(), 1);
 
diff --git a/crates/apollo-compiler/src/database/document.rs b/crates/apollo-compiler/src/database/document.rs
index f82d542..26f944f 100644
--- a/crates/apollo-compiler/src/database/document.rs
+++ b/crates/apollo-compiler/src/database/document.rs
@@ -372,7 +372,7 @@ mod tests {
                 union UnionType2 = Foo | Bar
                 "#,
             );
-            let schema = format!("{}\n{}", base_schema, schema);
+            let schema = format!("{base_schema}\n{schema}");
             let mut compiler = ApolloCompiler::new();
             compiler.add_document(&schema, "schema.graphql");
             compiler
@@ -398,7 +398,7 @@ mod tests {
                 interface InterfaceType2 implements Foo & Bar { me: String }
                 "#,
             );
-            let schema = format!("{}\n{}", base_schema, schema);
+            let schema = format!("{base_schema}\n{schema}");
             let mut compiler = ApolloCompiler::new();
             compiler.add_document(&schema, "schema.graphql");
             compiler
diff --git a/crates/apollo-compiler/src/diagnostics.rs b/crates/apollo-compiler/src/diagnostics.rs
index 85b5982..2f230ab 100644
--- a/crates/apollo-compiler/src/diagnostics.rs
+++ b/crates/apollo-compiler/src/diagnostics.rs
@@ -16,6 +16,7 @@ pub enum ApolloDiagnostic {
     UndefinedDefinition(UndefinedDefinition),
     UndefinedField(UndefinedField),
     UniqueArgument(UniqueArgument),
+    UniqueInputValue(UniqueInputValue),
     RecursiveDefinition(RecursiveDefinition),
     TransitiveImplementedInterfaces(TransitiveImplementedInterfaces),
     QueryRootOperationType(QueryRootOperationType),
@@ -46,6 +47,7 @@ impl ApolloDiagnostic {
                 | ApolloDiagnostic::QueryRootOperationType(_)
                 | ApolloDiagnostic::UndefinedField(_)
                 | ApolloDiagnostic::UniqueArgument(_)
+                | ApolloDiagnostic::UniqueInputValue(_)
                 | ApolloDiagnostic::BuiltInScalarDefinition(_)
                 | ApolloDiagnostic::OutputType(_)
                 | ApolloDiagnostic::ObjectType(_)
@@ -90,8 +92,9 @@ impl ApolloDiagnostic {
             ApolloDiagnostic::ObjectType(diagnostic) => Report::new(diagnostic.clone()),
             ApolloDiagnostic::UndefinedField(diagnostic) => Report::new(diagnostic.clone()),
             ApolloDiagnostic::UniqueArgument(diagnostic) => Report::new(diagnostic.clone()),
-            ApolloDiagnostic::IntrospectionField(diagnostic) => Report::new(diagnostic.clone()),
+            ApolloDiagnostic::UniqueInputValue(diagnostic) => Report::new(diagnostic.clone()),
             ApolloDiagnostic::UnsupportedLocation(diagnostic) => Report::new(diagnostic.clone()),
+            ApolloDiagnostic::IntrospectionField(diagnostic) => Report::new(diagnostic.clone()),
         }
     }
 }
@@ -386,6 +389,23 @@ pub struct UndefinedField {
     pub help: String,
 }
 
+#[derive(Diagnostic, Debug, Error, Clone, Hash, PartialEq, Eq)]
+#[error("input values must be unique")]
+#[diagnostic(code("apollo-compiler validation error"))]
+pub struct UniqueInputValue {
+    // current operation type: subscription, mutation, query
+    pub value: String,
+
+    #[source_code]
+    pub src: Arc<str>,
+
+    #[label("previous definition of `{}` input value here", self.value)]
+    pub original_value: SourceSpan,
+
+    #[label("`{}` is redefined here", self.value)]
+    pub redefined_value: SourceSpan,
+}
+
 #[derive(Diagnostic, Debug, Error, Clone, Hash, PartialEq, Eq)]
 #[error("the argument `{}` is defined multiple times", self.name)]
 #[diagnostic(code("apollo-compiler validation error"))]
diff --git a/crates/apollo-compiler/src/lib.rs b/crates/apollo-compiler/src/lib.rs
index ca6ce46..61b64e6 100644
--- a/crates/apollo-compiler/src/lib.rs
+++ b/crates/apollo-compiler/src/lib.rs
@@ -295,7 +295,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -349,7 +349,7 @@ type Query {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -500,7 +500,7 @@ directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: j
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -628,7 +628,7 @@ fragment vipCustomer on User {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -688,7 +688,7 @@ type Result {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
     }
@@ -709,7 +709,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -742,7 +742,7 @@ enum Pet {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -784,7 +784,7 @@ type SearchQuery {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -832,7 +832,7 @@ type Book @delegateField(name: "pageCount") @delegateField(name: "author") {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -867,7 +867,7 @@ input Point2D {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -898,7 +898,7 @@ directive @directiveB(name: String) on OBJECT | INTERFACE
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -932,7 +932,7 @@ scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -1014,7 +1014,7 @@ scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         assert!(diagnostics.is_empty());
 
@@ -1129,7 +1129,7 @@ type User
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
         // the scalar warning diagnostic
         assert_eq!(diagnostics.len(), 1);
@@ -1158,7 +1158,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
 
         assert!(diagnostics.is_empty());
diff --git a/crates/apollo-compiler/src/validation/argument.rs b/crates/apollo-compiler/src/validation/argument.rs
new file mode 100644
index 0000000..cde52f6
--- /dev/null
+++ b/crates/apollo-compiler/src/validation/argument.rs
@@ -0,0 +1,47 @@
+use std::collections::HashMap;
+
+use crate::{
+    diagnostics::UniqueArgument,
+    hir::{self, DirectiveLocation},
+    validation::ValidationDatabase,
+    ApolloDiagnostic,
+};
+
+pub fn validate_arguments(
+    db: &dyn ValidationDatabase,
+    args: Vec<hir::Argument>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+    let mut seen: HashMap<&str, &hir::Argument> = HashMap::new();
+
+    for arg in &args {
+        let name = arg.name();
+        if let Some(prev_arg) = seen.get(name) {
+            let prev_offset = prev_arg.loc().offset();
+            let prev_node_len = prev_arg.loc().node_len();
+
+            let current_offset = arg.loc().offset();
+            let current_node_len = arg.loc().node_len();
+
+            diagnostics.push(ApolloDiagnostic::UniqueArgument(UniqueArgument {
+                name: name.into(),
+                src: db.source_code(prev_arg.loc().file_id()),
+                original_definition: (prev_offset, prev_node_len).into(),
+                redefined_definition: (current_offset, current_node_len).into(),
+                help: Some(format!("`{name}` argument must only be provided once.")),
+            }));
+        } else {
+            seen.insert(name, arg);
+        }
+    }
+
+    diagnostics
+}
+
+pub fn validate_arguments_definition(
+    db: &dyn ValidationDatabase,
+    args_def: hir::ArgumentsDefinition,
+    dir_loc: DirectiveLocation,
+) -> Vec<ApolloDiagnostic> {
+    db.validate_input_values(args_def.input_values, dir_loc)
+}
diff --git a/crates/apollo-compiler/src/validation/directive.rs b/crates/apollo-compiler/src/validation/directive.rs
index 1defda3..057bfe2 100644
--- a/crates/apollo-compiler/src/validation/directive.rs
+++ b/crates/apollo-compiler/src/validation/directive.rs
@@ -1,12 +1,18 @@
+use std::collections::HashSet;
+
 use crate::{
-    diagnostics::{RecursiveDefinition, UniqueDefinition},
+    diagnostics::{
+        RecursiveDefinition, UndefinedDefinition, UniqueDefinition, UnsupportedLocation,
+    },
+    hir,
     validation::ast_type_definitions,
     ApolloDiagnostic, ValidationDatabase,
 };
 use apollo_parser::ast;
+use miette::SourceSpan;
 
-pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    let mut errors = Vec::new();
+pub fn validate_directive_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
 
     // Directive definitions must have unique names.
     //
@@ -21,7 +27,7 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
                 if *hir_loc == ast_loc {
                     // The HIR node was built from this AST node. This is fine.
                 } else {
-                    errors.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
+                    diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                         ty: "directive".into(),
                         name: name.to_owned(),
                         src: db.source_code(hir_loc.file_id()),
@@ -45,8 +51,8 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
             for directive in input_values.directives().iter() {
                 let directive_name = directive.name();
                 if name == directive_name {
-                    errors.push(ApolloDiagnostic::RecursiveDefinition(RecursiveDefinition {
-                        message: format!("{} directive definition cannot reference itself", name),
+                    diagnostics.push(ApolloDiagnostic::RecursiveDefinition(RecursiveDefinition {
+                        message: format!("{name} directive definition cannot reference itself"),
                         definition: directive.loc().into(),
                         src: db.source_code(directive.loc().file_id()),
                         definition_label: "recursive directive definition".into(),
@@ -54,7 +60,54 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
                 }
             }
         }
+
+        // Validate directive definitions' arguments
+        diagnostics.extend(db.validate_arguments_definition(
+            directive_def.arguments.clone(),
+            hir::DirectiveLocation::ArgumentDefinition,
+        ));
     }
 
-    errors
+    diagnostics
+}
+
+pub fn validate_directives(
+    db: &dyn ValidationDatabase,
+    dirs: Vec<hir::Directive>,
+    dir_loc: hir::DirectiveLocation,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+    for dir in dirs {
+        diagnostics.extend(db.validate_arguments(dir.arguments().to_vec()));
+
+        let name = dir.name();
+        let loc = dir.loc();
+        let offset = loc.offset();
+        let len = loc.node_len();
+
+        if let Some(directive) = db.find_directive_definition_by_name(name.into()) {
+            let directive_def_loc = directive
+                .loc
+                .map(|loc| SourceSpan::new(loc.offset().into(), loc.node_len().into()));
+            let allowed_loc: HashSet<hir::DirectiveLocation> =
+                HashSet::from_iter(directive.directive_locations().iter().cloned());
+            if !allowed_loc.contains(&dir_loc) {
+                diagnostics.push(ApolloDiagnostic::UnsupportedLocation(UnsupportedLocation {
+                ty: name.into(),
+                dir_loc,
+                src: db.source_code(loc.file_id()),
+                directive: (offset, len).into(),
+                directive_def: directive_def_loc,
+                help: Some("the directive must be used in a location that the service has declared support for".into()),
+            }))
+            }
+        } else {
+            diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
+                ty: name.into(),
+                src: db.source_code(loc.file_id()),
+                definition: (offset, len).into(),
+            }))
+        }
+    }
+    diagnostics
 }
diff --git a/crates/apollo-compiler/src/validation/enum_.rs b/crates/apollo-compiler/src/validation/enum_.rs
index 9d5f431..d324173 100644
--- a/crates/apollo-compiler/src/validation/enum_.rs
+++ b/crates/apollo-compiler/src/validation/enum_.rs
@@ -1,58 +1,78 @@
-use std::collections::HashMap;
+use std::{collections::HashMap, sync::Arc};
 
 use crate::{
     diagnostics::{CapitalizedValue, UniqueDefinition},
-    hir::EnumValueDefinition,
+    hir::{self, EnumValueDefinition},
     ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+pub fn validate_enum_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
-    // An Enum type must define one or more unique enum values.
-    //
-    // Return a Unique Value error in case of a duplicate value.
-    for enum_def in db.enums().values() {
-        let mut seen: HashMap<&str, &EnumValueDefinition> = HashMap::new();
-        for enum_value in enum_def.enum_values_definition().iter() {
-            let value = enum_value.enum_value();
-            if let Some(prev_def) = seen.get(&value) {
-                let prev_offset = prev_def.loc().offset();
-                let prev_node_len = prev_def.loc().node_len();
-
-                let current_offset = enum_value.loc().offset();
-                let current_node_len = enum_value.loc().node_len();
-                diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
-                    ty: "enum".into(),
-                    name: value.into(),
-                    src: db.source_code(prev_def.loc().file_id()),
-                    original_definition: (prev_offset, prev_node_len).into(),
-                    redefined_definition: (current_offset, current_node_len).into(),
-                    help: Some(format!("{value} must only be defined once in this enum.")),
-                }));
-            } else {
-                seen.insert(value, enum_value);
-            }
+    let defs = &db.type_system_definitions().enums;
+    for def in defs.values() {
+        diagnostics.extend(db.validate_enum_definition(def.clone()));
+    }
+
+    diagnostics
+}
+
+pub fn validate_enum_definition(
+    db: &dyn ValidationDatabase,
+    enum_def: Arc<hir::EnumTypeDefinition>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics =
+        db.validate_directives(enum_def.directives().to_vec(), hir::DirectiveLocation::Enum);
+
+    let mut seen: HashMap<&str, &EnumValueDefinition> = HashMap::new();
+    for enum_val in enum_def.enum_values_definition() {
+        diagnostics.extend(db.validate_enum_value(enum_val.clone()));
+
+        // An Enum type must define one or more unique enum values.
+        //
+        // Return a Unique Value error in case of a duplicate value.
+        let value = enum_val.enum_value();
+        if let Some(prev_def) = seen.get(&value) {
+            let prev_offset = prev_def.loc().offset();
+            let prev_node_len = prev_def.loc().node_len();
+
+            let current_offset = enum_val.loc().offset();
+            let current_node_len = enum_val.loc().node_len();
+            diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
+                ty: "enum".into(),
+                name: value.into(),
+                src: db.source_code(prev_def.loc().file_id()),
+                original_definition: (prev_offset, prev_node_len).into(),
+                redefined_definition: (current_offset, current_node_len).into(),
+                help: Some(format!("{value} must only be defined once in this enum.")),
+            }));
+        } else {
+            seen.insert(value, enum_val);
         }
     }
 
+    diagnostics
+}
+
+pub(crate) fn validate_enum_value(
+    db: &dyn ValidationDatabase,
+    enum_val: hir::EnumValueDefinition,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = db.validate_directives(
+        enum_val.directives().to_vec(),
+        hir::DirectiveLocation::EnumValue,
+    );
+
     // (convention) Values in an Enum Definition should be capitalized.
     //
     // Return a Capitalized Value warning if enum value is not capitalized.
-    for enum_def in db.enums().values() {
-        for enum_value in enum_def.enum_values_definition().iter() {
-            let value = enum_value.enum_value();
-            let offset = enum_value.loc().offset();
-            let len = enum_value.loc().node_len();
-
-            if value.to_uppercase() != value {
-                diagnostics.push(ApolloDiagnostic::CapitalizedValue(CapitalizedValue {
-                    ty: value.into(),
-                    src: db.source_code(enum_value.loc().file_id()),
-                    value: (offset, len).into(),
-                }));
-            }
-        }
+    let value = enum_val.enum_value();
+    if value.to_uppercase() != value {
+        diagnostics.push(ApolloDiagnostic::CapitalizedValue(CapitalizedValue {
+            ty: value.into(),
+            src: db.source_code(enum_val.loc().file_id()),
+            value: (enum_val.loc().offset(), enum_val.loc().node_len()).into(),
+        }));
     }
 
     diagnostics
diff --git a/crates/apollo-compiler/src/validation/field.rs b/crates/apollo-compiler/src/validation/field.rs
new file mode 100644
index 0000000..1a79bfb
--- /dev/null
+++ b/crates/apollo-compiler/src/validation/field.rs
@@ -0,0 +1,101 @@
+use std::{collections::HashMap, sync::Arc};
+
+use crate::{
+    diagnostics::{OutputType, UndefinedDefinition, UniqueField},
+    hir,
+    validation::ValidationDatabase,
+    ApolloDiagnostic,
+};
+
+pub fn validate_field(
+    db: &dyn ValidationDatabase,
+    field: Arc<hir::Field>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics =
+        db.validate_directives(field.directives().to_vec(), hir::DirectiveLocation::Field);
+    diagnostics.extend(db.validate_arguments(field.arguments().to_vec()));
+
+    diagnostics
+}
+
+pub fn validate_field_definition(
+    db: &dyn ValidationDatabase,
+    field: hir::FieldDefinition,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = db.validate_directives(
+        field.directives().to_vec(),
+        hir::DirectiveLocation::FieldDefinition,
+    );
+
+    diagnostics.extend(db.validate_arguments_definition(
+        field.arguments,
+        hir::DirectiveLocation::ArgumentDefinition,
+    ));
+
+    diagnostics
+}
+
+pub fn validate_field_definitions(
+    db: &dyn ValidationDatabase,
+    fields: Vec<hir::FieldDefinition>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    let mut seen: HashMap<&str, &hir::FieldDefinition> = HashMap::new();
+
+    for field in fields.iter() {
+        diagnostics.extend(db.validate_field_definition(field.clone()));
+
+        // Fields must be unique.
+        //
+        // Returns Unique Field error.
+        let field_name = field.name();
+        let offset = field.loc().offset();
+        let len = field.loc().node_len();
+
+        if let Some(prev_field) = seen.get(field_name) {
+            let prev_offset = prev_field.loc().offset();
+            let prev_node_len = prev_field.loc().node_len();
+
+            diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
+                field: field_name.into(),
+                src: db.source_code(prev_field.loc().file_id()),
+                original_field: (prev_offset, prev_node_len).into(),
+                redefined_field: (offset, len).into(),
+                help: Some(format!(
+                    "`{field_name}` field must only be defined once in this definition."
+                )),
+            }));
+        } else {
+            seen.insert(field_name, field);
+        }
+
+        // Field types in Object Types must be of output type
+        if let Some(field_ty) = field.ty().type_def(db.upcast()) {
+            if !field.ty().is_output_type(db.upcast()) {
+                diagnostics.push(ApolloDiagnostic::OutputType(OutputType {
+                    name: field.name().into(),
+                    ty: field_ty.kind(),
+                    src: db.source_code(field.loc().file_id()),
+                    definition: (offset, len).into(),
+                }))
+            }
+        } else if let Some(loc) = field.ty().loc() {
+            let field_ty_offset = loc.offset();
+            let field_ty_len = loc.node_len();
+            diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
+                ty: field.ty().name(),
+                src: db.source_code(field.loc().file_id()),
+                definition: (field_ty_offset, field_ty_len).into(),
+            }))
+        } else {
+            diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
+                ty: field.ty().name(),
+                src: db.source_code(field.loc().file_id()),
+                definition: (offset, len).into(),
+            }))
+        }
+    }
+
+    diagnostics
+}
diff --git a/crates/apollo-compiler/src/validation/fragment.rs b/crates/apollo-compiler/src/validation/fragment.rs
new file mode 100644
index 0000000..e36ac9c
--- /dev/null
+++ b/crates/apollo-compiler/src/validation/fragment.rs
@@ -0,0 +1,17 @@
+use crate::{hir::DirectiveLocation, ApolloDiagnostic, FileId, ValidationDatabase};
+
+pub fn validate_fragment_definitions(
+    db: &dyn ValidationDatabase,
+    file_id: FileId,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+    for def in db.fragments(file_id).values() {
+        diagnostics.extend(db.validate_directives(
+            def.directives().to_vec(),
+            DirectiveLocation::FragmentDefinition,
+        ));
+        diagnostics.extend(db.validate_selection_set(def.selection_set().clone()));
+    }
+
+    diagnostics
+}
diff --git a/crates/apollo-compiler/src/validation/input_object.rs b/crates/apollo-compiler/src/validation/input_object.rs
index d5687ca..c3c381b 100644
--- a/crates/apollo-compiler/src/validation/input_object.rs
+++ b/crates/apollo-compiler/src/validation/input_object.rs
@@ -1,16 +1,32 @@
-use std::collections::HashMap;
+use std::{collections::HashMap, sync::Arc};
 
 use crate::{
-    diagnostics::{UniqueDefinition, UniqueField},
-    hir::InputValueDefinition,
+    diagnostics::{UniqueDefinition, UniqueInputValue},
+    hir,
     validation::ast_type_definitions,
     ApolloDiagnostic, ValidationDatabase,
 };
 use apollo_parser::ast;
 
-pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+pub fn validate_input_object_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
+    let defs = &db.type_system_definitions().input_objects;
+    for def in defs.values() {
+        diagnostics.extend(db.validate_input_object_definition(def.clone()));
+    }
+
+    diagnostics
+}
+
+pub fn validate_input_object_definition(
+    db: &dyn ValidationDatabase,
+    input_obj: Arc<hir::InputObjectTypeDefinition>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = db.validate_directives(
+        input_obj.directives().to_vec(),
+        hir::DirectiveLocation::InputObject,
+    );
     // Input Object Definitions must have unique names.
     //
     // Return a Unique Definition error in case of a duplicate name.
@@ -40,34 +56,42 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     // Fields in an Input Object Definition must be unique
     //
     // Returns Unique Value error.
-    for input_objects in db.input_objects().values() {
-        let mut seen: HashMap<&str, &InputValueDefinition> = HashMap::new();
+    diagnostics.extend(db.validate_input_values(
+        input_obj.input_fields_definition.clone(),
+        hir::DirectiveLocation::InputFieldDefinition,
+    ));
+
+    diagnostics
+}
+
+pub fn validate_input_values(
+    db: &dyn ValidationDatabase,
+    input_values: Arc<Vec<hir::InputValueDefinition>>,
+    // directive location depends on parent node location, so we pass this down from parent
+    dir_loc: hir::DirectiveLocation,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+    let mut seen: HashMap<&str, &hir::InputValueDefinition> = HashMap::new();
 
-        let input_fields = input_objects.input_fields_definition();
+    for input_value in input_values.iter() {
+        diagnostics.extend(db.validate_directives(input_value.directives().to_vec(), dir_loc));
 
-        for field in input_fields {
-            let field_name = field.name();
-            if let Some(prev_field) = seen.get(&field_name) {
-                if prev_field.loc().is_some() && field.loc().is_some() {
-                    let prev_offset = prev_field.loc().unwrap().offset();
-                    let prev_node_len = prev_field.loc().unwrap().node_len();
+        let name = input_value.name();
+        if let Some(prev_arg) = seen.get(name) {
+            let prev_offset = prev_arg.loc().unwrap().offset();
+            let prev_node_len = prev_arg.loc().unwrap().node_len();
 
-                    let current_offset = field.loc().unwrap().offset();
-                    let current_node_len = field.loc().unwrap().node_len();
+            let current_offset = input_value.loc().unwrap().offset();
+            let current_node_len = input_value.loc().unwrap().node_len();
 
-                    diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
-                        field: field_name.into(),
-                        src: db.source_code(prev_field.loc().unwrap().file_id()),
-                        original_field: (prev_offset, prev_node_len).into(),
-                        redefined_field: (current_offset, current_node_len).into(),
-                        help: Some(format!(
-                            "{field_name} field must only be defined once in this input object definition."
-                        )),
-                    }));
-                }
-            } else {
-                seen.insert(field_name, field);
-            }
+            diagnostics.push(ApolloDiagnostic::UniqueInputValue(UniqueInputValue {
+                value: name.into(),
+                src: db.source_code(prev_arg.loc().unwrap().file_id()),
+                original_value: (prev_offset, prev_node_len).into(),
+                redefined_value: (current_offset, current_node_len).into(),
+            }));
+        } else {
+            seen.insert(name, input_value);
         }
     }
 
diff --git a/crates/apollo-compiler/src/validation/interface.rs b/crates/apollo-compiler/src/validation/interface.rs
index 4579acf..8e723f5 100644
--- a/crates/apollo-compiler/src/validation/interface.rs
+++ b/crates/apollo-compiler/src/validation/interface.rs
@@ -1,17 +1,34 @@
-use std::collections::{HashMap, HashSet};
+use std::{collections::HashSet, sync::Arc};
 
 use crate::{
     diagnostics::{
-        MissingField, OutputType, RecursiveDefinition, TransitiveImplementedInterfaces,
-        UndefinedDefinition, UniqueDefinition, UniqueField,
+        MissingField, RecursiveDefinition, TransitiveImplementedInterfaces, UndefinedDefinition,
+        UniqueDefinition,
     },
-    hir::FieldDefinition,
+    hir::{self, ImplementsInterface},
     validation::{ast_type_definitions, ValidationSet},
     ApolloDiagnostic, ValidationDatabase,
 };
 use apollo_parser::ast;
 
-pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+pub fn validate_interface_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    let defs = &db.type_system_definitions().interfaces;
+    for def in defs.values() {
+        diagnostics.extend(
+            db.validate_directives(def.directives().to_vec(), hir::DirectiveLocation::Interface),
+        );
+        diagnostics.extend(db.validate_interface_definition(def.clone()));
+    }
+
+    diagnostics
+}
+
+pub fn validate_interface_definition(
+    db: &dyn ValidationDatabase,
+    interface_def: Arc<hir::InterfaceTypeDefinition>,
+) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // Interface definitions must have unique names.
@@ -63,7 +80,7 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
                     let offset = implements_interface.loc().offset();
                     let len = implements_interface.loc().node_len();
                     diagnostics.push(ApolloDiagnostic::RecursiveDefinition(RecursiveDefinition {
-                        message: format!("{} interface cannot implement itself", i_name),
+                        message: format!("{i_name} interface cannot implement itself"),
                         definition: (offset, len).into(),
                         src: db.source_code(implements_interface.loc().file_id()),
                         definition_label: "recursive implements interfaces".into(),
@@ -73,65 +90,68 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
         }
     }
 
-    // Interface Type field validations.
-    for interface_def in db.interfaces().values() {
-        let mut seen: HashMap<&str, &FieldDefinition> = HashMap::new();
-
-        let fields = interface_def.fields_definition();
-
-        for field in fields {
-            // Fields in an Interface definition must be unique
-            //
-            // Returns Unique Value error.
-            let field_name = field.name();
-            let offset = field.loc().offset();
-            let len = field.loc().node_len();
-
-            if let Some(prev_field) = seen.get(&field_name) {
-                let prev_offset = prev_field.loc().offset();
-                let prev_node_len = prev_field.loc().node_len();
-
-                diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
-                    field: field_name.into(),
-                    src: db.source_code(prev_field.loc().file_id()),
-                    original_field: (prev_offset, prev_node_len).into(),
-                    redefined_field: (offset, len).into(),
-                    help: Some(format!(
-                        "`{field_name}` field must only be defined once in this interface definition."
-                    )),
-                }));
-            } else {
-                seen.insert(field_name, field);
-            }
+    // Interface Type field validation.
+    diagnostics.extend(db.validate_field_definitions(interface_def.fields_definition().to_vec()));
 
-            // Field types in interface types must be of output type
-            if let Some(field_ty) = field.ty().type_def(db.upcast()) {
-                if !field.ty().is_output_type(db.upcast()) {
-                    diagnostics.push(ApolloDiagnostic::OutputType(OutputType {
-                        name: field.name().into(),
-                        ty: field_ty.kind(),
-                        src: db.source_code(field.loc().file_id()),
-                        definition: (offset, len).into(),
-                    }))
-                }
-            } else if let Some(loc) = field.ty().loc() {
-                let field_ty_offset = loc.offset();
-                let field_ty_len = loc.node_len();
-                diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
-                    ty: field.ty().name(),
-                    src: db.source_code(loc.file_id()),
-                    definition: (field_ty_offset, field_ty_len).into(),
-                }))
-            } else {
-                diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
-                    ty: field.ty().name(),
-                    src: db.source_code(field.loc().file_id()),
-                    definition: (offset, len).into(),
+    // Implements Interfaceds validation.
+    diagnostics
+        .extend(db.validate_implements_interfaces(interface_def.implements_interfaces().to_vec()));
+
+    // When defining an interface that implements another interface, the
+    // implementing interface must define each field that is specified by
+    // the implemented interface.
+    //
+    // Returns a Missing Field error.
+    let fields: HashSet<ValidationSet> = interface_def
+        .fields_definition()
+        .iter()
+        .map(|field| ValidationSet {
+            name: field.name().into(),
+            loc: *field.loc(),
+        })
+        .collect();
+    for implements_interface in interface_def.implements_interfaces().iter() {
+        if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
+            let implements_interface_fields: HashSet<ValidationSet> = interface
+                .fields_definition()
+                .iter()
+                .map(|field| ValidationSet {
+                    name: field.name().into(),
+                    loc: *field.loc(),
+                })
+                .collect();
+
+            let field_diff = implements_interface_fields.difference(&fields);
+
+            for missing_field in field_diff {
+                let current_offset = interface_def.loc().offset();
+                let current_len = interface_def.loc().node_len();
+
+                let super_offset = interface.loc().offset();
+                let super_len = interface.loc().node_len();
+
+                diagnostics.push(ApolloDiagnostic::MissingField(MissingField {
+                    ty: missing_field.name.clone(),
+                    src: db.source_code(interface_def.loc.file_id()),
+                    current_definition: (current_offset, current_len).into(),
+                    super_definition: (super_offset, super_len).into(),
+                    help: Some(
+                        "An interface must be a super-set of all interfaces it implement".into(),
+                    ),
                 }))
             }
         }
     }
 
+    diagnostics
+}
+
+pub fn validate_implements_interfaces(
+    db: &dyn ValidationDatabase,
+    impl_interfaces: Vec<ImplementsInterface>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
     let interfaces = db.interfaces();
     let defined_interfaces: HashSet<ValidationSet> = interfaces
         .iter()
@@ -140,112 +160,62 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
             loc: *interface.loc(),
         })
         .collect();
-    for interface_def in interfaces.values() {
-        // Implements Interfaces must be defined.
-        //
-        // Returns Undefined Definition error.
-        let implements_interfaces: HashSet<ValidationSet> = interface_def
-            .implements_interfaces()
-            .iter()
-            .map(|interface| ValidationSet {
-                name: interface.interface().to_owned(),
-                loc: *interface.loc(),
-            })
-            .collect();
-        let diff = implements_interfaces.difference(&defined_interfaces);
-        for undefined in diff {
-            let offset = undefined.loc.offset();
-            let len: usize = undefined.loc.node_len();
-            diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
-                ty: undefined.name.clone(),
-                src: db.source_code(undefined.loc.file_id()),
-                definition: (offset, len).into(),
-            }))
-        }
 
-        // Transitively implemented interfaces must be defined on an implementing
-        // type or interface.
-        //
-        // Returns Transitive Implemented Interfaces error.
-        let transitive_interfaces: HashSet<ValidationSet> = interface_def
-            .implements_interfaces()
-            .iter()
-            .filter_map(|implements_interface| {
-                if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
-                    let child_interfaces: HashSet<ValidationSet> = interface
-                        .implements_interfaces()
-                        .iter()
-                        .map(|interface| ValidationSet {
-                            name: interface.interface().to_owned(),
-                            loc: *implements_interface.loc(),
-                        })
-                        .collect();
-                    Some(child_interfaces)
-                } else {
-                    None
-                }
-            })
-            .flatten()
-            .collect();
-        let transitive_diff = transitive_interfaces.difference(&implements_interfaces);
-        for undefined in transitive_diff {
-            let offset = undefined.loc.offset();
-            let len = undefined.loc.node_len();
-            diagnostics.push(ApolloDiagnostic::TransitiveImplementedInterfaces(
-                TransitiveImplementedInterfaces {
-                    missing_interface: undefined.name.clone(),
-                    src: db.source_code(undefined.loc.file_id()),
-                    definition: (offset, len).into(),
-                },
-            ))
-        }
+    // Implements Interfaces must be defined.
+    //
+    // Returns Undefined Definition error.
+    let implements_interfaces: HashSet<ValidationSet> = impl_interfaces
+        .iter()
+        .map(|interface| ValidationSet {
+            name: interface.interface().to_owned(),
+            loc: *interface.loc(),
+        })
+        .collect();
+    let diff = implements_interfaces.difference(&defined_interfaces);
+    for undefined in diff {
+        let offset = undefined.loc.offset();
+        let len: usize = undefined.loc.node_len();
+        diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
+            ty: undefined.name.clone(),
+            src: db.source_code(undefined.loc.file_id()),
+            definition: (offset, len).into(),
+        }))
+    }
 
-        // When defining an interface that implements another interface, the
-        // implementing interface must define each field that is specified by
-        // the implemented interface.
-        //
-        // Returns a Missing Field error.
-        let fields: HashSet<ValidationSet> = interface_def
-            .fields_definition()
-            .iter()
-            .map(|field| ValidationSet {
-                name: field.name().into(),
-                loc: *field.loc(),
-            })
-            .collect();
-        for implements_interface in interface_def.implements_interfaces().iter() {
+    // Transitively implemented interfaces must be defined on an implementing
+    // type or interface.
+    //
+    // Returns Transitive Implemented Interfaces error.
+    let transitive_interfaces: HashSet<ValidationSet> = impl_interfaces
+        .iter()
+        .filter_map(|implements_interface| {
             if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
-                let implements_interface_fields: HashSet<ValidationSet> = interface
-                    .fields_definition()
+                let child_interfaces: HashSet<ValidationSet> = interface
+                    .implements_interfaces()
                     .iter()
-                    .map(|field| ValidationSet {
-                        name: field.name().into(),
-                        loc: *field.loc(),
+                    .map(|interface| ValidationSet {
+                        name: interface.interface().to_owned(),
+                        loc: *implements_interface.loc(),
                     })
                     .collect();
-
-                let field_diff = implements_interface_fields.difference(&fields);
-
-                for missing_field in field_diff {
-                    let current_offset = interface_def.loc().offset();
-                    let current_len = interface_def.loc().node_len();
-
-                    let super_offset = interface.loc().offset();
-                    let super_len = interface.loc().node_len();
-
-                    diagnostics.push(ApolloDiagnostic::MissingField(MissingField {
-                        ty: missing_field.name.clone(),
-                        src: db.source_code(interface_def.loc.file_id()),
-                        current_definition: (current_offset, current_len).into(),
-                        super_definition: (super_offset, super_len).into(),
-                        help: Some(
-                            "An interface must be a super-set of all interfaces it implement"
-                                .into(),
-                        ),
-                    }))
-                }
+                Some(child_interfaces)
+            } else {
+                None
             }
-        }
+        })
+        .flatten()
+        .collect();
+    let transitive_diff = transitive_interfaces.difference(&implements_interfaces);
+    for undefined in transitive_diff {
+        let offset = undefined.loc.offset();
+        let len = undefined.loc.node_len();
+        diagnostics.push(ApolloDiagnostic::TransitiveImplementedInterfaces(
+            TransitiveImplementedInterfaces {
+                missing_interface: undefined.name.clone(),
+                src: db.source_code(undefined.loc.file_id()),
+                definition: (offset, len).into(),
+            },
+        ))
     }
 
     diagnostics
@@ -279,7 +249,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 1);
     }
@@ -313,7 +283,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 1);
     }
@@ -341,7 +311,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 1);
     }
@@ -362,7 +332,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 1);
     }
@@ -393,7 +363,7 @@ interface Image implements Resource & Node {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 1);
     }
@@ -456,7 +426,7 @@ scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 3);
     }
diff --git a/crates/apollo-compiler/src/validation/mod.rs b/crates/apollo-compiler/src/validation/mod.rs
index 0b81c44..5f3c7f8 100644
--- a/crates/apollo-compiler/src/validation/mod.rs
+++ b/crates/apollo-compiler/src/validation/mod.rs
@@ -1,16 +1,19 @@
 mod validation_db;
 
+mod argument;
 mod directive;
 mod enum_;
+mod field;
+mod fragment;
 mod input_object;
 mod interface;
 mod object;
 mod operation;
 mod scalar;
 mod schema;
-mod subscription;
+mod selection;
 mod union_;
-mod unused_variable;
+mod variable;
 
 pub use validation_db::{ValidationDatabase, ValidationStorage};
 
diff --git a/crates/apollo-compiler/src/validation/object.rs b/crates/apollo-compiler/src/validation/object.rs
index d7ca0bd..17acc30 100644
--- a/crates/apollo-compiler/src/validation/object.rs
+++ b/crates/apollo-compiler/src/validation/object.rs
@@ -1,19 +1,34 @@
-use std::collections::{HashMap, HashSet};
+use std::{collections::HashSet, sync::Arc};
 
 use crate::{
-    diagnostics::{
-        MissingField, OutputType, TransitiveImplementedInterfaces, UndefinedDefinition,
-        UniqueDefinition, UniqueField,
-    },
-    hir::FieldDefinition,
+    diagnostics::{MissingField, UniqueDefinition},
+    hir,
     validation::{ast_type_definitions, ValidationSet},
     ApolloDiagnostic, ValidationDatabase,
 };
 use apollo_parser::ast;
 
-pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+pub fn validate_object_type_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
+    let defs = &db.type_system_definitions().objects;
+    for def in defs.values() {
+        diagnostics.extend(db.validate_object_type_definition(def.clone()))
+    }
+
+    diagnostics
+}
+
+pub fn validate_object_type_definition(
+    db: &dyn ValidationDatabase,
+    object: Arc<hir::ObjectTypeDefinition>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    diagnostics.extend(
+        db.validate_directives(object.directives().to_vec(), hir::DirectiveLocation::Object),
+    );
+
     // Object Type definitions must have unique names.
     //
     // Return a Unique Definition error in case of a duplicate name.
@@ -41,176 +56,53 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     }
 
     // Object Type field validations.
-    for object in db.object_types().values() {
-        let mut seen: HashMap<&str, &FieldDefinition> = HashMap::new();
-
-        let fields = object.fields_definition();
-        for field in fields {
-            // Fields in an Object Type definition must be unique
-            //
-            // Returns Unique Value error.
-            let field_name = field.name();
-            let offset = field.loc().offset();
-            let len = field.loc().node_len();
-
-            if let Some(prev_field) = seen.get(&field_name) {
-                let prev_offset = prev_field.loc().offset();
-                let prev_node_len = prev_field.loc().node_len();
-
-                diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
-                    field: field_name.into(),
-                    src: db.source_code(prev_field.loc().file_id()),
-                    original_field: (prev_offset, prev_node_len).into(),
-                    redefined_field: (offset, len).into(),
-                    help: Some(format!(
-                        "`{field_name}` field must only be defined once in this object type definition."
-                    )),
-                }));
-            } else {
-                seen.insert(field_name, field);
-            }
+    diagnostics.extend(db.validate_field_definitions(object.fields_definition().to_vec()));
 
-            // Field types in Object Types must be of output type
-            if let Some(field_ty) = field.ty().type_def(db.upcast()) {
-                if !field.ty().is_output_type(db.upcast()) {
-                    diagnostics.push(ApolloDiagnostic::OutputType(OutputType {
-                        name: field.name().into(),
-                        ty: field_ty.kind(),
-                        src: db.source_code(field.loc().file_id()),
-                        definition: (offset, len).into(),
-                    }))
-                }
-            } else if let Some(loc) = field.ty().loc() {
-                let field_ty_offset = loc.offset();
-                let field_ty_len = loc.node_len();
-                diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
-                    ty: field.ty().name(),
-                    src: db.source_code(field.loc().file_id()),
-                    definition: (field_ty_offset, field_ty_len).into(),
-                }))
-            } else {
-                diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
-                    ty: field.ty().name(),
-                    src: db.source_code(field.loc().file_id()),
-                    definition: (offset, len).into(),
-                }))
-            }
-        }
-    }
+    // Implements Interfaceds validation.
+    diagnostics.extend(db.validate_implements_interfaces(object.implements_interfaces().to_vec()));
 
-    let objects = db.object_types();
-    let defined_interfaces: HashSet<ValidationSet> = db
-        .interfaces()
+    // When defining an interface that implements another interface, the
+    // implementing interface must define each field that is specified by
+    // the implemented interface.
+    //
+    // Returns a Missing Field error.
+    let fields: HashSet<ValidationSet> = object
+        .fields_definition()
         .iter()
-        .map(|(name, interface)| ValidationSet {
-            name: name.to_owned(),
-            loc: *interface.loc(),
+        .map(|field| ValidationSet {
+            name: field.name().into(),
+            loc: *field.loc(),
         })
         .collect();
-    for object in objects.values() {
-        // Implements Interfaces must be defined.
-        //
-        // Returns Undefined Definition error.
-        let implements_interfaces: HashSet<ValidationSet> = object
-            .implements_interfaces()
-            .iter()
-            .map(|interface| ValidationSet {
-                name: interface.interface().to_owned(),
-                loc: *interface.loc(),
-            })
-            .collect();
-        let diff = implements_interfaces.difference(&defined_interfaces);
-        for undefined in diff {
-            let offset = undefined.loc.offset();
-            let len: usize = undefined.loc.node_len();
-            diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
-                ty: undefined.name.clone(),
-                src: db.source_code(undefined.loc.file_id()),
-                definition: (offset, len).into(),
-            }))
-        }
-
-        // Transitively implemented interfaces must be defined on an implementing
-        // type or interface.
-        //
-        // Returns Transitive Implemented Interfaces error.
-        let transitive_interfaces: HashSet<ValidationSet> = object
-            .implements_interfaces()
-            .iter()
-            .filter_map(|implements_interface| {
-                if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
-                    let child_interfaces: HashSet<ValidationSet> = interface
-                        .implements_interfaces()
-                        .iter()
-                        .map(|interface| ValidationSet {
-                            name: interface.interface().to_owned(),
-                            loc: *implements_interface.loc(),
-                        })
-                        .collect();
-                    Some(child_interfaces)
-                } else {
-                    None
-                }
-            })
-            .flatten()
-            .collect();
-        let transitive_diff = transitive_interfaces.difference(&implements_interfaces);
-        for undefined in transitive_diff {
-            let offset = undefined.loc.offset();
-            let len = undefined.loc.node_len();
-            diagnostics.push(ApolloDiagnostic::TransitiveImplementedInterfaces(
-                TransitiveImplementedInterfaces {
-                    missing_interface: undefined.name.clone(),
-                    src: db.source_code(undefined.loc.file_id()),
-                    definition: (offset, len).into(),
-                },
-            ))
-        }
-
-        // When defining an interface that implements another interface, the
-        // implementing interface must define each field that is specified by
-        // the implemented interface.
-        //
-        // Returns a Missing Field error.
-        let fields: HashSet<ValidationSet> = object
-            .fields_definition()
-            .iter()
-            .map(|field| ValidationSet {
-                name: field.name().into(),
-                loc: *field.loc(),
-            })
-            .collect();
-        for implements_interface in object.implements_interfaces().iter() {
-            if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
-                let implements_interface_fields: HashSet<ValidationSet> = interface
-                    .fields_definition()
-                    .iter()
-                    .map(|field| ValidationSet {
-                        name: field.name().into(),
-                        loc: *field.loc(),
-                    })
-                    .collect();
-
-                let field_diff = implements_interface_fields.difference(&fields);
-
-                for missing_field in field_diff {
-                    let current_offset = object.loc().offset();
-                    let current_len = object.loc().node_len();
-
-                    let super_offset = interface.loc().offset();
-                    let super_len = interface.loc().node_len();
-
-                    diagnostics.push(ApolloDiagnostic::MissingField(MissingField {
-                        ty: missing_field.name.clone(),
-                        src: db.source_code(object.loc().file_id()),
-                        current_definition: (current_offset, current_len).into(),
-                        super_definition: (super_offset, super_len).into(),
-                        help: Some(
-                            "An interface must be a super-set of all interfaces it implement"
-                                .into(),
-                        ),
-                    }))
-                }
+    for implements_interface in object.implements_interfaces().iter() {
+        if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
+            let implements_interface_fields: HashSet<ValidationSet> = interface
+                .fields_definition()
+                .iter()
+                .map(|field| ValidationSet {
+                    name: field.name().into(),
+                    loc: *field.loc(),
+                })
+                .collect();
+
+            let field_diff = implements_interface_fields.difference(&fields);
+
+            for missing_field in field_diff {
+                let current_offset = object.loc().offset();
+                let current_len = object.loc().node_len();
+
+                let super_offset = interface.loc().offset();
+                let super_len = interface.loc().node_len();
+
+                diagnostics.push(ApolloDiagnostic::MissingField(MissingField {
+                    ty: missing_field.name.clone(),
+                    src: db.source_code(object.loc().file_id()),
+                    current_definition: (current_offset, current_len).into(),
+                    super_definition: (super_offset, super_len).into(),
+                    help: Some(
+                        "An interface must be a super-set of all interfaces it implement".into(),
+                    ),
+                }))
             }
         }
     }
@@ -278,7 +170,7 @@ scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 3);
     }
diff --git a/crates/apollo-compiler/src/validation/operation.rs b/crates/apollo-compiler/src/validation/operation.rs
index 734f2c1..3f87469 100644
--- a/crates/apollo-compiler/src/validation/operation.rs
+++ b/crates/apollo-compiler/src/validation/operation.rs
@@ -1,21 +1,35 @@
-use std::collections::HashMap;
+use std::{collections::HashMap, sync::Arc};
 
 use crate::{
     diagnostics::{
-        MissingIdent, SingleRootField, UndefinedField, UniqueDefinition, UnsupportedOperation,
+        IntrospectionField, MissingIdent, SingleRootField, UndefinedField, UniqueDefinition,
+        UnsupportedOperation,
     },
-    hir::{OperationDefinition, Selection},
-    ApolloDiagnostic, FileId, ValidationDatabase,
+    hir, ApolloDiagnostic, FileId, ValidationDatabase,
 };
-// use crate::{diagnostics::ErrorDiagnostic, ApolloDiagnostic, Document};
 
-pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnostic> {
+pub fn validate_operation_definitions(
+    db: &dyn ValidationDatabase,
+    file_id: FileId,
+) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
+
     let operations = db.operations(file_id);
+    for def in operations.iter() {
+        diagnostics
+            .extend(db.validate_directives(def.directives().to_vec(), def.operation_ty().into()));
+        diagnostics.extend(db.validate_variable_definitions(def.variables.as_ref().clone()));
+        diagnostics.extend(db.validate_selection_set(def.selection_set().clone()));
+    }
+
     let subscription_operations = db.upcast().subscription_operations(file_id);
     let query_operations = db.upcast().query_operations(file_id);
     let mutation_operations = db.upcast().mutation_operations(file_id);
 
+    diagnostics.extend(db.validate_subscription_operations(subscription_operations));
+    diagnostics.extend(db.validate_query_operations(query_operations));
+    diagnostics.extend(db.validate_mutation_operations(mutation_operations));
+
     // It is possible to have an unnamed (anonymous) operation definition only
     // if there is **one** operation definition.
     //
@@ -44,7 +58,7 @@ pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnost
     // Operation definitions must have unique names.
     //
     // Return a Unique Operation Definition error in case of a duplicate name.
-    let mut seen: HashMap<&str, &OperationDefinition> = HashMap::new();
+    let mut seen: HashMap<&str, &hir::OperationDefinition> = HashMap::new();
     for op in operations.iter() {
         if let Some(name) = op.name() {
             if let Some(prev_def) = seen.get(&name) {
@@ -69,10 +83,65 @@ pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnost
         }
     }
 
+    // Fields must exist on the type being queried.
+    for op in operations.iter() {
+        for selection in op.selection_set().selection() {
+            let obj_name = op.object_type(db.upcast()).map(|obj| obj.name().to_owned());
+            if let hir::Selection::Field(field) = selection {
+                if field.ty(db.upcast()).is_none() {
+                    let offset = field.loc().offset();
+                    let len = field.loc().node_len();
+                    let field_name = field.name().into();
+                    let help = if let Some(obj_type) = obj_name {
+                        format!("`{field_name}` is not defined on `{obj_type}` type")
+                    } else {
+                        format!(
+                            "`{}` is not defined on the current {} root operation type.",
+                            field_name,
+                            op.operation_ty()
+                        )
+                    };
+                    diagnostics.push(ApolloDiagnostic::UndefinedField(UndefinedField {
+                        field: field_name,
+                        src: db.source_code(field.loc().file_id()),
+                        definition: (offset, len).into(),
+                        help,
+                    }))
+                }
+            }
+        }
+    }
+
+    diagnostics
+}
+
+pub fn validate_subscription_operations(
+    db: &dyn ValidationDatabase,
+    subscriptions: Arc<Vec<Arc<hir::OperationDefinition>>>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    // Subscription fields must not have an introspection field in the selection set.
+    //
+    // Return an Introspection Field error in case of an introspection field existing as the root field in the set.
+    for op in subscriptions.iter() {
+        for selection in op.selection_set().selection() {
+            if let hir::Selection::Field(field) = selection {
+                if field.is_introspection() {
+                    diagnostics.push(ApolloDiagnostic::IntrospectionField(IntrospectionField {
+                        field: field.name().into(),
+                        src: db.source_code(op.loc().file_id()),
+                        definition: (field.loc.offset(), field.loc.node_len()).into(),
+                    }));
+                }
+            }
+        }
+    }
+
     // A Subscription operation definition can only have **one** root level
     // field.
-    if subscription_operations.len() >= 1 {
-        let single_root_field: Vec<ApolloDiagnostic> = subscription_operations
+    if !subscriptions.is_empty() {
+        let single_root_field: Vec<ApolloDiagnostic> = subscriptions
             .iter()
             .filter_map(|op| {
                 let mut fields = op.fields(db.upcast()).as_ref().clone();
@@ -104,8 +173,8 @@ pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnost
     // defined schema root operation types.
     //
     //   * subscription operation - subscription root operation
-    if subscription_operations.len() >= 1 && db.schema().subscription(db.upcast()).is_none() {
-        let unsupported_ops: Vec<ApolloDiagnostic> = subscription_operations
+    if !subscriptions.is_empty() && db.schema().subscription(db.upcast()).is_none() {
+        let unsupported_ops: Vec<ApolloDiagnostic> = subscriptions
             .iter()
             .map(|op| {
                 let op_offset = op.loc().offset();
@@ -138,9 +207,21 @@ pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnost
         diagnostics.extend(unsupported_ops)
     }
 
+    diagnostics
+}
+
+pub fn validate_query_operations(
+    db: &dyn ValidationDatabase,
+    queries: Arc<Vec<Arc<hir::OperationDefinition>>>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    // All query, subscription and mutation operations must be against legally
+    // defined schema root operation types.
+    //
     //   * query operation - query root operation
-    if query_operations.len() >= 1 && db.schema().query(db.upcast()).is_none() {
-        let unsupported_ops: Vec<ApolloDiagnostic> = query_operations
+    if !queries.is_empty() && db.schema().query(db.upcast()).is_none() {
+        let unsupported_ops: Vec<ApolloDiagnostic> = queries
             .iter()
             .map(|op| {
                 let op_offset = op.loc().offset();
@@ -172,9 +253,21 @@ pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnost
         diagnostics.extend(unsupported_ops)
     }
 
+    diagnostics
+}
+
+pub fn validate_mutation_operations(
+    db: &dyn ValidationDatabase,
+    mutations: Arc<Vec<Arc<hir::OperationDefinition>>>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    // All query, subscription and mutation operations must be against legally
+    // defined schema root operation types.
+    //
     //   * mutation operation - mutation root operation
-    if mutation_operations.len() >= 1 && db.schema().mutation(db.upcast()).is_none() {
-        let unsupported_ops: Vec<ApolloDiagnostic> = mutation_operations
+    if !mutations.is_empty() && db.schema().mutation(db.upcast()).is_none() {
+        let unsupported_ops: Vec<ApolloDiagnostic> = mutations
             .iter()
             .map(|op| {
                 let op_offset = op.loc().offset();
@@ -207,35 +300,6 @@ pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnost
         diagnostics.extend(unsupported_ops)
     }
 
-    // Fields must exist on the type being queried.
-    for op in operations.iter() {
-        for selection in op.selection_set().selection() {
-            let obj_name = op.object_type(db.upcast()).map(|obj| obj.name().to_owned());
-            if let Selection::Field(field) = selection {
-                if field.ty(db.upcast()).is_none() {
-                    let offset = field.loc().offset();
-                    let len = field.loc().node_len();
-                    let field_name = field.name().into();
-                    let help = if let Some(obj_type) = obj_name {
-                        format!("`{}` is not defined on `{}` type", field_name, obj_type)
-                    } else {
-                        format!(
-                            "`{}` is not defined on the current {} root operation type.",
-                            field_name,
-                            op.operation_ty()
-                        )
-                    };
-                    diagnostics.push(ApolloDiagnostic::UndefinedField(UndefinedField {
-                        field: field_name,
-                        src: db.source_code(field.loc().file_id()),
-                        definition: (offset, len).into(),
-                        help,
-                    }))
-                }
-            }
-        }
-    }
-
     diagnostics
 }
 
@@ -305,7 +369,7 @@ union CatOrDog = Cat | Dog
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 5)
     }
@@ -354,7 +418,7 @@ type Cat implements Pet {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
         assert_eq!(diagnostics.len(), 1)
     }
@@ -442,7 +506,7 @@ type Cat implements Pet {
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic)
+            println!("{diagnostic}")
         }
 
         assert_eq!(diagnostics.len(), 2)
@@ -474,7 +538,7 @@ directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: j
 
         let diagnostics = compiler.validate();
         for diagnostic in &diagnostics {
-            println!("{}", diagnostic);
+            println!("{diagnostic}");
         }
 
         assert_eq!(diagnostics.len(), 2)
diff --git a/crates/apollo-compiler/src/validation/scalar.rs b/crates/apollo-compiler/src/validation/scalar.rs
index 2e77f6d..2e69400 100644
--- a/crates/apollo-compiler/src/validation/scalar.rs
+++ b/crates/apollo-compiler/src/validation/scalar.rs
@@ -1,42 +1,62 @@
+use std::sync::Arc;
+
 use crate::{
     diagnostics::{BuiltInScalarDefinition, ScalarSpecificationURL},
+    hir::{self, DirectiveLocation},
     ApolloDiagnostic, ValidationDatabase,
 };
 
 const BUILT_IN_SCALARS: [&str; 5] = ["Int", "Float", "Boolean", "String", "ID"];
 
-pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+pub fn validate_scalar_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
-    for (name, scalar) in db.scalars().iter() {
-        if let Some(loc) = scalar.loc() {
-            let offset = loc.offset();
-            let len = loc.node_len();
+    let defs = &db.type_system_definitions().scalars;
+    for def in defs.values() {
+        diagnostics.extend(db.validate_scalar_definition(def.clone()));
+    }
+
+    diagnostics
+}
 
-            // All built-in scalars must be omitted for brevity.
-            if BUILT_IN_SCALARS.contains(&&**name) && !scalar.is_built_in() {
-                diagnostics.push(ApolloDiagnostic::BuiltInScalarDefinition(
-                    BuiltInScalarDefinition {
+pub fn validate_scalar_definition(
+    db: &dyn ValidationDatabase,
+    scalar_def: Arc<hir::ScalarTypeDefinition>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+    let name = scalar_def.name();
+
+    if let Some(loc) = scalar_def.loc() {
+        let offset = loc.offset();
+        let len = loc.node_len();
+
+        // All built-in scalars must be omitted for brevity.
+        if BUILT_IN_SCALARS.contains(&name) && !scalar_def.is_built_in() {
+            diagnostics.push(ApolloDiagnostic::BuiltInScalarDefinition(
+                BuiltInScalarDefinition {
+                    scalar: (offset, len).into(),
+                    src: db.source_code(loc.file_id()),
+                },
+            ));
+        } else if !scalar_def.is_built_in() {
+            // Custom scalars must provide a scalar specification URL via the
+            // @specifiedBy directive
+            if !scalar_def
+                .directives()
+                .iter()
+                .any(|directive| directive.name() == "specifiedBy")
+            {
+                diagnostics.push(ApolloDiagnostic::ScalarSpecificationURL(
+                    ScalarSpecificationURL {
                         scalar: (offset, len).into(),
                         src: db.source_code(loc.file_id()),
                     },
-                ));
-            } else if !scalar.is_built_in() {
-                // Custom scalars must provide a scalar specification URL via the
-                // @specifiedBy directive
-                if !scalar
-                    .directives()
-                    .iter()
-                    .any(|directive| directive.name() == "specifiedBy")
-                {
-                    diagnostics.push(ApolloDiagnostic::ScalarSpecificationURL(
-                        ScalarSpecificationURL {
-                            scalar: (offset, len).into(),
-                            src: db.source_code(loc.file_id()),
-                        },
-                    ))
-                }
+                ))
             }
+
+            diagnostics.extend(
+                db.validate_directives(scalar_def.directives().to_vec(), DirectiveLocation::Scalar),
+            );
         }
     }
 
diff --git a/crates/apollo-compiler/src/validation/schema.rs b/crates/apollo-compiler/src/validation/schema.rs
index 2451bdd..c7313aa 100644
--- a/crates/apollo-compiler/src/validation/schema.rs
+++ b/crates/apollo-compiler/src/validation/schema.rs
@@ -1,16 +1,18 @@
-use std::collections::HashMap;
+use std::{collections::HashMap, sync::Arc};
 
 use crate::{
     diagnostics::{QueryRootOperationType, UniqueDefinition},
-    hir::RootOperationTypeDefinition,
-    ApolloDiagnostic, ValidationDatabase,
+    hir, ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+pub fn validate_schema_definition(
+    db: &dyn ValidationDatabase,
+    schema_def: Arc<hir::SchemaDefinition>,
+) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // A GraphQL schema must have a Query root operation.
-    if db.schema().query(db.upcast()).is_none() {
+    if schema_def.query(db.upcast()).is_none() {
         if let Some(loc) = db.schema().loc() {
             let offset = loc.offset();
             let len = loc.node_len();
@@ -22,12 +24,32 @@ pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
             ));
         }
     }
+    diagnostics.extend(
+        db.validate_root_operation_definitions(
+            schema_def.root_operation_type_definition().to_vec(),
+        ),
+    );
 
-    // All root operations in a schema definition must be unique.
-    //
-    // Return a Unique Operation Definition error in case of a duplicate name.
-    let mut seen: HashMap<String, &RootOperationTypeDefinition> = HashMap::new();
-    for op_type in db.schema().root_operation_type_definition().iter() {
+    diagnostics.extend(db.validate_directives(
+        schema_def.directives().to_vec(),
+        hir::DirectiveLocation::Schema,
+    ));
+
+    diagnostics
+}
+
+// All root operations in a schema definition must be unique.
+//
+// Return a Unique Operation Definition error in case of a duplicate name.
+pub fn validate_root_operation_definitions(
+    db: &dyn ValidationDatabase,
+    root_op_defs: Vec<hir::RootOperationTypeDefinition>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    let mut seen: HashMap<String, &hir::RootOperationTypeDefinition> = HashMap::new();
+
+    for op_type in root_op_defs.iter() {
         let name = op_type.named_type().name();
         if let Some(prev_def) = seen.get(&name) {
             if prev_def.loc().is_some() && op_type.loc().is_some() {
diff --git a/crates/apollo-compiler/src/validation/selection.rs b/crates/apollo-compiler/src/validation/selection.rs
new file mode 100644
index 0000000..80f4dfc
--- /dev/null
+++ b/crates/apollo-compiler/src/validation/selection.rs
@@ -0,0 +1,39 @@
+use std::sync::Arc;
+
+use crate::{hir, validation::ValidationDatabase, ApolloDiagnostic};
+
+pub fn validate_selection(
+    db: &dyn ValidationDatabase,
+    selection: Arc<Vec<hir::Selection>>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    for sel in selection.iter() {
+        match sel {
+            hir::Selection::Field(field) => {
+                if !field.selection_set().selection().is_empty() {
+                    diagnostics
+                        .extend(db.validate_selection(field.selection_set().selection.clone()))
+                }
+                diagnostics.extend(db.validate_field(field.clone()));
+            }
+            hir::Selection::FragmentSpread(frag) => diagnostics.extend(db.validate_directives(
+                frag.directives().to_vec(),
+                hir::DirectiveLocation::FragmentSpread,
+            )),
+            hir::Selection::InlineFragment(inline) => diagnostics.extend(db.validate_directives(
+                inline.directives().to_vec(),
+                hir::DirectiveLocation::InlineFragment,
+            )),
+        }
+    }
+
+    diagnostics
+}
+
+pub fn validate_selection_set(
+    db: &dyn ValidationDatabase,
+    selection_set: hir::SelectionSet,
+) -> Vec<ApolloDiagnostic> {
+    db.validate_selection(selection_set.selection)
+}
diff --git a/crates/apollo-compiler/src/validation/subscription.rs b/crates/apollo-compiler/src/validation/subscription.rs
deleted file mode 100644
index b92cbc6..0000000
--- a/crates/apollo-compiler/src/validation/subscription.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use crate::{
-    diagnostics::IntrospectionField, hir::Selection, ApolloDiagnostic, FileId, ValidationDatabase,
-};
-
-pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
-    let subscription_operations = db.upcast().subscription_operations(file_id);
-
-    // Subscription fields must not have an introspection field in the selection set.
-    //
-    // Return an Introspection Field error in case of an introspection field existing as the root field in the set.
-    for op in subscription_operations.iter() {
-        for selection in op.selection_set().selection() {
-            if let Selection::Field(field) = selection {
-                if field.is_introspection() {
-                    diagnostics.push(ApolloDiagnostic::IntrospectionField(IntrospectionField {
-                        field: field.name().into(),
-                        src: db.source_code(op.loc().file_id()),
-                        definition: (field.loc.offset(), field.loc.node_len()).into(),
-                    }));
-                }
-            }
-        }
-    }
-
-    diagnostics
-}
diff --git a/crates/apollo-compiler/src/validation/union_.rs b/crates/apollo-compiler/src/validation/union_.rs
index 7a6a7bf..eca73f6 100644
--- a/crates/apollo-compiler/src/validation/union_.rs
+++ b/crates/apollo-compiler/src/validation/union_.rs
@@ -1,60 +1,75 @@
-use std::collections::HashMap;
+use std::{collections::HashMap, sync::Arc};
 
 use crate::{
     diagnostics::{ObjectType, UndefinedDefinition, UniqueDefinition},
-    hir::{TypeDefinition, UnionMember},
+    hir::{self, TypeDefinition, UnionMember},
     ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+pub fn validate_union_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
-    for union_def in db.unions().values() {
-        let mut seen: HashMap<&str, &UnionMember> = HashMap::new();
-        for union_member in union_def.union_members().iter() {
-            let name = union_member.name();
-            let offset = union_member.loc().offset();
-            let len = union_member.loc().node_len();
-            // A Union type must include one or more unique member types.
-            //
-            // Return a Unique Value error in case of a duplicate member.
-            if let Some(prev_def) = seen.get(&name) {
-                let prev_offset = prev_def.loc().offset();
-                let prev_node_len = prev_def.loc().node_len();
-
-                diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
-                    name: name.into(),
-                    ty: "union member".into(),
-                    src: db.source_code(prev_def.loc().file_id()),
-                    original_definition: (prev_offset, prev_node_len).into(),
-                    redefined_definition: (offset, len).into(),
-                    help: Some(format!(
-                        "`{name}` must only be defined once in this document."
-                    )),
-                }));
-            } else {
-                seen.insert(name, union_member);
-            }
+    let defs = &db.type_system_definitions().unions;
+    for def in defs.values() {
+        diagnostics.extend(db.validate_union_definition(def.clone()));
+    }
+
+    diagnostics
+}
 
-            match db.upcast().find_type_definition_by_name(name.to_string()) {
-                None => {
-                    // Union member must be defined.
-                    diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
-                        ty: name.into(),
-                        src: db.source_code(union_member.loc().file_id()),
-                        definition: (offset, len).into(),
-                    }))
-                }
-                Some(TypeDefinition::ObjectTypeDefinition { .. }) => {} // good
-                Some(ty) => {
-                    // Union member must be of object type.
-                    diagnostics.push(ApolloDiagnostic::ObjectType(ObjectType {
-                        name: name.into(),
-                        ty: ty.kind(),
-                        src: db.source_code(union_member.loc().file_id()),
-                        definition: (offset, len).into(),
-                    }))
-                }
+pub fn validate_union_definition(
+    db: &dyn ValidationDatabase,
+    union_def: Arc<hir::UnionTypeDefinition>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = db.validate_directives(
+        union_def.directives().to_vec(),
+        hir::DirectiveLocation::Union,
+    );
+
+    let mut seen: HashMap<&str, &UnionMember> = HashMap::new();
+    for union_member in union_def.union_members().iter() {
+        let name = union_member.name();
+        let offset = union_member.loc().offset();
+        let len = union_member.loc().node_len();
+        // A Union type must include one or more unique member types.
+        //
+        // Return a Unique Value error in case of a duplicate member.
+        if let Some(prev_def) = seen.get(&name) {
+            let prev_offset = prev_def.loc().offset();
+            let prev_node_len = prev_def.loc().node_len();
+
+            diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
+                name: name.into(),
+                ty: "union member".into(),
+                src: db.source_code(prev_def.loc().file_id()),
+                original_definition: (prev_offset, prev_node_len).into(),
+                redefined_definition: (offset, len).into(),
+                help: Some(format!(
+                    "`{name}` must only be defined once in this document."
+                )),
+            }));
+        } else {
+            seen.insert(name, union_member);
+        }
+
+        match db.upcast().find_type_definition_by_name(name.to_string()) {
+            None => {
+                // Union member must be defined.
+                diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
+                    ty: name.into(),
+                    src: db.source_code(union_member.loc().file_id()),
+                    definition: (offset, len).into(),
+                }))
+            }
+            Some(TypeDefinition::ObjectTypeDefinition { .. }) => {} // good
+            Some(ty) => {
+                // Union member must be of object type.
+                diagnostics.push(ApolloDiagnostic::ObjectType(ObjectType {
+                    name: name.into(),
+                    ty: ty.kind(),
+                    src: db.source_code(union_member.loc().file_id()),
+                    definition: (offset, len).into(),
+                }))
             }
         }
     }
diff --git a/crates/apollo-compiler/src/validation/validation_db.rs b/crates/apollo-compiler/src/validation/validation_db.rs
index 6648de4..dd7bfc7 100644
--- a/crates/apollo-compiler/src/validation/validation_db.rs
+++ b/crates/apollo-compiler/src/validation/validation_db.rs
@@ -1,21 +1,17 @@
-use std::{
-    collections::{HashMap, HashSet},
-    sync::Arc,
-};
-
-use miette::SourceSpan;
+use std::sync::Arc;
 
 use crate::{
     database::db::Upcast,
-    diagnostics::{UndefinedDefinition, UniqueArgument, UnsupportedLocation},
-    hir,
+    hir::*,
     validation::{
-        directive, enum_, input_object, interface, object, operation, scalar, schema, subscription,
-        union_, unused_variable,
+        argument, directive, enum_, fragment, input_object, interface, object, operation, scalar,
+        schema, selection, union_, variable,
     },
     ApolloDiagnostic, AstDatabase, FileId, HirDatabase, InputDatabase,
 };
 
+use super::field;
+
 #[salsa::query_group(ValidationStorage)]
 pub trait ValidationDatabase:
     Upcast<dyn HirDatabase> + InputDatabase + AstDatabase + HirDatabase
@@ -23,474 +19,158 @@ pub trait ValidationDatabase:
     /// Validate all documents.
     fn validate(&self) -> Vec<ApolloDiagnostic>;
 
-    /// Validate the schema, combined of all schema documents known to the compiler.
-    fn validate_schema(&self) -> Vec<ApolloDiagnostic>;
-    fn validate_schema_definition(&self) -> Vec<ApolloDiagnostic>;
-    fn validate_scalar_definitions(&self) -> Vec<ApolloDiagnostic>;
-    fn validate_enum_definitions(&self) -> Vec<ApolloDiagnostic>;
-    fn validate_union_definitions(&self) -> Vec<ApolloDiagnostic>;
-    fn validate_interface_definitions(&self) -> Vec<ApolloDiagnostic>;
-    fn validate_directive_definitions(&self) -> Vec<ApolloDiagnostic>;
-    fn validate_input_object_definitions(&self) -> Vec<ApolloDiagnostic>;
-    fn validate_object_type_definitions(&self) -> Vec<ApolloDiagnostic>;
+    /// Validate the type system, combined of all type system documents known to
+    /// the compiler.
+    fn validate_type_system(&self) -> Vec<ApolloDiagnostic>;
 
-    /// Validate an executable document.
+    /// Validate the corresonding executable document.
     fn validate_executable(&self, file_id: FileId) -> Vec<ApolloDiagnostic>;
-    fn validate_operation_definitions(&self, file_id: FileId) -> Vec<ApolloDiagnostic>;
-    fn validate_subscription_operations(&self, file_id: FileId) -> Vec<ApolloDiagnostic>;
-    fn validate_unused_variable(&self, file_id: FileId) -> Vec<ApolloDiagnostic>;
 
-    fn check_directive_definition(
-        &self,
-        def: Arc<hir::DirectiveDefinition>,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_object_type_definition(
-        &self,
-        def: Arc<hir::ObjectTypeDefinition>,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_interface_type_definition(
-        &self,
-        def: Arc<hir::InterfaceTypeDefinition>,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_scalar_type_definition(
-        &self,
-        def: Arc<hir::ScalarTypeDefinition>,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_union_type_definition(
-        &self,
-        def: Arc<hir::UnionTypeDefinition>,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_enum_type_definition(
-        &self,
-        def: Arc<hir::EnumTypeDefinition>,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_enum_value(&self, enum_val: hir::EnumValueDefinition) -> Vec<ApolloDiagnostic>;
-    fn check_input_object_type_definition(
-        &self,
-        def: Arc<hir::InputObjectTypeDefinition>,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_schema_definition(&self, def: Arc<hir::SchemaDefinition>) -> Vec<ApolloDiagnostic>;
-    fn check_selection_set(&self, selection_set: hir::SelectionSet) -> Vec<ApolloDiagnostic>;
-    fn check_selection(&self, selection: Vec<hir::Selection>) -> Vec<ApolloDiagnostic>;
-    fn check_arguments_definition(
-        &self,
-        arguments_def: hir::ArgumentsDefinition,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_field_definition(&self, field: hir::FieldDefinition) -> Vec<ApolloDiagnostic>;
-    fn check_input_values(
-        &self,
-        input_values: Arc<Vec<hir::InputValueDefinition>>,
-        dir_loc: hir::DirectiveLocation,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_db_definitions(&self) -> Vec<ApolloDiagnostic>;
-    fn check_directives(
-        &self,
-        dirs: Vec<hir::Directive>,
-        dir_loc: hir::DirectiveLocation,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_directive(
-        &self,
-        schema: hir::Directive,
-        dir_loc: hir::DirectiveLocation,
-    ) -> Vec<ApolloDiagnostic>;
-    fn check_arguments(&self, schema: Vec<hir::Argument>) -> Vec<ApolloDiagnostic>;
-    fn check_field(&self, field: Arc<hir::Field>) -> Vec<ApolloDiagnostic>;
-    fn check_variable_definitions(
+    #[salsa::invoke(schema::validate_schema_definition)]
+    fn validate_schema_definition(&self, def: Arc<SchemaDefinition>) -> Vec<ApolloDiagnostic>;
+
+    #[salsa::invoke(schema::validate_root_operation_definitions)]
+    fn validate_root_operation_definitions(
         &self,
-        variables: Arc<Vec<hir::VariableDefinition>>,
+        defs: Vec<RootOperationTypeDefinition>,
     ) -> Vec<ApolloDiagnostic>;
-}
-
-pub fn check_directive_definition(
-    db: &dyn ValidationDatabase,
-    directive: Arc<hir::DirectiveDefinition>,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
-
-    diagnostics.extend(db.check_arguments_definition(directive.arguments.clone()));
-
-    diagnostics
-}
-
-pub fn check_object_type_definition(
-    db: &dyn ValidationDatabase,
-    object_type: Arc<hir::ObjectTypeDefinition>,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
 
-    // TODO: validate extensions
-    for field in object_type.fields_definition() {
-        diagnostics.extend(db.check_field_definition(field.clone()));
-    }
-
-    diagnostics
-}
-
-pub fn check_interface_type_definition(
-    db: &dyn ValidationDatabase,
-    interface_type: Arc<hir::InterfaceTypeDefinition>,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
-
-    // TODO: validate extensions
-    for field in interface_type.fields_definition() {
-        diagnostics.extend(db.check_field_definition(field.clone()));
-    }
-
-    diagnostics
-}
-
-pub fn check_scalar_type_definition(
-    _db: &dyn ValidationDatabase,
-    _union_type: Arc<hir::ScalarTypeDefinition>,
-) -> Vec<ApolloDiagnostic> {
-    // TODO: validate extensions
-    vec![]
-}
-
-pub fn check_union_type_definition(
-    _db: &dyn ValidationDatabase,
-    _union_type: Arc<hir::UnionTypeDefinition>,
-) -> Vec<ApolloDiagnostic> {
-    // TODO: validate extensions
-    vec![]
-}
-
-pub fn check_enum_type_definition(
-    db: &dyn ValidationDatabase,
-    enum_def: Arc<hir::EnumTypeDefinition>,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
-
-    for val in enum_def.enum_values_definition() {
-        diagnostics.extend(db.check_enum_value(val.clone()))
-    }
-
-    diagnostics
-}
-
-pub fn check_enum_value(
-    db: &dyn ValidationDatabase,
-    enum_val: hir::EnumValueDefinition,
-) -> Vec<ApolloDiagnostic> {
-    db.check_directives(
-        enum_val.directives().to_vec(),
-        hir::DirectiveLocation::EnumValue,
-    )
-}
+    #[salsa::invoke(scalar::validate_scalar_definitions)]
+    fn validate_scalar_definitions(&self) -> Vec<ApolloDiagnostic>;
 
-pub fn check_input_object_type_definition(
-    db: &dyn ValidationDatabase,
-    input_obj: Arc<hir::InputObjectTypeDefinition>,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
+    #[salsa::invoke(scalar::validate_scalar_definition)]
+    fn validate_scalar_definition(
+        &self,
+        scalar_def: Arc<ScalarTypeDefinition>,
+    ) -> Vec<ApolloDiagnostic>;
 
-    diagnostics.extend(db.check_input_values(
-        input_obj.input_fields_definition.clone(),
-        hir::DirectiveLocation::InputFieldDefinition,
-    ));
+    #[salsa::invoke(enum_::validate_enum_definitions)]
+    fn validate_enum_definitions(&self) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-}
+    #[salsa::invoke(enum_::validate_enum_definition)]
+    fn validate_enum_definition(&self, def: Arc<EnumTypeDefinition>) -> Vec<ApolloDiagnostic>;
 
-pub fn check_schema_definition(
-    db: &dyn ValidationDatabase,
-    schema_def: Arc<hir::SchemaDefinition>,
-) -> Vec<ApolloDiagnostic> {
-    db.check_directives(
-        schema_def.directives().to_vec(),
-        hir::DirectiveLocation::Schema,
-    )
-}
+    #[salsa::invoke(enum_::validate_enum_value)]
+    fn validate_enum_value(&self, def: EnumValueDefinition) -> Vec<ApolloDiagnostic>;
 
-pub fn check_selection_set(
-    db: &dyn ValidationDatabase,
-    selection_set: hir::SelectionSet,
-) -> Vec<ApolloDiagnostic> {
-    db.check_selection((*selection_set.selection).clone())
-}
+    #[salsa::invoke(union_::validate_union_definitions)]
+    fn validate_union_definitions(&self) -> Vec<ApolloDiagnostic>;
 
-pub fn check_selection(
-    db: &dyn ValidationDatabase,
-    selection: Vec<hir::Selection>,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
+    #[salsa::invoke(union_::validate_union_definition)]
+    fn validate_union_definition(&self, def: Arc<UnionTypeDefinition>) -> Vec<ApolloDiagnostic>;
 
-    for sel in selection {
-        match sel {
-            hir::Selection::Field(field) => {
-                if !field.selection_set().selection().is_empty() {
-                    diagnostics
-                        .extend(db.check_selection((*field.selection_set().selection).clone()))
-                }
-                diagnostics.extend(db.check_field(field));
-            }
-            hir::Selection::FragmentSpread(frag) => diagnostics.extend(db.check_directives(
-                frag.directives().to_vec(),
-                hir::DirectiveLocation::FragmentSpread,
-            )),
-            hir::Selection::InlineFragment(inline) => diagnostics.extend(db.check_directives(
-                inline.directives().to_vec(),
-                hir::DirectiveLocation::InlineFragment,
-            )),
-        }
-    }
-
-    diagnostics
-}
+    #[salsa::invoke(interface::validate_interface_definitions)]
+    fn validate_interface_definitions(&self) -> Vec<ApolloDiagnostic>;
 
-pub fn check_arguments_definition(
-    db: &dyn ValidationDatabase,
-    arguments_def: hir::ArgumentsDefinition,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
+    #[salsa::invoke(interface::validate_interface_definition)]
+    fn validate_interface_definition(
+        &self,
+        def: Arc<InterfaceTypeDefinition>,
+    ) -> Vec<ApolloDiagnostic>;
 
-    diagnostics.extend(db.check_input_values(
-        arguments_def.input_values,
-        hir::DirectiveLocation::ArgumentDefinition,
-    ));
+    #[salsa::invoke(interface::validate_implements_interfaces)]
+    fn validate_implements_interfaces(
+        &self,
+        impl_interfaces: Vec<ImplementsInterface>,
+    ) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-}
+    #[salsa::invoke(directive::validate_directive_definitions)]
+    fn validate_directive_definitions(&self) -> Vec<ApolloDiagnostic>;
 
-pub fn check_field_definition(
-    db: &dyn ValidationDatabase,
-    field: hir::FieldDefinition,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
+    #[salsa::invoke(directive::validate_directives)]
+    fn validate_directives(
+        &self,
+        dirs: Vec<Directive>,
+        loc: DirectiveLocation,
+    ) -> Vec<ApolloDiagnostic>;
 
-    diagnostics.extend(db.check_directives(
-        field.directives().to_vec(),
-        hir::DirectiveLocation::FieldDefinition,
-    ));
+    #[salsa::invoke(input_object::validate_input_object_definitions)]
+    fn validate_input_object_definitions(&self) -> Vec<ApolloDiagnostic>;
 
-    diagnostics.extend(db.check_arguments_definition(field.arguments));
+    #[salsa::invoke(input_object::validate_input_object_definition)]
+    fn validate_input_object_definition(
+        &self,
+        def: Arc<InputObjectTypeDefinition>,
+    ) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-}
+    #[salsa::invoke(input_object::validate_input_values)]
+    fn validate_input_values(
+        &self,
+        vals: Arc<Vec<InputValueDefinition>>,
+        dir_loc: DirectiveLocation,
+    ) -> Vec<ApolloDiagnostic>;
 
-pub fn check_input_values(
-    db: &dyn ValidationDatabase,
-    input_values: Arc<Vec<hir::InputValueDefinition>>,
-    // directive location depends on parent node location, so we pass this down from parent
-    dir_loc: hir::DirectiveLocation,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
-    let mut seen: HashMap<&str, &hir::InputValueDefinition> = HashMap::new();
-
-    for input_value in input_values.iter() {
-        diagnostics.extend(db.check_directives(input_value.directives().to_vec(), dir_loc));
-        let name = input_value.name();
-        if let Some(prev_arg) = seen.get(name) {
-            let prev_offset = prev_arg.loc().unwrap().offset();
-            let prev_node_len = prev_arg.loc().unwrap().node_len();
-
-            let current_offset = input_value.loc().unwrap().offset();
-            let current_node_len = input_value.loc().unwrap().node_len();
-
-            diagnostics.push(ApolloDiagnostic::UniqueArgument(UniqueArgument {
-                name: name.into(),
-                src: db.source_code(prev_arg.loc().unwrap().file_id()),
-                original_definition: (prev_offset, prev_node_len).into(),
-                redefined_definition: (current_offset, current_node_len).into(),
-                help: Some(format!("`{name}` argument must only be defined once.")),
-            }));
-        } else {
-            seen.insert(name, input_value);
-        }
-    }
+    #[salsa::invoke(object::validate_object_type_definitions)]
+    fn validate_object_type_definitions(&self) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-}
+    #[salsa::invoke(operation::validate_subscription_operations)]
+    fn validate_subscription_operations(
+        &self,
+        defs: Arc<Vec<Arc<OperationDefinition>>>,
+    ) -> Vec<ApolloDiagnostic>;
 
-pub fn check_variable_definitions(
-    db: &dyn ValidationDatabase,
-    variables: Arc<Vec<hir::VariableDefinition>>,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
+    #[salsa::invoke(operation::validate_query_operations)]
+    fn validate_query_operations(
+        &self,
+        defs: Arc<Vec<Arc<OperationDefinition>>>,
+    ) -> Vec<ApolloDiagnostic>;
 
-    for variable in variables.iter() {
-        diagnostics.extend(db.check_directives(
-            variable.directives().to_vec(),
-            hir::DirectiveLocation::VariableDefinition,
-        ));
-    }
+    #[salsa::invoke(operation::validate_mutation_operations)]
+    fn validate_mutation_operations(
+        &self,
+        mutations: Arc<Vec<Arc<OperationDefinition>>>,
+    ) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-}
+    #[salsa::invoke(object::validate_object_type_definition)]
+    fn validate_object_type_definition(
+        &self,
+        def: Arc<ObjectTypeDefinition>,
+    ) -> Vec<ApolloDiagnostic>;
 
-pub fn check_db_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
-    let type_system = db.type_system_definitions();
-    let hir::TypeSystemDefinitions {
-        schema,
-        scalars,
-        objects,
-        interfaces,
-        unions,
-        enums,
-        input_objects,
-        directives,
-    } = &*type_system;
-
-    for def in db.all_operations().iter() {
-        diagnostics
-            .extend(db.check_directives(def.directives().to_vec(), def.operation_ty().into()));
-        diagnostics.extend(db.check_variable_definitions(def.variables.clone()));
-        diagnostics.extend(db.check_selection_set(def.selection_set().clone()));
-    }
-    for def in db.all_fragments().values() {
-        diagnostics.extend(db.check_directives(
-            def.directives().to_vec(),
-            hir::DirectiveLocation::FragmentDefinition,
-        ));
-        diagnostics.extend(db.check_selection_set(def.selection_set().clone()));
-    }
-    for def in directives.values() {
-        diagnostics.extend(db.check_directive_definition(def.clone()));
-    }
-    for def in scalars.values() {
-        diagnostics
-            .extend(db.check_directives(def.directives().to_vec(), hir::DirectiveLocation::Scalar));
-        diagnostics.extend(db.check_scalar_type_definition(def.clone()));
-    }
-    for def in objects.values() {
-        diagnostics
-            .extend(db.check_directives(def.directives().to_vec(), hir::DirectiveLocation::Object));
-        diagnostics.extend(db.check_object_type_definition(def.clone()));
-    }
-    for def in interfaces.values() {
-        diagnostics.extend(
-            db.check_directives(def.directives().to_vec(), hir::DirectiveLocation::Interface),
-        );
-        diagnostics.extend(db.check_interface_type_definition(def.clone()));
-        // TODO: validate extensions
-    }
-    for def in unions.values() {
-        diagnostics
-            .extend(db.check_directives(def.directives().to_vec(), hir::DirectiveLocation::Union));
-        diagnostics.extend(db.check_union_type_definition(def.clone()));
-        // TODO: validate extensions
-    }
-    for def in enums.values() {
-        diagnostics
-            .extend(db.check_directives(def.directives().to_vec(), hir::DirectiveLocation::Enum));
-        diagnostics.extend(db.check_enum_type_definition(def.clone()));
-        // TODO: validate extensions
-    }
-    for def in input_objects.values() {
-        diagnostics.extend(db.check_directives(
-            def.directives().to_vec(),
-            hir::DirectiveLocation::InputObject,
-        ));
-        diagnostics.extend(db.check_input_object_type_definition(def.clone()));
-        // TODO: validate extensions
-    }
-    diagnostics.extend(db.check_schema_definition(schema.clone()));
+    #[salsa::invoke(field::validate_field_definitions)]
+    fn validate_field_definitions(&self, fields: Vec<FieldDefinition>) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-}
+    #[salsa::invoke(field::validate_field_definition)]
+    fn validate_field_definition(&self, field: FieldDefinition) -> Vec<ApolloDiagnostic>;
 
-pub fn check_field(db: &dyn ValidationDatabase, field: Arc<hir::Field>) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
+    #[salsa::invoke(field::validate_field)]
+    fn validate_field(&self, field: Arc<Field>) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-        .extend(db.check_directives(field.directives().to_vec(), hir::DirectiveLocation::Field));
-    diagnostics.extend(db.check_arguments(field.arguments().to_vec()));
+    #[salsa::invoke(argument::validate_arguments_definition)]
+    fn validate_arguments_definition(
+        &self,
+        def: ArgumentsDefinition,
+        loc: DirectiveLocation,
+    ) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-}
+    #[salsa::invoke(argument::validate_arguments)]
+    fn validate_arguments(&self, arg: Vec<Argument>) -> Vec<ApolloDiagnostic>;
 
-pub fn check_directives(
-    db: &dyn ValidationDatabase,
-    dirs: Vec<hir::Directive>,
-    dir_loc: hir::DirectiveLocation,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
-    for dir in dirs {
-        diagnostics.extend(db.check_directive(dir.clone(), dir_loc));
-    }
-    diagnostics
-}
+    #[salsa::invoke(operation::validate_operation_definitions)]
+    fn validate_operation_definitions(&self, file_id: FileId) -> Vec<ApolloDiagnostic>;
 
-pub fn check_directive(
-    db: &dyn ValidationDatabase,
-    directive: hir::Directive,
-    dir_loc: hir::DirectiveLocation,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
+    #[salsa::invoke(fragment::validate_fragment_definitions)]
+    fn validate_fragment_definitions(&self, file_id: FileId) -> Vec<ApolloDiagnostic>;
 
-    diagnostics.extend(db.check_arguments(directive.arguments().to_vec()));
-
-    let name = directive.name();
-    let loc = directive.loc();
-    let offset = loc.offset();
-    let len = loc.node_len();
-
-    if let Some(directive) = db.find_directive_definition_by_name(name.into()) {
-        let directive_def_loc = directive
-            .loc
-            .map(|loc| SourceSpan::new(loc.offset().into(), loc.node_len().into()));
-        let allowed_loc: HashSet<hir::DirectiveLocation> =
-            HashSet::from_iter(directive.directive_locations().iter().cloned());
-        if !allowed_loc.contains(&dir_loc) {
-            diagnostics.push(ApolloDiagnostic::UnsupportedLocation(UnsupportedLocation {
-                ty: name.into(),
-                dir_loc,
-                src: db.source_code(loc.file_id()),
-                directive: (offset, len).into(),
-                directive_def: directive_def_loc,
-                help: Some("the directive must be used in a location that the service has declared support for".into()),
-            }))
-        }
-    } else {
-        diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
-            ty: name.into(),
-            src: db.source_code(loc.file_id()),
-            definition: (offset, len).into(),
-        }))
-    }
+    #[salsa::invoke(selection::validate_selection_set)]
+    fn validate_selection_set(&self, sel_set: SelectionSet) -> Vec<ApolloDiagnostic>;
 
-    diagnostics
-}
+    #[salsa::invoke(selection::validate_selection)]
+    fn validate_selection(&self, sel: Arc<Vec<Selection>>) -> Vec<ApolloDiagnostic>;
 
-pub fn check_arguments(
-    db: &dyn ValidationDatabase,
-    arguments: Vec<hir::Argument>,
-) -> Vec<ApolloDiagnostic> {
-    let mut diagnostics = Vec::new();
-    let mut seen: HashMap<&str, &hir::Argument> = HashMap::new();
-
-    for argument in &arguments {
-        let name = argument.name();
-        if let Some(prev_arg) = seen.get(name) {
-            let prev_offset = prev_arg.loc().offset();
-            let prev_node_len = prev_arg.loc().node_len();
-
-            let current_offset = argument.loc().offset();
-            let current_node_len = argument.loc().node_len();
-
-            diagnostics.push(ApolloDiagnostic::UniqueArgument(UniqueArgument {
-                name: name.into(),
-                src: db.source_code(prev_arg.loc().file_id()),
-                original_definition: (prev_offset, prev_node_len).into(),
-                redefined_definition: (current_offset, current_node_len).into(),
-                help: Some(format!("`{name}` argument must only be provided once.")),
-            }));
-        } else {
-            seen.insert(name, argument);
-        }
-    }
+    #[salsa::invoke(variable::validate_variable_definitions)]
+    fn validate_variable_definitions(&self, defs: Vec<VariableDefinition>)
+        -> Vec<ApolloDiagnostic>;
 
-    diagnostics
+    #[salsa::invoke(variable::validate_unused_variables)]
+    fn validate_unused_variable(&self, file_id: FileId) -> Vec<ApolloDiagnostic>;
 }
 
 pub fn validate(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
     diagnostics.extend(db.syntax_errors());
 
-    diagnostics.extend(db.validate_schema());
-    diagnostics.extend(db.check_db_definitions());
+    diagnostics.extend(db.validate_type_system());
 
     for file_id in db.executable_definition_files() {
         diagnostics.extend(db.validate_executable(file_id));
@@ -499,10 +179,10 @@ pub fn validate(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     diagnostics
 }
 
-pub fn validate_schema(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+pub fn validate_type_system(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
-    diagnostics.extend(db.validate_schema_definition());
+    diagnostics.extend(db.validate_schema_definition(db.type_system_definitions().schema.clone()));
 
     diagnostics.extend(db.validate_scalar_definitions());
     diagnostics.extend(db.validate_enum_definitions());
@@ -520,73 +200,8 @@ pub fn validate_executable(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<
     let mut diagnostics = Vec::new();
 
     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.extend(db.validate_subscription_operations(file_id));
 
     diagnostics
 }
-
-pub fn validate_schema_definition(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    schema::check(db)
-}
-
-pub fn validate_scalar_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    scalar::check(db)
-}
-
-pub fn validate_enum_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    enum_::check(db)
-}
-
-pub fn validate_union_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    union_::check(db)
-}
-
-pub fn validate_interface_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    interface::check(db)
-}
-
-pub fn validate_directive_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    directive::check(db)
-}
-
-pub fn validate_input_object_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    input_object::check(db)
-}
-
-pub fn validate_object_type_definitions(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
-    object::check(db)
-}
-
-pub fn validate_operation_definitions(
-    db: &dyn ValidationDatabase,
-    file_id: FileId,
-) -> Vec<ApolloDiagnostic> {
-    operation::check(db, file_id)
-}
-
-pub fn validate_unused_variable(
-    db: &dyn ValidationDatabase,
-    file_id: FileId,
-) -> Vec<ApolloDiagnostic> {
-    unused_variable::check(db, file_id)
-}
-
-pub fn validate_subscription_operations(
-    db: &dyn ValidationDatabase,
-    file_id: FileId,
-) -> Vec<ApolloDiagnostic> {
-    subscription::check(db, file_id)
-}
-
-// #[salsa::query_group(ValidationStorage)]
-// pub trait Validation: Document + Inputs + DocumentParser + Definitions {
-//     fn validate(&self) -> Arc<Vec<ApolloDiagnostic>>;
-// }
-//
-// pub fn validate(db: &dyn Validation) -> Arc<Vec<ApolloDiagnostic>> {
-//     let mut diagnostics = Vec::new();
-//     diagnostics.extend(schema::check(db));
-//
-//     Arc::new(diagnostics)
-// }
diff --git a/crates/apollo-compiler/src/validation/unused_variable.rs b/crates/apollo-compiler/src/validation/variable.rs
similarity index 87%
rename from crates/apollo-compiler/src/validation/unused_variable.rs
rename to crates/apollo-compiler/src/validation/variable.rs
index 73c888c..4688840 100644
--- a/crates/apollo-compiler/src/validation/unused_variable.rs
+++ b/crates/apollo-compiler/src/validation/variable.rs
@@ -2,14 +2,34 @@ use std::collections::HashSet;
 
 use crate::{
     diagnostics::{ApolloDiagnostic, UndefinedDefinition, UnusedVariable},
+    hir,
     validation::ValidationSet,
     FileId, ValidationDatabase,
 };
 
+pub fn validate_variable_definitions(
+    db: &dyn ValidationDatabase,
+    variables: Vec<hir::VariableDefinition>,
+) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+
+    for variable in variables.iter() {
+        diagnostics.extend(db.validate_directives(
+            variable.directives().to_vec(),
+            hir::DirectiveLocation::VariableDefinition,
+        ));
+    }
+
+    diagnostics
+}
+
 // check in scope
 // check in use
 // compare the two
-pub fn check(db: &dyn ValidationDatabase, file_id: FileId) -> Vec<ApolloDiagnostic> {
+pub fn validate_unused_variables(
+    db: &dyn ValidationDatabase,
+    file_id: FileId,
+) -> Vec<ApolloDiagnostic> {
     db.operations(file_id)
         .iter()
         .flat_map(|op| {
@@ -106,7 +126,7 @@ type Products {
         let diagnostics = compiler.validate();
 
         for error in &diagnostics {
-            println!("{}", error)
+            println!("{error}")
         }
 
         assert_eq!(diagnostics.len(), 2);
@@ -138,7 +158,7 @@ type Product {
         let diagnostics = compiler.validate();
 
         for error in diagnostics {
-            println!("{}", error)
+            println!("{error}")
         }
     }
 
@@ -179,7 +199,7 @@ type Product {
         let diagnostics = compiler.validate();
 
         for error in &diagnostics {
-            println!("{}", error)
+            println!("{error}")
         }
 
         assert_eq!(diagnostics.len(), 2);
diff --git a/crates/apollo-compiler/test_data/diagnostics/0050_subscription_operation_with_root_introspection_field.graphql b/crates/apollo-compiler/test_data/diagnostics/0051_subscription_operation_with_root_introspection_field.graphql
similarity index 100%
rename from crates/apollo-compiler/test_data/diagnostics/0050_subscription_operation_with_root_introspection_field.graphql
rename to crates/apollo-compiler/test_data/diagnostics/0051_subscription_operation_with_root_introspection_field.graphql
diff --git a/crates/apollo-compiler/test_data/diagnostics/0050_subscription_operation_with_root_introspection_field.txt b/crates/apollo-compiler/test_data/diagnostics/0051_subscription_operation_with_root_introspection_field.txt
similarity index 100%
rename from crates/apollo-compiler/test_data/diagnostics/0050_subscription_operation_with_root_introspection_field.txt
rename to crates/apollo-compiler/test_data/diagnostics/0051_subscription_operation_with_root_introspection_field.txt
diff --git a/crates/apollo-compiler/test_data/diagnostics/0051_undefined_directive.graphql b/crates/apollo-compiler/test_data/diagnostics/0052_undefined_directive.graphql
similarity index 100%
rename from crates/apollo-compiler/test_data/diagnostics/0051_undefined_directive.graphql
rename to crates/apollo-compiler/test_data/diagnostics/0052_undefined_directive.graphql
diff --git a/crates/apollo-compiler/test_data/diagnostics/0051_undefined_directive.txt b/crates/apollo-compiler/test_data/diagnostics/0052_undefined_directive.txt
similarity index 100%
rename from crates/apollo-compiler/test_data/diagnostics/0051_undefined_directive.txt
rename to crates/apollo-compiler/test_data/diagnostics/0052_undefined_directive.txt
diff --git a/crates/apollo-encoder/src/argument.rs b/crates/apollo-encoder/src/argument.rs
index b9a6637..acb4e7e 100644
--- a/crates/apollo-encoder/src/argument.rs
+++ b/crates/apollo-encoder/src/argument.rs
@@ -69,16 +69,16 @@ impl fmt::Display for ArgumentsDefinition {
             //   ): Type
             if input_val_def.description.is_some() {
                 if i != self.input_values.len() - 1 {
-                    write!(f, "{},", input_val_def)?;
+                    write!(f, "{input_val_def},")?;
                 } else {
-                    writeln!(f, "{}", input_val_def)?;
+                    writeln!(f, "{input_val_def}")?;
                 }
             // with no descriptions we single line input value definitions:
             //   two(argument: InputType!): Type
             } else if i != self.input_values.len() - 1 {
-                write!(f, "{}, ", input_val_def)?;
+                write!(f, "{input_val_def}, ")?;
             } else {
-                write!(f, "{}", input_val_def)?;
+                write!(f, "{input_val_def}")?;
             }
         }
 
diff --git a/crates/apollo-encoder/src/directive.rs b/crates/apollo-encoder/src/directive.rs
index f3e5b21..4b5219c 100644
--- a/crates/apollo-encoder/src/directive.rs
+++ b/crates/apollo-encoder/src/directive.rs
@@ -46,8 +46,8 @@ impl fmt::Display for Directive {
         if !self.args.is_empty() {
             for (i, arg) in self.args.iter().enumerate() {
                 match i {
-                    0 => write!(f, "({}", arg)?,
-                    _ => write!(f, ", {}", arg)?,
+                    0 => write!(f, "({arg}")?,
+                    _ => write!(f, ", {arg}")?,
                 }
             }
             write!(f, ")")?;
diff --git a/crates/apollo-encoder/src/directive_def.rs b/crates/apollo-encoder/src/directive_def.rs
index 290ee3f..a29168b 100644
--- a/crates/apollo-encoder/src/directive_def.rs
+++ b/crates/apollo-encoder/src/directive_def.rs
@@ -84,7 +84,7 @@ impl DirectiveDefinition {
 impl fmt::Display for DirectiveDefinition {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
         write!(f, "directive @{}", self.name)?;
 
@@ -98,8 +98,8 @@ impl fmt::Display for DirectiveDefinition {
 
         for (i, location) in self.locations.iter().enumerate() {
             match i {
-                0 => write!(f, " on {}", location)?,
-                _ => write!(f, " | {}", location)?,
+                0 => write!(f, " on {location}")?,
+                _ => write!(f, " | {location}")?,
             }
         }
 
diff --git a/crates/apollo-encoder/src/document.rs b/crates/apollo-encoder/src/document.rs
index 90644ca..6ee830c 100644
--- a/crates/apollo-encoder/src/document.rs
+++ b/crates/apollo-encoder/src/document.rs
@@ -145,34 +145,34 @@ impl Document {
 impl fmt::Display for Document {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         for operation_def in &self.operation_definitions {
-            write!(f, "{}", operation_def)?;
+            write!(f, "{operation_def}")?;
         }
         for fragment_def in &self.fragment_definitions {
-            write!(f, "{}", fragment_def)?;
+            write!(f, "{fragment_def}")?;
         }
         for schema_def in &self.schema_definitions {
-            write!(f, "{}", schema_def)?;
+            write!(f, "{schema_def}")?;
         }
         for scalar_type_def in &self.scalar_type_definitions {
-            write!(f, "{}", scalar_type_def)?;
+            write!(f, "{scalar_type_def}")?;
         }
         for object_type_def in &self.object_type_definitions {
-            write!(f, "{}", object_type_def)?;
+            write!(f, "{object_type_def}")?;
         }
         for interface_type_def in &self.interface_type_definitions {
-            write!(f, "{}", interface_type_def)?;
+            write!(f, "{interface_type_def}")?;
         }
         for union_type_def in &self.union_type_definitions {
-            write!(f, "{}", union_type_def)?;
+            write!(f, "{union_type_def}")?;
         }
         for enum_type_def in &self.enum_type_definitions {
-            write!(f, "{}", enum_type_def)?;
+            write!(f, "{enum_type_def}")?;
         }
         for input_object_type_def in &self.input_object_type_definitions {
-            write!(f, "{}", input_object_type_def)?;
+            write!(f, "{input_object_type_def}")?;
         }
         for directive_def in &self.directive_definitions {
-            write!(f, "{}", directive_def)?;
+            write!(f, "{directive_def}")?;
         }
 
         Ok(())
diff --git a/crates/apollo-encoder/src/enum_def.rs b/crates/apollo-encoder/src/enum_def.rs
index 705f41a..b4c8c5e 100644
--- a/crates/apollo-encoder/src/enum_def.rs
+++ b/crates/apollo-encoder/src/enum_def.rs
@@ -94,17 +94,17 @@ impl fmt::Display for EnumDefinition {
             write!(f, "extend ")?;
         // No description when it's a extension
         } else if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
 
         write!(f, "enum {}", self.name)?;
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         write!(f, " {{")?;
         for value in &self.values {
-            write!(f, "\n{}", value)?;
+            write!(f, "\n{value}")?;
         }
         writeln!(f, "\n}}")
     }
diff --git a/crates/apollo-encoder/src/enum_value.rs b/crates/apollo-encoder/src/enum_value.rs
index 0b7e149..3e1b8f0 100644
--- a/crates/apollo-encoder/src/enum_value.rs
+++ b/crates/apollo-encoder/src/enum_value.rs
@@ -66,12 +66,12 @@ impl EnumValue {
 impl fmt::Display for EnumValue {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
         write!(f, "  {}", self.name)?;
 
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         Ok(())
diff --git a/crates/apollo-encoder/src/field.rs b/crates/apollo-encoder/src/field.rs
index 88f43f5..f13ce24 100644
--- a/crates/apollo-encoder/src/field.rs
+++ b/crates/apollo-encoder/src/field.rs
@@ -82,7 +82,7 @@ impl FieldDefinition {
 impl fmt::Display for FieldDefinition {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
         write!(f, "  {}", self.name)?;
 
@@ -93,7 +93,7 @@ impl fmt::Display for FieldDefinition {
         write!(f, ": {}", self.type_)?;
 
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         Ok(())
diff --git a/crates/apollo-encoder/src/fragment.rs b/crates/apollo-encoder/src/fragment.rs
index 5200f0d..88593e8 100644
--- a/crates/apollo-encoder/src/fragment.rs
+++ b/crates/apollo-encoder/src/fragment.rs
@@ -64,7 +64,7 @@ impl fmt::Display for FragmentDefinition {
         let indent_level = 0;
         write!(f, "fragment {} {}", self.name, self.type_condition)?;
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
         write!(
             f,
@@ -115,7 +115,7 @@ impl fmt::Display for FragmentSpread {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "...{}", self.name)?;
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         Ok(())
@@ -187,10 +187,10 @@ impl InlineFragment {
         let mut text = String::from("...");
 
         if let Some(type_condition) = &self.type_condition {
-            let _ = write!(text, " {}", type_condition);
+            let _ = write!(text, " {type_condition}");
         }
         for directive in &self.directives {
-            let _ = write!(text, " {}", directive);
+            let _ = write!(text, " {directive}");
         }
 
         let _ = write!(
diff --git a/crates/apollo-encoder/src/input_field.rs b/crates/apollo-encoder/src/input_field.rs
index 371e09d..46a896f 100644
--- a/crates/apollo-encoder/src/input_field.rs
+++ b/crates/apollo-encoder/src/input_field.rs
@@ -67,16 +67,16 @@ impl InputField {
 impl fmt::Display for InputField {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
 
         write!(f, "  {}: {}", self.name, self.type_)?;
         if let Some(default) = &self.default_value {
-            write!(f, " = {}", default)?;
+            write!(f, " = {default}")?;
         }
 
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         Ok(())
diff --git a/crates/apollo-encoder/src/input_object_def.rs b/crates/apollo-encoder/src/input_object_def.rs
index 671663d..f0db184 100644
--- a/crates/apollo-encoder/src/input_object_def.rs
+++ b/crates/apollo-encoder/src/input_object_def.rs
@@ -98,18 +98,18 @@ impl fmt::Display for InputObjectDefinition {
             write!(f, "extend ")?;
         // No description when it's a extension
         } else if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
 
         write!(f, "input {}", &self.name)?;
 
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
         write!(f, " {{")?;
 
         for field in &self.fields {
-            write!(f, "\n{}", field)?;
+            write!(f, "\n{field}")?;
         }
         writeln!(f, "\n}}")
     }
diff --git a/crates/apollo-encoder/src/input_value.rs b/crates/apollo-encoder/src/input_value.rs
index 06a6310..8b3a78a 100644
--- a/crates/apollo-encoder/src/input_value.rs
+++ b/crates/apollo-encoder/src/input_value.rs
@@ -79,7 +79,7 @@ impl fmt::Display for InputValueDefinition {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match &self.description {
             Some(description) => {
-                writeln!(f, "\n    {}", description)?;
+                writeln!(f, "\n    {description}")?;
 
                 write!(f, "    {}: {}", self.name, self.type_)?;
             }
@@ -89,11 +89,11 @@ impl fmt::Display for InputValueDefinition {
         }
 
         if let Some(default) = &self.default_value {
-            write!(f, " = {}", default)?;
+            write!(f, " = {default}")?;
         }
 
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         Ok(())
diff --git a/crates/apollo-encoder/src/interface_def.rs b/crates/apollo-encoder/src/interface_def.rs
index 0e8537b..fdce4ad 100644
--- a/crates/apollo-encoder/src/interface_def.rs
+++ b/crates/apollo-encoder/src/interface_def.rs
@@ -133,25 +133,25 @@ impl fmt::Display for InterfaceDefinition {
         } else {
             // No description when it's a extension
             if let Some(description) = &self.description {
-                write!(f, "{}", description)?;
+                write!(f, "{description}")?;
             }
         }
 
         write!(f, "interface {}", &self.name)?;
         for (i, interface) in self.interfaces.iter().enumerate() {
             match i {
-                0 => write!(f, " implements {}", interface)?,
-                _ => write!(f, "& {}", interface)?,
+                0 => write!(f, " implements {interface}")?,
+                _ => write!(f, "& {interface}")?,
             }
         }
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         write!(f, " {{")?;
 
         for field in &self.fields {
-            write!(f, "\n{}", field)?;
+            write!(f, "\n{field}")?;
         }
         writeln!(f, "\n}}")
     }
diff --git a/crates/apollo-encoder/src/object_def.rs b/crates/apollo-encoder/src/object_def.rs
index 079fcab..4e2c8b6 100644
--- a/crates/apollo-encoder/src/object_def.rs
+++ b/crates/apollo-encoder/src/object_def.rs
@@ -113,24 +113,24 @@ impl fmt::Display for ObjectDefinition {
             write!(f, "extend ")?;
         // No description when it's a extension
         } else if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
 
         write!(f, "type {}", &self.name)?;
         for (i, interface) in self.interfaces.iter().enumerate() {
             match i {
-                0 => write!(f, " implements {}", interface)?,
-                _ => write!(f, " & {}", interface)?,
+                0 => write!(f, " implements {interface}")?,
+                _ => write!(f, " & {interface}")?,
             }
         }
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         write!(f, " {{")?;
 
         for field in &self.fields {
-            write!(f, "\n{}", field)?;
+            write!(f, "\n{field}")?;
         }
         writeln!(f, "\n}}")
     }
diff --git a/crates/apollo-encoder/src/operation.rs b/crates/apollo-encoder/src/operation.rs
index 1a7ef03..2e675c4 100644
--- a/crates/apollo-encoder/src/operation.rs
+++ b/crates/apollo-encoder/src/operation.rs
@@ -107,22 +107,22 @@ impl fmt::Display for OperationDefinition {
         if !self.shorthand {
             write!(f, "{}", self.operation_type)?;
             if let Some(name) = &self.name {
-                write!(f, " {}", name)?;
+                write!(f, " {name}")?;
             }
 
             if !self.variable_definitions.is_empty() {
                 write!(f, "(")?;
                 for (i, var_def) in self.variable_definitions.iter().enumerate() {
                     if i == self.variable_definitions.len() - 1 {
-                        write!(f, "{}", var_def)?;
+                        write!(f, "{var_def}")?;
                     } else {
-                        write!(f, "{}, ", var_def)?;
+                        write!(f, "{var_def}, ")?;
                     }
                 }
                 write!(f, ")")?;
             }
             for directive in &self.directives {
-                write!(f, " {}", directive)?;
+                write!(f, " {directive}")?;
             }
             write!(f, " ")?;
         }
diff --git a/crates/apollo-encoder/src/scalar_def.rs b/crates/apollo-encoder/src/scalar_def.rs
index 017e337..d89061c 100644
--- a/crates/apollo-encoder/src/scalar_def.rs
+++ b/crates/apollo-encoder/src/scalar_def.rs
@@ -68,12 +68,12 @@ impl fmt::Display for ScalarDefinition {
         if self.extend {
             write!(f, "extend ")?;
         } else if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
 
         write!(f, "scalar {}", self.name)?;
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
         writeln!(f)
     }
diff --git a/crates/apollo-encoder/src/schema_def.rs b/crates/apollo-encoder/src/schema_def.rs
index b9eba56..d7d2283 100644
--- a/crates/apollo-encoder/src/schema_def.rs
+++ b/crates/apollo-encoder/src/schema_def.rs
@@ -104,27 +104,27 @@ impl fmt::Display for SchemaDefinition {
             write!(f, "extend ")?;
         // No description when it's an extension schema
         } else if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
 
         write!(f, "schema")?;
 
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         writeln!(f, " {{")?;
 
         if let Some(query) = &self.query {
-            writeln!(f, "  query: {}", query)?;
+            writeln!(f, "  query: {query}")?;
         }
 
         if let Some(mutation) = &self.mutation {
-            writeln!(f, "  mutation: {}", mutation)?;
+            writeln!(f, "  mutation: {mutation}")?;
         }
 
         if let Some(subscription) = &self.subscription {
-            writeln!(f, "  subscription: {}", subscription)?;
+            writeln!(f, "  subscription: {subscription}")?;
         }
 
         writeln!(f, "}}")
diff --git a/crates/apollo-encoder/src/string_value.rs b/crates/apollo-encoder/src/string_value.rs
index eb2a387..6d5d08d 100644
--- a/crates/apollo-encoder/src/string_value.rs
+++ b/crates/apollo-encoder/src/string_value.rs
@@ -31,7 +31,7 @@ impl fmt::Display for StringValue {
                 if is_block_string_character(source) {
                     writeln!(f, "\"\"\"\n{}\n\"\"\"", trim_double_quotes(source))?
                 } else {
-                    writeln!(f, "\"{}\"", source)?
+                    writeln!(f, "\"{source}\"")?
                 }
             }
             StringValue::Field { source } => {
@@ -39,11 +39,11 @@ impl fmt::Display for StringValue {
                     write!(f, "  \"\"\"")?;
                     let desc = trim_double_quotes(source);
                     for line in desc.lines() {
-                        write!(f, "\n  {}", line)?;
+                        write!(f, "\n  {line}")?;
                     }
                     writeln!(f, "\n  \"\"\"")?;
                 } else {
-                    writeln!(f, "  \"{}\"", source)?
+                    writeln!(f, "  \"{source}\"")?
                 }
             }
             StringValue::Input { source } => {
@@ -51,11 +51,11 @@ impl fmt::Display for StringValue {
                     write!(f, "\"\"\"")?;
                     let desc = trim_double_quotes(source);
                     for line in desc.lines() {
-                        write!(f, "\n    {}", line)?;
+                        write!(f, "\n    {line}")?;
                     }
                     write!(f, "\n    \"\"\"")?
                 } else {
-                    write!(f, "\"{}\"", source)?
+                    write!(f, "\"{source}\"")?
                 }
             }
         }
diff --git a/crates/apollo-encoder/src/ty.rs b/crates/apollo-encoder/src/ty.rs
index 743033a..e42c5eb 100644
--- a/crates/apollo-encoder/src/ty.rs
+++ b/crates/apollo-encoder/src/ty.rs
@@ -44,12 +44,12 @@ impl Display for Type_ {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Type_::List { ty } => {
-                write!(f, "[{}]", ty)
+                write!(f, "[{ty}]")
             }
             Type_::NonNull { ty } => {
-                write!(f, "{}!", ty)
+                write!(f, "{ty}!")
             }
-            Type_::NamedType { name } => write!(f, "{}", name),
+            Type_::NamedType { name } => write!(f, "{name}"),
         }
     }
 }
diff --git a/crates/apollo-encoder/src/union_def.rs b/crates/apollo-encoder/src/union_def.rs
index c6cf060..42e316f 100644
--- a/crates/apollo-encoder/src/union_def.rs
+++ b/crates/apollo-encoder/src/union_def.rs
@@ -77,21 +77,21 @@ impl fmt::Display for UnionDefinition {
             write!(f, "extend ")?;
         // No description when it's a extension
         } else if let Some(description) = &self.description {
-            write!(f, "{}", description)?;
+            write!(f, "{description}")?;
         }
 
         write!(f, "union {}", self.name)?;
 
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         write!(f, " =")?;
 
         for (i, member) in self.members.iter().enumerate() {
             match i {
-                0 => write!(f, " {}", member)?,
-                _ => write!(f, " | {}", member)?,
+                0 => write!(f, " {member}")?,
+                _ => write!(f, " | {member}")?,
             }
         }
 
diff --git a/crates/apollo-encoder/src/variable.rs b/crates/apollo-encoder/src/variable.rs
index 957bc0b..d51fee6 100644
--- a/crates/apollo-encoder/src/variable.rs
+++ b/crates/apollo-encoder/src/variable.rs
@@ -64,11 +64,11 @@ impl fmt::Display for VariableDefinition {
         write!(f, "${}: {}", self.variable, self.ty)?;
 
         if let Some(default_value) = &self.default_value {
-            write!(f, " = {}", default_value)?;
+            write!(f, " = {default_value}")?;
         }
 
         for directive in &self.directives {
-            write!(f, " {}", directive)?;
+            write!(f, " {directive}")?;
         }
 
         Ok(())
diff --git a/crates/apollo-parser/benches/supergraph.rs b/crates/apollo-parser/benches/supergraph.rs
index c0904ca..3ed80a6 100644
--- a/crates/apollo-parser/benches/supergraph.rs
+++ b/crates/apollo-parser/benches/supergraph.rs
@@ -7,7 +7,7 @@ fn parse_schema(schema: &str) {
     let errors = tree.errors().collect::<Vec<_>>();
 
     if !errors.is_empty() {
-        panic!("error parsing query: {:?}", errors);
+        panic!("error parsing query: {errors:?}");
     }
 
     let document = tree.document();
diff --git a/crates/apollo-parser/examples/annotate_snippet.rs b/crates/apollo-parser/examples/annotate_snippet.rs
index 4f5de20..64c410a 100644
--- a/crates/apollo-parser/examples/annotate_snippet.rs
+++ b/crates/apollo-parser/examples/annotate_snippet.rs
@@ -53,7 +53,7 @@ fn parse_schema() -> ast::Document {
         };
 
         let dl = DisplayList::from(snippet);
-        println!("{}\n\n", dl);
+        println!("{dl}\n\n");
     }
 
     ast.document()
diff --git a/crates/apollo-parser/examples/miette.rs b/crates/apollo-parser/examples/miette.rs
index 389762b..5947551 100644
--- a/crates/apollo-parser/examples/miette.rs
+++ b/crates/apollo-parser/examples/miette.rs
@@ -46,7 +46,7 @@ fn parse_schema() -> ast::Document {
             span: (err.index(), err.data().len()).into(), // (offset, length of error token)
             ty: err.message().into(),
         });
-        println!("{:?}", err);
+        println!("{err:?}");
     }
 
     ast.document()
diff --git a/crates/apollo-parser/examples/modify_query_using_parser_and_encoder.rs b/crates/apollo-parser/examples/modify_query_using_parser_and_encoder.rs
index 415e684..a956004 100644
--- a/crates/apollo-parser/examples/modify_query_using_parser_and_encoder.rs
+++ b/crates/apollo-parser/examples/modify_query_using_parser_and_encoder.rs
@@ -111,10 +111,10 @@ fn omitted_fields() -> Result<apollo_encoder::Document> {
 
 fn main() -> Result<()> {
     let merged = merge_queries()?;
-    println!("{}", merged);
+    println!("{merged}");
 
     let omitted_fields = omitted_fields()?;
-    println!("{}", omitted_fields);
+    println!("{omitted_fields}");
 
     Ok(())
 }
diff --git a/crates/apollo-parser/src/error.rs b/crates/apollo-parser/src/error.rs
index aeafaf2..4cd39ed 100644
--- a/crates/apollo-parser/src/error.rs
+++ b/crates/apollo-parser/src/error.rs
@@ -55,7 +55,7 @@ impl fmt::Display for ErrorData {
         match self {
             Self::Eof => write!(f, "EOF"),
             Self::LimitExceeded => Ok(()),
-            Self::Text(text) => write!(f, "{}", text),
+            Self::Text(text) => write!(f, "{text}"),
         }
     }
 }
diff --git a/crates/apollo-parser/src/lexer/mod.rs b/crates/apollo-parser/src/lexer/mod.rs
index 6688eec..0cbc60c 100644
--- a/crates/apollo-parser/src/lexer/mod.rs
+++ b/crates/apollo-parser/src/lexer/mod.rs
@@ -289,14 +289,11 @@ impl Cursor<'_> {
                 self.bump();
                 buf.push('.');
 
-                self.add_err(Error::new(
-                    "Unterminated spread operator",
-                    format!("..{}", b),
-                ));
+                self.add_err(Error::new("Unterminated spread operator", format!("..{b}")));
             }
             (a, b) => self.add_err(Error::new(
                 "Unterminated spread operator",
-                format!(".{}{}", a, b),
+                format!(".{a}{b}"),
             )),
         }
 
@@ -357,13 +354,13 @@ impl Cursor<'_> {
                     self.bump();
                     if !has_digit {
                         self.add_err(Error::new(
-                            format!("Unexpected character `{}` in exponent", first),
+                            format!("Unexpected character `{first}` in exponent"),
                             first.to_string(),
                         ));
                     }
                     if has_exponent {
                         self.add_err(Error::new(
-                            format!("Unexpected character `{}`", first),
+                            format!("Unexpected character `{first}`"),
                             first.to_string(),
                         ));
                     }
@@ -379,21 +376,21 @@ impl Cursor<'_> {
 
                     if !has_digit {
                         self.add_err(Error::new(
-                            format!("Unexpected character `{}` before a digit", first),
+                            format!("Unexpected character `{first}` before a digit"),
                             first.to_string(),
                         ));
                     }
 
                     if has_fractional {
                         self.add_err(Error::new(
-                            format!("Unexpected character `{}`", first),
+                            format!("Unexpected character `{first}`"),
                             first.to_string(),
                         ));
                     }
 
                     if has_exponent {
                         self.add_err(Error::new(
-                            format!("Unexpected character `{}`", first),
+                            format!("Unexpected character `{first}`"),
                             first.to_string(),
                         ));
                     }
@@ -459,7 +456,7 @@ fn is_line_terminator(c: char) -> bool {
 }
 
 fn is_digit_char(c: char) -> bool {
-    matches!(c, '0'..='9')
+    c.is_ascii_digit()
 }
 
 // EscapedCharacter
diff --git a/crates/apollo-parser/src/lexer/token.rs b/crates/apollo-parser/src/lexer/token.rs
index 30046d8..ae230ba 100644
--- a/crates/apollo-parser/src/lexer/token.rs
+++ b/crates/apollo-parser/src/lexer/token.rs
@@ -93,7 +93,7 @@ impl fmt::Debug for Token {
                 write!(f, "PIPE@{}:{} {:?}", start, end, self.data)
             }
             TokenKind::Eof => {
-                write!(f, "EOF@{}:{}", start, start)
+                write!(f, "EOF@{start}:{start}")
             }
 
             // composite nodes
diff --git a/crates/apollo-parser/src/parser/mod.rs b/crates/apollo-parser/src/parser/mod.rs
index 1599c40..ca16a63 100644
--- a/crates/apollo-parser/src/parser/mod.rs
+++ b/crates/apollo-parser/src/parser/mod.rs
@@ -255,7 +255,7 @@ impl<'a> Parser<'a> {
             return;
         }
 
-        let message = format!("expected {:?}, got {}", kind, data);
+        let message = format!("expected {kind:?}, got {data}");
         let err = if is_eof {
             Error::eof(message, index)
         } else {
diff --git a/crates/apollo-parser/src/parser/syntax_tree.rs b/crates/apollo-parser/src/parser/syntax_tree.rs
index 362d1d4..6997241 100644
--- a/crates/apollo-parser/src/parser/syntax_tree.rs
+++ b/crates/apollo-parser/src/parser/syntax_tree.rs
@@ -100,7 +100,7 @@ impl fmt::Debug for SyntaxTree {
 
         fn print_err(f: &mut fmt::Formatter<'_>, errors: Vec<Error>) -> fmt::Result {
             for err in errors {
-                writeln!(f, "- {:?}", err)?;
+                writeln!(f, "- {err:?}")?;
             }
 
             write!(f, "")
@@ -110,7 +110,7 @@ impl fmt::Debug for SyntaxTree {
             f: &mut fmt::Formatter<'_>,
             recursion_limit: LimitTracker,
         ) -> fmt::Result {
-            write!(f, "{:?}", recursion_limit)
+            write!(f, "{recursion_limit:?}")
         }
 
         print(f, 0, self.syntax_node().into())?;
diff --git a/crates/apollo-smith/src/name.rs b/crates/apollo-smith/src/name.rs
index 6289304..c259ebc 100644
--- a/crates/apollo-smith/src/name.rs
+++ b/crates/apollo-smith/src/name.rs
@@ -82,7 +82,7 @@ impl<'a> DocumentBuilder<'a> {
     /// Create an arbitrary `Name` with an index included in the name (to avoid name conflict)
     pub fn name_with_index(&mut self, index: usize) -> ArbitraryResult<Name> {
         let mut name = self.limited_string(30)?;
-        let _ = write!(name, "{}", index);
+        let _ = write!(name, "{index}");
 
         Ok(Name::new(name))
     }
diff --git a/fuzz/fuzz_targets/lexer.rs b/fuzz/fuzz_targets/lexer.rs
index 7116d4d..f3ff6a0 100644
--- a/fuzz/fuzz_targets/lexer.rs
+++ b/fuzz/fuzz_targets/lexer.rs
@@ -17,7 +17,7 @@ fuzz_target!(|data: &[u8]| {
 
     let (_tokens, errors) = match lexer {
         Err(err) => {
-            panic!("error {:?}", err);
+            panic!("error {err:?}");
         }
         Ok(p) => p,
     };
diff --git a/fuzz/fuzz_targets/parser.rs b/fuzz/fuzz_targets/parser.rs
index 62b9fb2..4929863 100644
--- a/fuzz/fuzz_targets/parser.rs
+++ b/fuzz/fuzz_targets/parser.rs
@@ -17,7 +17,7 @@ fuzz_target!(|data: &[u8]| {
 
     let parser = match parser {
         Err(err) => {
-            panic!("error {:?}", err);
+            panic!("error {err:?}");
         }
         Ok(p) => p,
     };
diff --git a/xtask/src/codegen/gen_syntax_nodes.rs b/xtask/src/codegen/gen_syntax_nodes.rs
index c94bb8e..778d6b3 100644
--- a/xtask/src/codegen/gen_syntax_nodes.rs
+++ b/xtask/src/codegen/gen_syntax_nodes.rs
@@ -222,7 +222,7 @@ fn to_pascal_case(s: &str) -> String {
 
 fn write_doc_comment(contents: &[String], dest: &mut String) {
     for line in contents {
-        writeln!(dest, "///{}", line).unwrap();
+        writeln!(dest, "///{line}").unwrap();
     }
 }
 
diff --git a/xtask/src/codegen/mod.rs b/xtask/src/codegen/mod.rs
index 5def0ed..2bb0832 100644
--- a/xtask/src/codegen/mod.rs
+++ b/xtask/src/codegen/mod.rs
@@ -120,7 +120,7 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r
             let mut name = grammar[*token].name.clone();
             if name != "int_number" && name != "string" {
                 if "[]{}()".contains(&name) {
-                    name = format!("'{}'", name);
+                    name = format!("'{name}'");
                 }
                 let field = Field::Token(name);
                 acc.push(field);
diff --git a/xtask/src/utils.rs b/xtask/src/utils.rs
index 5d77385..b56b82c 100644
--- a/xtask/src/utils.rs
+++ b/xtask/src/utils.rs
@@ -4,7 +4,7 @@ use std::{
 };
 
 pub(crate) fn pluralize(s: &str) -> String {
-    format!("{}s", s)
+    format!("{s}s")
 }
 
 #[allow(unused)]


Test patch:
diff --git a/crates/apollo-compiler/src/tests.rs b/crates/apollo-compiler/src/tests.rs
index b7b5d37..94ddf53 100644
--- a/crates/apollo-compiler/src/tests.rs
+++ b/crates/apollo-compiler/src/tests.rs
@@ -31,7 +31,7 @@ fn compiler_tests() {
         let errors = compiler.validate();
         let ast = compiler.db.ast(file_id);
         assert_diagnostics_are_absent(&errors, path);
-        format!("{:?}", ast)
+        format!("{ast:?}")
     });
 
     dir_tests(&test_data_dir(), &["diagnostics"], "txt", |text, path| {
@@ -40,7 +40,7 @@ fn compiler_tests() {
 
         let diagnostics = compiler.validate();
         assert_diagnostics_are_present(&diagnostics, path);
-        format!("{:#?}", diagnostics)
+        format!("{diagnostics:#?}")
     });
 }
 
@@ -54,7 +54,7 @@ fn assert_diagnostics_are_present(errors: &[ApolloDiagnostic], path: &Path) {
 
 fn assert_diagnostics_are_absent(errors: &[ApolloDiagnostic], path: &Path) {
     if !errors.is_empty() {
-        let formatted: Vec<String> = errors.iter().map(|e| format!("{:?}", e)).collect();
+        let formatted: Vec<String> = errors.iter().map(|e| format!("{e:?}")).collect();
         println!("{:?}", formatted.join("\n"));
         panic!(
             "There should be no diagnostics in the file {:?}",
@@ -92,7 +92,7 @@ fn collect_graphql_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, Strin
         })
         .map(|path| {
             let text = fs::read_to_string(&path)
-                .unwrap_or_else(|_| panic!("File at {:?} should be valid", path));
+                .unwrap_or_else(|_| panic!("File at {path:?} should be valid"));
             (path, text)
         })
         .collect()
diff --git a/crates/apollo-compiler/test_data/diagnostics/0024_interface_definition_with_duplicate_fields.txt b/crates/apollo-compiler/test_data/diagnostics/0024_interface_definition_with_duplicate_fields.txt
index 83c7747..70cc186 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0024_interface_definition_with_duplicate_fields.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0024_interface_definition_with_duplicate_fields.txt
@@ -20,7 +20,7 @@
                 ),
             },
             help: Some(
-                "`name` field must only be defined once in this interface definition.",
+                "`name` field must only be defined once in this definition.",
             ),
         },
     ),
diff --git a/crates/apollo-compiler/test_data/diagnostics/0031_input_object_with_duplicate_fields.txt b/crates/apollo-compiler/test_data/diagnostics/0031_input_object_with_duplicate_fields.txt
index 4d5a817..aa95347 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0031_input_object_with_duplicate_fields.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0031_input_object_with_duplicate_fields.txt
@@ -1,9 +1,9 @@
 [
-    UniqueField(
-        UniqueField {
-            field: "x",
+    UniqueInputValue(
+        UniqueInputValue {
+            value: "x",
             src: "type Query {\n  website: URL,\n  amount: Int\n}\n\nscalar URL @specifiedBy(url: \"https://tools.ietf.org/html/rfc3986\")\n\ninput Point2D {\n  x: Float\n  x: Float\n}",
-            original_field: SourceSpan {
+            original_value: SourceSpan {
                 offset: SourceOffset(
                     133,
                 ),
@@ -11,7 +11,7 @@
                     11,
                 ),
             },
-            redefined_field: SourceSpan {
+            redefined_value: SourceSpan {
                 offset: SourceOffset(
                     144,
                 ),
@@ -19,34 +19,6 @@
                     9,
                 ),
             },
-            help: Some(
-                "x field must only be defined once in this input object definition.",
-            ),
-        },
-    ),
-    UniqueArgument(
-        UniqueArgument {
-            name: "x",
-            src: "type Query {\n  website: URL,\n  amount: Int\n}\n\nscalar URL @specifiedBy(url: \"https://tools.ietf.org/html/rfc3986\")\n\ninput Point2D {\n  x: Float\n  x: Float\n}",
-            original_definition: SourceSpan {
-                offset: SourceOffset(
-                    133,
-                ),
-                length: SourceOffset(
-                    11,
-                ),
-            },
-            redefined_definition: SourceSpan {
-                offset: SourceOffset(
-                    144,
-                ),
-                length: SourceOffset(
-                    9,
-                ),
-            },
-            help: Some(
-                "`x` argument must only be defined once.",
-            ),
         },
     ),
 ]
diff --git a/crates/apollo-compiler/test_data/diagnostics/0033_object_type_definition_with_duplicate_fields.txt b/crates/apollo-compiler/test_data/diagnostics/0033_object_type_definition_with_duplicate_fields.txt
index 4589230..1069e50 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0033_object_type_definition_with_duplicate_fields.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0033_object_type_definition_with_duplicate_fields.txt
@@ -20,7 +20,7 @@
                 ),
             },
             help: Some(
-                "`name` field must only be defined once in this object type definition.",
+                "`name` field must only be defined once in this definition.",
             ),
         },
     ),
diff --git a/crates/apollo-compiler/test_data/diagnostics/0048_duplicate_field_argument_definition_names.txt b/crates/apollo-compiler/test_data/diagnostics/0048_duplicate_field_argument_definition_names.txt
index 7dcd5d4..bd1f011 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0048_duplicate_field_argument_definition_names.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0048_duplicate_field_argument_definition_names.txt
@@ -1,52 +1,46 @@
 [
-    UniqueArgument(
-        UniqueArgument {
-            name: "arg",
+    UniqueInputValue(
+        UniqueInputValue {
+            value: "arg",
             src: "interface Duplicate {\n  duplicate(arg: Boolean, arg: Boolean): Int\n}\n\ntype Query implements Duplicate {\n  single(arg: Boolean): Int\n  duplicate(arg: Boolean, arg: Boolean): Int\n}\n",
-            original_definition: SourceSpan {
+            original_value: SourceSpan {
                 offset: SourceOffset(
-                    144,
+                    34,
                 ),
                 length: SourceOffset(
                     14,
                 ),
             },
-            redefined_definition: SourceSpan {
+            redefined_value: SourceSpan {
                 offset: SourceOffset(
-                    158,
+                    48,
                 ),
                 length: SourceOffset(
                     12,
                 ),
             },
-            help: Some(
-                "`arg` argument must only be defined once.",
-            ),
         },
     ),
-    UniqueArgument(
-        UniqueArgument {
-            name: "arg",
+    UniqueInputValue(
+        UniqueInputValue {
+            value: "arg",
             src: "interface Duplicate {\n  duplicate(arg: Boolean, arg: Boolean): Int\n}\n\ntype Query implements Duplicate {\n  single(arg: Boolean): Int\n  duplicate(arg: Boolean, arg: Boolean): Int\n}\n",
-            original_definition: SourceSpan {
+            original_value: SourceSpan {
                 offset: SourceOffset(
-                    34,
+                    144,
                 ),
                 length: SourceOffset(
                     14,
                 ),
             },
-            redefined_definition: SourceSpan {
+            redefined_value: SourceSpan {
                 offset: SourceOffset(
-                    48,
+                    158,
                 ),
                 length: SourceOffset(
                     12,
                 ),
             },
-            help: Some(
-                "`arg` argument must only be defined once.",
-            ),
         },
     ),
 ]
diff --git a/crates/apollo-compiler/test_data/diagnostics/0049_duplicate_directive_argument_definition_names.txt b/crates/apollo-compiler/test_data/diagnostics/0049_duplicate_directive_argument_definition_names.txt
index 9d8f16b..0a4523f 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0049_duplicate_directive_argument_definition_names.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0049_duplicate_directive_argument_definition_names.txt
@@ -1,9 +1,9 @@
 [
-    UniqueArgument(
-        UniqueArgument {
-            name: "arg",
+    UniqueInputValue(
+        UniqueInputValue {
+            value: "arg",
             src: "directive @example(arg: Boolean, arg: Boolean) on FIELD\n",
-            original_definition: SourceSpan {
+            original_value: SourceSpan {
                 offset: SourceOffset(
                     19,
                 ),
@@ -11,7 +11,7 @@
                     14,
                 ),
             },
-            redefined_definition: SourceSpan {
+            redefined_value: SourceSpan {
                 offset: SourceOffset(
                     33,
                 ),
@@ -19,9 +19,6 @@
                     12,
                 ),
             },
-            help: Some(
-                "`arg` argument must only be defined once.",
-            ),
         },
     ),
 ]
diff --git a/crates/apollo-compiler/test_data/diagnostics/0050_directives_in_invalid_locations.graphql b/crates/apollo-compiler/test_data/diagnostics/0050_directives_in_invalid_locations.graphql
index d453ac1..9b26e98 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0050_directives_in_invalid_locations.graphql
+++ b/crates/apollo-compiler/test_data/diagnostics/0050_directives_in_invalid_locations.graphql
@@ -20,7 +20,7 @@ subscription subscriptionA @directiveA {
   }
 }
 
-mutation @skip(if: true) {
+mutation myMutation @skip(if: true) {
   setMessage (message: "Hello, World! Yours, GraphQL.")
 }
 
diff --git a/crates/apollo-compiler/test_data/diagnostics/0050_directives_in_invalid_locations.txt b/crates/apollo-compiler/test_data/diagnostics/0050_directives_in_invalid_locations.txt
index 3adcd5e..477abda 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0050_directives_in_invalid_locations.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0050_directives_in_invalid_locations.txt
@@ -1,15 +1,15 @@
 [
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "skip",
-            dir_loc: Query,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "include",
+            dir_loc: Schema,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    36,
+                    1105,
                 ),
                 length: SourceOffset(
-                    15,
+                    9,
                 ),
             },
             directive_def: None,
@@ -20,18 +20,27 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "skip",
-            dir_loc: VariableDefinition,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "directiveB",
+            dir_loc: Scalar,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    29,
+                    1246,
                 ),
                 length: SourceOffset(
-                    5,
+                    12,
                 ),
             },
-            directive_def: None,
+            directive_def: Some(
+                SourceSpan {
+                    offset: SourceOffset(
+                        1337,
+                    ),
+                    length: SourceOffset(
+                        29,
+                    ),
+                },
+            ),
             help: Some(
                 "the directive must be used in a location that the service has declared support for",
             ),
@@ -39,18 +48,27 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "deprecated",
-            dir_loc: Field,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "directiveA",
+            dir_loc: Enum,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    89,
+                    791,
                 ),
                 length: SourceOffset(
-                    14,
+                    12,
                 ),
             },
-            directive_def: None,
+            directive_def: Some(
+                SourceSpan {
+                    offset: SourceOffset(
+                        1306,
+                    ),
+                    length: SourceOffset(
+                        31,
+                    ),
+                },
+            ),
             help: Some(
                 "the directive must be used in a location that the service has declared support for",
             ),
@@ -58,24 +76,24 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "directiveB",
-            dir_loc: FragmentSpread,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "directiveA",
+            dir_loc: EnumValue,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    123,
+                    813,
                 ),
                 length: SourceOffset(
-                    14,
+                    15,
                 ),
             },
             directive_def: Some(
                 SourceSpan {
                     offset: SourceOffset(
-                        1326,
+                        1306,
                     ),
                     length: SourceOffset(
-                        29,
+                        31,
                     ),
                 },
             ),
@@ -86,12 +104,12 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "directiveA",
-            dir_loc: Subscription,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "directiveB",
+            dir_loc: Union,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    258,
+                    709,
                 ),
                 length: SourceOffset(
                     12,
@@ -100,10 +118,10 @@
             directive_def: Some(
                 SourceSpan {
                     offset: SourceOffset(
-                        1295,
+                        1337,
                     ),
                     length: SourceOffset(
-                        31,
+                        29,
                     ),
                 },
             ),
@@ -115,14 +133,14 @@
     UnsupportedLocation(
         UnsupportedLocation {
             ty: "skip",
-            dir_loc: Mutation,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            dir_loc: Interface,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    323,
+                    425,
                 ),
                 length: SourceOffset(
-                    16,
+                    6,
                 ),
             },
             directive_def: None,
@@ -133,27 +151,18 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "directiveB",
-            dir_loc: FragmentDefinition,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "include",
+            dir_loc: InputObject,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    162,
+                    639,
                 ),
                 length: SourceOffset(
-                    11,
+                    9,
                 ),
             },
-            directive_def: Some(
-                SourceSpan {
-                    offset: SourceOffset(
-                        1326,
-                    ),
-                    length: SourceOffset(
-                        29,
-                    ),
-                },
-            ),
+            directive_def: None,
             help: Some(
                 "the directive must be used in a location that the service has declared support for",
             ),
@@ -161,27 +170,18 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "directiveA",
-            dir_loc: InlineFragment,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "include",
+            dir_loc: InputFieldDefinition,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    201,
+                    666,
                 ),
                 length: SourceOffset(
-                    12,
+                    11,
                 ),
             },
-            directive_def: Some(
-                SourceSpan {
-                    offset: SourceOffset(
-                        1295,
-                    ),
-                    length: SourceOffset(
-                        31,
-                    ),
-                },
-            ),
+            directive_def: None,
             help: Some(
                 "the directive must be used in a location that the service has declared support for",
             ),
@@ -190,20 +190,20 @@
     UnsupportedLocation(
         UnsupportedLocation {
             ty: "directiveB",
-            dir_loc: Scalar,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            dir_loc: FieldDefinition,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    1235,
+                    492,
                 ),
                 length: SourceOffset(
-                    12,
+                    14,
                 ),
             },
             directive_def: Some(
                 SourceSpan {
                     offset: SourceOffset(
-                        1326,
+                        1337,
                     ),
                     length: SourceOffset(
                         29,
@@ -217,27 +217,18 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "directiveB",
-            dir_loc: FieldDefinition,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "deprecated",
+            dir_loc: Object,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    481,
+                    856,
                 ),
                 length: SourceOffset(
-                    14,
+                    12,
                 ),
             },
-            directive_def: Some(
-                SourceSpan {
-                    offset: SourceOffset(
-                        1326,
-                    ),
-                    length: SourceOffset(
-                        29,
-                    ),
-                },
-            ),
+            directive_def: None,
             help: Some(
                 "the directive must be used in a location that the service has declared support for",
             ),
@@ -245,15 +236,15 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "deprecated",
-            dir_loc: Object,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "specifiedBy",
+            dir_loc: ArgumentDefinition,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    845,
+                    928,
                 ),
                 length: SourceOffset(
-                    12,
+                    56,
                 ),
             },
             directive_def: None,
@@ -264,15 +255,15 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "specifiedBy",
-            dir_loc: ArgumentDefinition,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "skip",
+            dir_loc: Query,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    917,
+                    36,
                 ),
                 length: SourceOffset(
-                    56,
+                    15,
                 ),
             },
             directive_def: None,
@@ -284,14 +275,14 @@
     UnsupportedLocation(
         UnsupportedLocation {
             ty: "skip",
-            dir_loc: Interface,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            dir_loc: VariableDefinition,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    414,
+                    29,
                 ),
                 length: SourceOffset(
-                    6,
+                    5,
                 ),
             },
             directive_def: None,
@@ -302,27 +293,18 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "directiveB",
-            dir_loc: Union,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "deprecated",
+            dir_loc: Field,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    698,
+                    89,
                 ),
                 length: SourceOffset(
-                    12,
+                    14,
                 ),
             },
-            directive_def: Some(
-                SourceSpan {
-                    offset: SourceOffset(
-                        1326,
-                    ),
-                    length: SourceOffset(
-                        29,
-                    ),
-                },
-            ),
+            directive_def: None,
             help: Some(
                 "the directive must be used in a location that the service has declared support for",
             ),
@@ -330,24 +312,24 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "directiveA",
-            dir_loc: Enum,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "directiveB",
+            dir_loc: FragmentSpread,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    780,
+                    123,
                 ),
                 length: SourceOffset(
-                    12,
+                    14,
                 ),
             },
             directive_def: Some(
                 SourceSpan {
                     offset: SourceOffset(
-                        1295,
+                        1337,
                     ),
                     length: SourceOffset(
-                        31,
+                        29,
                     ),
                 },
             ),
@@ -359,20 +341,20 @@
     UnsupportedLocation(
         UnsupportedLocation {
             ty: "directiveA",
-            dir_loc: EnumValue,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            dir_loc: Subscription,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    802,
+                    258,
                 ),
                 length: SourceOffset(
-                    15,
+                    12,
                 ),
             },
             directive_def: Some(
                 SourceSpan {
                     offset: SourceOffset(
-                        1295,
+                        1306,
                     ),
                     length: SourceOffset(
                         31,
@@ -386,15 +368,15 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "include",
-            dir_loc: InputObject,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "skip",
+            dir_loc: Mutation,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    628,
+                    334,
                 ),
                 length: SourceOffset(
-                    9,
+                    16,
                 ),
             },
             directive_def: None,
@@ -405,18 +387,27 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "include",
-            dir_loc: InputFieldDefinition,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "directiveB",
+            dir_loc: FragmentDefinition,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    655,
+                    162,
                 ),
                 length: SourceOffset(
                     11,
                 ),
             },
-            directive_def: None,
+            directive_def: Some(
+                SourceSpan {
+                    offset: SourceOffset(
+                        1337,
+                    ),
+                    length: SourceOffset(
+                        29,
+                    ),
+                },
+            ),
             help: Some(
                 "the directive must be used in a location that the service has declared support for",
             ),
@@ -424,36 +415,29 @@
     ),
     UnsupportedLocation(
         UnsupportedLocation {
-            ty: "include",
-            dir_loc: Schema,
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
+            ty: "directiveA",
+            dir_loc: InlineFragment,
+            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation myMutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
             directive: SourceSpan {
                 offset: SourceOffset(
-                    1094,
+                    201,
                 ),
                 length: SourceOffset(
-                    9,
+                    12,
                 ),
             },
-            directive_def: None,
-            help: Some(
-                "the directive must be used in a location that the service has declared support for",
+            directive_def: Some(
+                SourceSpan {
+                    offset: SourceOffset(
+                        1306,
+                    ),
+                    length: SourceOffset(
+                        31,
+                    ),
+                },
             ),
-        },
-    ),
-    MissingIdent(
-        MissingIdent {
-            src: "query queryA($status: String @skip) @skip(if: $foo){\n  field\n  response(status: $status) @deprecated\n  human {\n    ... pet @directiveB\n  }\n}\n\nfragment pet on Cat @directiveB{\n  meowVolume\n  ... on Pet @directiveA {\n    name\n  }\n}\n\nsubscription subscriptionA @directiveA {\n  newMessage {\n    body\n    sender\n  }\n}\n\nmutation @skip(if: true) {\n  setMessage (message: \"Hello, World! Yours, GraphQL.\")\n}\n\ninterface Pet @skip {\n  name: String\n}\n\ntype Dog implements Pet {\n  name: String @directiveB\n  nickname: String\n  barkVolume: Int\n}\n\ntype Cat implements Pet {\n  name: String\n  nickname: String\n  meowVolume: Int\n}\n\ninput Example @include {\n  self: Example @include\n  value: String\n}\n\nunion CatOrDog @directiveB = Cat | Dog\n\ntype Human {\n  name: String\n  pets: [Pet]\n}\n\nenum Status @directiveA {\n  GREEN @directiveA,\n  RED,\n  YELLOW\n}\n\ntype Query @deprecated {\n  human: Human\n  field: String,\n  response(status: String @specifiedBy(url: \"https://tools.ietf.org/html/rfc4122\")): Status\n}\n\ntype Subscription {\n  newMessage: Result\n}\n\ntype Mutation {\n  setMessage(message: String): String\n}\n\nschema @include {\n  query: Query\n  subscription: Subscription\n  mutation: Mutation\n}\n\ntype Result {\n  body: String,\n  sender: String\n}\n\nscalar spec @directiveB @specifiedBy(url: \"https://spec.graphql.org/\")\n\ndirective @directiveA on UNION\ndirective @directiveB on ENUM",
-            definition: SourceSpan {
-                offset: SourceOffset(
-                    314,
-                ),
-                length: SourceOffset(
-                    86,
-                ),
-            },
             help: Some(
-                "GraphQL allows a short-hand form for defining query operations when only that one operation exists in the document. There are 3 operations in this document.",
+                "the directive must be used in a location that the service has declared support for",
             ),
         },
     ),
diff --git a/crates/apollo-parser/src/tests.rs b/crates/apollo-parser/src/tests.rs
index 856a78e..f1a8f50 100644
--- a/crates/apollo-parser/src/tests.rs
+++ b/crates/apollo-parser/src/tests.rs
@@ -41,14 +41,14 @@ fn parser_tests() {
         let parser = Parser::new(text);
         let ast = parser.parse();
         assert_errors_are_absent(&ast.errors().cloned().collect::<Vec<_>>(), path);
-        format!("{:?}", ast)
+        format!("{ast:?}")
     });
 
     dir_tests(&test_data_dir(), &["parser/err"], "txt", |text, path| {
         let parser = Parser::new(text);
         let ast = parser.parse();
         assert_errors_are_present(&ast.errors().cloned().collect::<Vec<_>>(), path);
-        format!("{:?}", ast)
+        format!("{ast:?}")
     });
 }
 
@@ -78,10 +78,10 @@ fn assert_errors_are_absent(errors: &[Error], path: &Path) {
 fn dump_tokens_and_errors(tokens: &[Token], errors: &[Error]) -> String {
     let mut acc = String::new();
     for token in tokens {
-        writeln!(acc, "{:?}", token).unwrap();
+        writeln!(acc, "{token:?}").unwrap();
     }
     for err in errors {
-        writeln!(acc, "{:?}", err).unwrap();
+        writeln!(acc, "{err:?}").unwrap();
     }
     acc
 }
@@ -114,7 +114,7 @@ fn collect_graphql_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, Strin
         })
         .map(|path| {
             let text = fs::read_to_string(&path)
-                .unwrap_or_else(|_| panic!("File at {:?} should be valid", path));
+                .unwrap_or_else(|_| panic!("File at {path:?} should be valid"));
             (path, text)
         })
         .collect()