-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Description
TypeScript Version: (Whatever is current with VSCode 1.35.1)
Search Terms: JSDoc combining comments
Code:
/**
* @typedef {Object} Foo
*/
/**
* @typedef {Object} Bar
*/
/**
* Description goes here
*/
export default class Baz {
}Expected behavior:
Documentation for Baz does not include information from JSDoc islets that are not connected to the class definition.
Actual behavior:
Documentation for Baz includes information from earlier JSDoc islets that are not connected to the class definition.
Related Issues:
#15106
#19537
#41811 (vscode)
(And possibly a lot of others)
Possible workarounds:
- Place comments that are not attached to code artifacts, such as
@typedef, at the bottom of the file. - Place those comments into separate files and use
{import("")}typings. - Insert dummy code lines inbetween those comments and the code such as
void 0;to force the TypeScript compiler to treat them in isolation.
I understand that others have closed this type of issue before as a won't fix, for instance because:
@mhegazy
this is how the comment system is built. changing that would not be simple,
or:
@mjbvz
This is a limitation of how our JS Doc support is designed. JSDoc comments are merged into the next declaration
I am re-filing this issue again, but with reason to doubt the above.
The decision to treat this "as designed" makes no sense. Clearly it is broken - as in delivering a broken user experience for JSDoc users. And one which runs counter to practically any other sane JSDoc or JSDoc-like parser on market. Microsoft should pave the cowpath here and follow existing convention, not drop back into old misbehavior and towing their own line.
I doubt that - as prior quoted - this "would not be simple" in the sense of requiring large amounts of code to be adapted. Or that there is a hard "limitation" somehow at play in the parser.
The core problem here seems that when parsing JSDoc comments attached to nodes, the entire range of leading comment blocks is processed, using - eventually - the scanner.ts file's getLeadingCommentRanges function, which calls into the reduceEachLeadingCommentRange function.
I.e.
TypeScript/src/compiler/parser.ts
Lines 866 to 871 in 917cd6c
| function addJSDocComment<T extends HasJSDoc>(node: T): T { | |
| Debug.assert(!node.jsDoc); // Should only be called once per node | |
| const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceFile.text), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos)); | |
| if (jsDoc.length) node.jsDoc = jsDoc; | |
| return node; | |
| } |
TypeScript/src/compiler/utilities.ts
Lines 997 to 1010 in 1cbace6
| export function getJSDocCommentRanges(node: Node, text: string) { | |
| const commentRanges = (node.kind === SyntaxKind.Parameter || | |
| node.kind === SyntaxKind.TypeParameter || | |
| node.kind === SyntaxKind.FunctionExpression || | |
| node.kind === SyntaxKind.ArrowFunction || | |
| node.kind === SyntaxKind.ParenthesizedExpression) ? | |
| concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) : | |
| getLeadingCommentRanges(text, node.pos); | |
| // True if the comment starts with '/**' but not if it is '/**/' | |
| return filter(commentRanges, comment => | |
| text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && | |
| text.charCodeAt(comment.pos + 2) === CharacterCodes.asterisk && | |
| text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash); | |
| } |
TypeScript/src/compiler/scanner.ts
Lines 794 to 796 in c3a9429
| export function getLeadingCommentRanges(text: string, pos: number): CommentRange[] | undefined { | |
| return reduceEachLeadingCommentRange(text, pos, appendCommentRange, /*state*/ undefined, /*initial*/ undefined); | |
| } |
The reduceEachLeadingCommentRange combines all preceding comments, also those separated from the code by lines of white-space. A simple fix for this JSDoc parsing bug would be to not do this and to instead ignore comments that are separated from the declaring node by one or more lines of whitespace.
This requires either:
- a separate implementation which traverses bottom-to-top through the comment blocks and halts at the first line of white-space; or
- a switch/flag on the existing function to trigger aforementioned alternate behavior.
Even a more simple and less performant implementation which simply discards disconnected comments after they've already been parsed would atleast be functional. (And have no worse performance than current.)
Either way, it seems to not quite be rocket-science. So why this problem has been marked as "won't fix" / "as designed" before is beyond me.
If there's a particularly hard component here to fix that I am missing, then please also consider this bug report a request to illustrate where that would be, as this was not disclosed when any of the previous issues were filed and - wrongfully, imho - closed.
Thank you.