no_unused_variables.ex (1845B)
1 defmodule Absinthe.Phase.Document.Validation.NoUnusedVariables do 2 @moduledoc false 3 4 # Validates document to ensure that the only variables that are used in a 5 # document are defined on the operation. 6 7 alias Absinthe.{Blueprint, Phase} 8 9 use Absinthe.Phase 10 use Absinthe.Phase.Validation 11 12 @doc """ 13 Run the validation. 14 """ 15 @spec run(Blueprint.t(), Keyword.t()) :: Phase.result_t() 16 def run(input, _options \\ []) do 17 result = Blueprint.prewalk(input, &handle_node(&1, input.operations)) 18 {:ok, result} 19 end 20 21 def handle_node(%Blueprint.Document.VariableDefinition{} = node, operations) do 22 errors = 23 for op <- operations do 24 for var <- op.variable_definitions, var.name == node.name do 25 if Enum.find(op.variable_uses, &(&1.name == var.name)) do 26 [] 27 else 28 [error(node, op)] 29 end 30 end 31 end 32 |> List.flatten() 33 34 case errors do 35 [] -> 36 node 37 38 errors -> 39 %{node | errors: errors ++ node.errors} 40 |> flag_invalid(:unused) 41 end 42 end 43 44 def handle_node(node, _) do 45 node 46 end 47 48 # Generate the error for the node 49 @spec error(Blueprint.Document.VariableDefinition.t(), Blueprint.Document.Operation.t()) :: 50 Phase.Error.t() 51 defp error(node, operation) do 52 %Phase.Error{ 53 phase: __MODULE__, 54 message: error_message(node.name, operation.name), 55 locations: Enum.uniq([node.source_location, operation.source_location]) 56 } 57 end 58 59 @doc """ 60 Generate an error message for an unused variable. 61 """ 62 @spec error_message(String.t(), nil | String.t()) :: String.t() 63 def error_message(name, nil) do 64 ~s(Variable "#{name}" is never used.) 65 end 66 67 def error_message(name, operation_name) do 68 ~s(Variable "#{name}" is never used in operation "#{operation_name}".) 69 end 70 end