code: function getFragmentVariables(
    fragment: ReaderFragment,
    rootVariables: Variables,
    argumentVariables: Variables,
  ): Variables {
    let variables;
    fragment.argumentDefinitions.forEach(definition => {
      if (argumentVariables.hasOwnProperty(definition.name)) {
        return;
      }
      variables = variables || {...argumentVariables};
      switch (definition.kind) {
        case 'LocalArgument':
          variables[definition.name] = definition.defaultValue;
          break;
        case 'RootArgument':
          if (!rootVariables.hasOwnProperty(definition.name)) {
            /*
             * Global variables passed as values of @arguments are not required to
             * be declared unless they are used by the callee fragment or a
             * descendant. In this case, the root variable may not be defined when
             * resolving the callee's variables. The value is explicitly set to
             * undefined to conform to the check in
             * RelayStoreUtils.getStableVariableValue() that variable keys are all
             * present.
             */
            variables[definition.name] = undefined;
            break;
          }
          variables[definition.name] = rootVariables[definition.name];
          break;
        default:
          (definition: empty);
          invariant(
            false,
            'RelayConcreteVariables: Unexpected node kind `%s` in fragment `%s`.',
            definition.kind,
            fragment.name,
          );
      }
docs: Determines the variables that are in scope for a fragment given the variables
  in scope at the root query as well as any arguments applied at the fragment
  spread via `@arguments`.
  
  Note that this is analagous to determining function arguments given a function call.