{"record":{"id":"95441c0454ceff2a","repo":"apple/pkl","slug":"danglingdoccomment","errorCode":"danglingDocComment","errorMessage":"Dangling documentation comment.","messagePattern":"Dangling documentation comment\\.","errorType":"exception","errorClass":"GenericParserError","httpStatus":null,"severity":"error","filePath":"pkl-parser/src/main/java/org/pkl/parser/GenericParserImpl.java","lineNumber":207,"sourceCode":"      ff(children);\n    }\n    if (hasModifier) children.add(new Node(NodeType.MODIFIER_LIST, modifiers));\n    return new HeaderResult(hasDocComment, hasAnnotation, hasModifier);\n  }\n\n  private Node parseModuleMember(List<Node> preChildren) {\n    return switch (lookahead) {\n      case IDENTIFIER -> parseClassProperty(preChildren);\n      case TYPE_ALIAS -> parseTypeAlias(preChildren);\n      case CLASS -> parseClass(preChildren);\n      case FUNCTION -> parseClassMethod(preChildren);\n      case EOF -> throw parserError(\"unexpectedEndOfFile\");\n      default -> {\n        if (lookahead.isKeyword()) {\n          throw parserError(\"keywordNotAllowedHere\", lookahead.text());\n        }\n        if (lookahead == Token.DOC_COMMENT) {\n          throw parserError(\"danglingDocComment\");\n        }\n        throw parserError(\"invalidTopLevelToken\");\n      }\n    };\n  }\n\n  private Node parseTypeAlias(List<Node> preChildren) {\n    var headerParts = getHeaderParts(preChildren);\n    var children = new ArrayList<>(headerParts.preffixes);\n    var headers = new ArrayList<Node>();\n    if (headerParts.modifierList != null) {\n      headers.add(headerParts.modifierList);\n    }\n    // typealias keyword\n    headers.add(makeTerminal(next()));\n    ff(headers);\n    headers.add(parseIdentifier());\n    ff(headers);","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-parser/src/main/java/org/pkl/parser/GenericParserImpl.java#L189-L225","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nmodule my.mod\n/// Documents the old setting.\n// after\nmodule my.mod\n/// Documents the setting.\nx = 1","handlingStrategy":"validation","validationCode":"// lint: every /// doc comment must be followed by a declaration line\nconst lines = pklSource.split(\"\\n\");\nlines.forEach((l, i) => {\n  if (l.trim().startsWith(\"///\")) {\n    const next = lines[i + 1] || \"\";\n    if (!/[A-Za-z_@]/.test(next.trim()) || next.trim().startsWith(\"///\")) {\n      // next must start a declaration (identifier or annotation), or be another doc line followed by one\n    }\n  }\n});","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["pkl","parser","doc-comment","syntax"],"backgroundTag":"invalid-syntax","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}