Skip to content

Commit

Permalink
add no_unused_variables and mark missing defer ones
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed May 22, 2024
1 parent d16aab7 commit b0cdf31
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 1 deletion.
7 changes: 6 additions & 1 deletion src/validate/rules/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use crate::visit::ComposedVisitor;

// Missing schemaless rules
// https://github.com/graphql/graphql-js/blob/main/src/validation/rules/DeferStreamDirectiveLabelRule.ts
// https://github.com/graphql/graphql-js/blob/main/src/validation/rules/DeferStreamDirectiveOnValidOperationsRule.ts
mod known_fragment_names;
mod lone_anonymous_operation;
mod no_fragment_cycles;
mod no_undefined_variables;
mod no_unused_fragments;
mod no_unused_variables;
mod unique_argument_names;
mod unique_fragment_names;
mod unique_operation_names;
Expand All @@ -16,6 +20,7 @@ pub use lone_anonymous_operation::*;
pub use no_fragment_cycles::*;
pub use no_undefined_variables::*;
pub use no_unused_fragments::*;
pub use no_unused_variables::*;
pub use unique_argument_names::*;
pub use unique_fragment_names::*;
pub use unique_operation_names::*;
Expand Down Expand Up @@ -51,7 +56,7 @@ pub type DefaultRules<'a> = ComposedVisitor<
'a,
ValidationContext<'a>,
NoFragmentCycles<'a>,
NoUndefinedVariables<'a>,
ComposedVisitor<'a, ValidationContext<'a>, NoUndefinedVariables<'a>, NoUnusedVariables<'a>>
>,
>,
>,
Expand Down
75 changes: 75 additions & 0 deletions src/validate/rules/no_unused_variables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use super::super::{ValidationContext, ValidationRule};
use crate::{ast::*, visit::*};

/// Validate that a document uses all the variables it defines at least once.
///
/// See [`ValidationRule`]
/// [Reference](https://spec.graphql.org/draft/#sec-All-Variables-Used)
#[derive(Default)]
pub struct NoUnusedVariables<'a> {
variables: Vec<&'a str>,
used_variables: Vec<&'a str>,
}

impl<'a> ValidationRule<'a> for NoUnusedVariables<'a> {}

impl<'a> Visitor<'a, ValidationContext<'a>> for NoUnusedVariables<'a> {
fn enter_operation(
&mut self,
_ctx: &mut ValidationContext<'a>,
operation: &'a OperationDefinition<'a>,
_info: &VisitInfo
) -> VisitFlow {
operation.variable_definitions.children.iter().for_each(|def| {
self.variables.push(def.variable.name);
});
VisitFlow::Next
}

fn enter_field(&mut self, _ctx: &mut ValidationContext<'a>, field: &'a Field<'a>, _info: &VisitInfo) -> VisitFlow {
field.arguments.children.iter().for_each(|arg| {
if let Value::Variable(var) = arg.value {
self.used_variables.push(var.name);
}
});
VisitFlow::Next
}

fn leave_document(
&mut self,
ctx: &mut ValidationContext<'a>,
_document: &'a Document<'a>,
_info: &VisitInfo
) -> VisitFlow {
self.variables.iter().for_each(|defined_variable| {
if !self.used_variables.contains(defined_variable) {
ctx.add_error("All defined variables must be at least used once");
}
});
VisitFlow::Next
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn used_all_variables() {
let ctx = ASTContext::new();
let document =
Document::parse(&ctx, "query ($x: Int!) { todos(from: $x) { id } }").unwrap();
NoUnusedVariables::validate(&ctx, document).unwrap();
}

#[test]
fn has_unused_variable() {
let ctx = ASTContext::new();
let document = Document::parse(
&ctx,
"query ($x: Int!, $unused: String!) { todos(from: $x) { id } }",
)
.unwrap();
NoUnusedVariables::validate(&ctx, document).unwrap_err();
}
}

0 comments on commit b0cdf31

Please sign in to comment.