docs: private void checkTemplate(TemplateNode node, TemplateRegistry templateRegistry) {
      ListMultimap<String, SourceLocation> dataKeys = ArrayListMultimap.create();
  
      for (VarRefNode varRefNode : SoyTreeUtils.getAllNodesOfType(node, VarRefNode.class)) {
        if (varRefNode.isPossibleHeaderVar()) {
          dataKeys.put(varRefNode.getName(), varRefNode.getSourceLocation());
        }
      }
  
      IndirectParamsInfo ipi =
          new IndirectParamsCalculator(templateRegistry)
              .calculateIndirectParams(templateRegistry.getMetadata(node));
  
      Set<String> allHeaderVarNames = new HashSet<>();
      List<TemplateHeaderVarDefn> unusedParams = new ArrayList<>();
      // Process @param header variables.
      for (TemplateParam param : node.getAllParams()) {
        allHeaderVarNames.add(param.name());
        if (dataKeys.containsKey(param.name())) {
          // Good: Declared and referenced in template. We remove these from dataKeys so
          // that at the end of the for-loop, dataKeys will only contain the keys that are referenced
          // but not declared in SoyDoc.
          dataKeys.removeAll(param.name());
        } else if (ipi.paramKeyToCalleesMultimap.containsKey(param.name())
            || ipi.mayHaveIndirectParamsInExternalCalls
            || ipi.mayHaveIndirectParamsInExternalDelCalls) {
          // Good: Declared in SoyDoc and either (a) used in a call that passes all data or (b) used
          // in an external call or delcall that passes all data, which may need the param (we can't
          // verify).
        } else {
          // Bad: Declared in SoyDoc but not referenced in template.
          unusedParams.add(param);
        }
      }
  
      List<TemplateHeaderVarDefn> unusedStateVars = new ArrayList<>();
      // Process @state header variables.
      if (node instanceof TemplateElementNode) {
        TemplateElementNode el = (TemplateElementNode) node;
        for (TemplateStateVar stateVar : el.getStateVars()) {
          allHeaderVarNames.add(stateVar.name());
          if (dataKeys.containsKey(stateVar.name())) {
            // Good: declared and referenced in the template.
            dataKeys.removeAll(stateVar.name());
          } else {
            // Bad: declared in the header, but not used.
            unusedStateVars.add(stateVar);
          }
        }
      }
      // At this point, the only keys left in dataKeys are undeclared.
      for (Entry<String, SourceLocation> undeclared : dataKeys.entries()) {
        String extraErrorMessage =
            SoyErrors.getDidYouMeanMessage(allHeaderVarNames, undeclared.getKey());
        errorReporter.report(
            undeclared.getValue(), UNDECLARED_DATA_KEY, undeclared.getKey(), extraErrorMessage);
      }
  
      // Delegate templates can declare unused params because other implementations
      // of the same delegate may need to use those params.
      if (node instanceof TemplateBasicNode) {
        reportUnusedHeaderVars(errorReporter, unusedParams, UNUSED_PARAM);
      }
      if (node instanceof TemplateElementNode) {
        reportUnusedHeaderVars(errorReporter, unusedStateVars, UNUSED_STATE);
      }
    }