docs: private void visit(
        HtmlMatcherTagNode tagNode,
        Map<Equivalence.Wrapper<ExprNode>, Boolean> exprValueMap,
        HtmlStack stack) {
      HtmlTagNode tag = (HtmlTagNode) tagNode.getSoyNode().get();
      TagName openTagName = tag.getTagName();
      HtmlStack prev = stack;
      switch (tagNode.getTagKind()) {
        case VOID_TAG:
          HtmlOpenTagNode voidTag = (HtmlOpenTagNode) tag;
          // Report errors for non-void tags that are self-closing.
          // For void tags, we don't care if they are self-closing or not. But when we visit
          // a HtmlCloseTagNode we will throw an error if it is a void tag.
          // Ignore this check if we are currently in a foreign content (svg).
          if (!stack.inForeignContent
              && !openTagName.isDefinitelyVoid()
              && voidTag.isSelfClosing()
              && openTagName.isStatic()) {
            errorReporter.report(
                voidTag.getSourceLocation(),
                INVALID_SELF_CLOSING_TAG,
                openTagName.getStaticTagName());
          }
          break;
        case OPEN_TAG:
          HtmlOpenTagNode openTag = (HtmlOpenTagNode) tag;
          if (openTagName.isForeignContent() && stack.inForeignContent) {
            errorReporter.report(openTag.getSourceLocation(), makeSoyErrorKind(NESTED_SVG));
          }
          // In a case where an open tag can close another open tag (ie <p><p> or <li><li>),
          // check if this is possible by peeking the stack and inject a tag before the open tag.
          if (!prev.isEmpty()) {
            HtmlOpenTagNode optionalTag = stack.tagNode;
            if (optionalTag.getTagName().isDefinitelyOptional()) {
              if (TagName.checkOpenTagClosesOptional(
                  openTag.getTagName(), optionalTag.getTagName())) {
                injectCloseTag(optionalTag, openTag, idGenerator);
                prev = prev.pop();
              }
            }
          }
          prev =
              prev.push(openTag, stack.inForeignContent || openTag.getTagName().isForeignContent());
          break;
        case CLOSE_TAG:
          HtmlCloseTagNode closeTag = (HtmlCloseTagNode) tag;
          // Report an error if this node is a void tag. Void tag should never be closed.
          if (closeTag.getTagName().isDefinitelyVoid()) {
            errorReporter.report(
                closeTag.getTagName().getTagLocation(),
                INVALID_CLOSE_TAG,
                closeTag.getTagName().getStaticTagName());
            break;
          }
          // This is for cases similar to {block}</p>{/block}
          if (stack.isEmpty()) {
            errorReporter.report(
                closeTag.getSourceLocation(), makeSoyErrorKind(UNEXPECTED_CLOSE_TAG));
            break;
          }
          prev = stack;
          while (!prev.isEmpty()) {
            HtmlOpenTagNode nextOpenTag = prev.tagNode;
            if (nextOpenTag.getTagName().equals(closeTag.getTagName())) {
              annotationMap.put(nextOpenTag, Optional.of(closeTag));
              annotationMap.put(closeTag, Optional.of(nextOpenTag));
              prev = prev.pop();
              break;
            } else if (nextOpenTag.getTagName().isDefinitelyOptional()
                && TagName.checkCloseTagClosesOptional(
                    closeTag.getTagName(), nextOpenTag.getTagName())) {
              // Close tag closes an optional open tag (e.g. <li> ... </ul>). Inject a synthetic
              // close tag that matches `openTag`.
              injectCloseTag(nextOpenTag, closeTag, idGenerator);
              prev = prev.pop();
            } else {
              annotationMap.put(nextOpenTag, INVALID_NODE);
              errorReporter.report(
                  closeTag.getSourceLocation(),
                  makeSoyErrorKind(UNEXPECTED_CLOSE_TAG_KNOWN),
                  nextOpenTag.getTagName(),
                  nextOpenTag.getSourceLocation());
              prev = prev.pop();
            }
          }
          break;
      }
      Optional<HtmlMatcherGraphNode> nextNode = tagNode.getNodeForEdgeKind(EdgeKind.TRUE_EDGE);
      if (nextNode.isPresent()) {
        visit(nextNode.get(), exprValueMap, prev);
      } else {
        checkUnusedTags(prev);
      }
    }