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
- Re-attach the doc comment by placing the declaration it documents immediately after it.
- Remove the doc comment if the declaration no longer exists.
- 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
- Never leave a /// comment as the last content in a file or before a non-declaration.
- Use plain // comments for explanatory notes that don't document a declaration.
- When deleting a declaration, delete its doc comment too.
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.