apple/pkl · error · GenericParserError

danglingDocComment

danglingDocComment

Error message

Dangling documentation comment.

What it means

The parser throws danglingDocComment ("Dangling documentation comment.") when a /// doc comment appears at a top-level position where no declaration follows to attach to. parseModuleMember encounters Token.DOC_COMMENT where a member start is expected, meaning the doc comment has nothing (valid) to document.

Source

Thrown at pkl-parser/src/main/java/org/pkl/parser/GenericParserImpl.java:207

      ff(children);
    }
    if (hasModifier) children.add(new Node(NodeType.MODIFIER_LIST, modifiers));
    return new HeaderResult(hasDocComment, hasAnnotation, hasModifier);
  }

  private Node parseModuleMember(List<Node> preChildren) {
    return switch (lookahead) {
      case IDENTIFIER -> parseClassProperty(preChildren);
      case TYPE_ALIAS -> parseTypeAlias(preChildren);
      case CLASS -> parseClass(preChildren);
      case FUNCTION -> parseClassMethod(preChildren);
      case EOF -> throw parserError("unexpectedEndOfFile");
      default -> {
        if (lookahead.isKeyword()) {
          throw parserError("keywordNotAllowedHere", lookahead.text());
        }
        if (lookahead == Token.DOC_COMMENT) {
          throw parserError("danglingDocComment");
        }
        throw parserError("invalidTopLevelToken");
      }
    };
  }

  private Node parseTypeAlias(List<Node> preChildren) {
    var headerParts = getHeaderParts(preChildren);
    var children = new ArrayList<>(headerParts.preffixes);
    var headers = new ArrayList<Node>();
    if (headerParts.modifierList != null) {
      headers.add(headerParts.modifierList);
    }
    // typealias keyword
    headers.add(makeTerminal(next()));
    ff(headers);
    headers.add(parseIdentifier());
    ff(headers);

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Re-attach the doc comment by placing the declaration it documents immediately after it.
  2. Remove the doc comment if the declaration no longer exists.
  3. If you just want a regular comment, use `//` line comments instead of `///` doc comments, which must precede a declaration.

Example fix

// before
module my.mod
/// Documents the old setting.
// after
module my.mod
/// Documents the setting.
x = 1
Defensive patterns

Strategy: validation

Validate before calling

// lint: every /// doc comment must be followed by a declaration line
const lines = pklSource.split("\n");
lines.forEach((l, i) => {
  if (l.trim().startsWith("///")) {
    const next = lines[i + 1] || "";
    if (!/[A-Za-z_@]/.test(next.trim()) || next.trim().startsWith("///")) {
      // next must start a declaration (identifier or annotation), or be another doc line followed by one
    }
  }
});

Prevention

When it happens

Trigger: A /// comment is the last thing in a module body; a /// comment sits between the headers and an invalid/keyword token; a /// comment followed by a blank construct it cannot attach to (e.g. followed directly by another statement type that isn't a documentable member).

Common situations: Deleting or renaming the declaration a doc comment documented, leaving the comment orphaned; editors auto-inserting `///` above the cursor; a comment left at the end of a file after a member was moved elsewhere.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/95441c0454ceff2a. Report an issue: GitHub.