Skip to content

Commit e4d4b0a

Browse files
author
Andy
authored
Handle PropertyAssignment in getCommentOwnerInfo (#25911)
1 parent fd2eb49 commit e4d4b0a

File tree

3 files changed

+62
-40
lines changed

3 files changed

+62
-40
lines changed

src/compiler/utilities.ts

+10
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,16 @@ namespace ts {
142142
return undefined;
143143
}
144144

145+
export function forEachAncestor<T>(node: Node, callback: (n: Node) => T | undefined | "quit"): T | undefined {
146+
while (true) {
147+
const res = callback(node);
148+
if (res === "quit") return undefined;
149+
if (res !== undefined) return res;
150+
if (isSourceFile(node)) return undefined;
151+
node = node.parent;
152+
}
153+
}
154+
145155
/**
146156
* Calls `callback` for each entry in the map, returning the first truthy result.
147157
* Use `map.forEach` instead for normal iteration.

src/services/jsDoc.ts

+44-40
Original file line numberDiff line numberDiff line change
@@ -338,50 +338,54 @@ namespace ts.JsDoc {
338338
readonly parameters?: ReadonlyArray<ParameterDeclaration>;
339339
}
340340
function getCommentOwnerInfo(tokenAtPos: Node): CommentOwnerInfo | undefined {
341-
for (let commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
342-
switch (commentOwner.kind) {
343-
case SyntaxKind.FunctionDeclaration:
344-
case SyntaxKind.FunctionExpression:
345-
case SyntaxKind.MethodDeclaration:
346-
case SyntaxKind.Constructor:
347-
case SyntaxKind.MethodSignature:
348-
const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature;
349-
return { commentOwner, parameters };
350-
351-
case SyntaxKind.ClassDeclaration:
352-
case SyntaxKind.InterfaceDeclaration:
353-
case SyntaxKind.PropertySignature:
354-
case SyntaxKind.EnumDeclaration:
355-
case SyntaxKind.EnumMember:
356-
case SyntaxKind.TypeAliasDeclaration:
357-
return { commentOwner };
358-
359-
case SyntaxKind.VariableStatement: {
360-
const varStatement = <VariableStatement>commentOwner;
361-
const varDeclarations = varStatement.declarationList.declarations;
362-
const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer
363-
? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer!)
364-
: undefined;
365-
return { commentOwner, parameters };
366-
}
341+
return forEachAncestor(tokenAtPos, getCommentOwnerInfoWorker);
342+
}
343+
function getCommentOwnerInfoWorker(commentOwner: Node): CommentOwnerInfo | undefined | "quit" {
344+
switch (commentOwner.kind) {
345+
case SyntaxKind.FunctionDeclaration:
346+
case SyntaxKind.FunctionExpression:
347+
case SyntaxKind.MethodDeclaration:
348+
case SyntaxKind.Constructor:
349+
case SyntaxKind.MethodSignature:
350+
const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature;
351+
return { commentOwner, parameters };
352+
353+
case SyntaxKind.PropertyAssignment:
354+
return getCommentOwnerInfoWorker((commentOwner as PropertyAssignment).initializer);
355+
356+
case SyntaxKind.ClassDeclaration:
357+
case SyntaxKind.InterfaceDeclaration:
358+
case SyntaxKind.PropertySignature:
359+
case SyntaxKind.EnumDeclaration:
360+
case SyntaxKind.EnumMember:
361+
case SyntaxKind.TypeAliasDeclaration:
362+
return { commentOwner };
363+
364+
case SyntaxKind.VariableStatement: {
365+
const varStatement = <VariableStatement>commentOwner;
366+
const varDeclarations = varStatement.declarationList.declarations;
367+
const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer
368+
? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer!)
369+
: undefined;
370+
return { commentOwner, parameters };
371+
}
367372

368-
case SyntaxKind.SourceFile:
369-
return undefined;
373+
case SyntaxKind.SourceFile:
374+
return "quit";
370375

371-
case SyntaxKind.ModuleDeclaration:
372-
// If in walking up the tree, we hit a a nested namespace declaration,
373-
// then we must be somewhere within a dotted namespace name; however we don't
374-
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
375-
return commentOwner.parent.kind === SyntaxKind.ModuleDeclaration ? undefined : { commentOwner };
376+
case SyntaxKind.ModuleDeclaration:
377+
// If in walking up the tree, we hit a a nested namespace declaration,
378+
// then we must be somewhere within a dotted namespace name; however we don't
379+
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
380+
return commentOwner.parent.kind === SyntaxKind.ModuleDeclaration ? undefined : { commentOwner };
376381

377-
case SyntaxKind.BinaryExpression: {
378-
const be = commentOwner as BinaryExpression;
379-
if (getSpecialPropertyAssignmentKind(be) === SpecialPropertyAssignmentKind.None) {
380-
return undefined;
381-
}
382-
const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray;
383-
return { commentOwner, parameters };
382+
case SyntaxKind.BinaryExpression: {
383+
const be = commentOwner as BinaryExpression;
384+
if (getSpecialPropertyAssignmentKind(be) === SpecialPropertyAssignmentKind.None) {
385+
return "quit";
384386
}
387+
const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray;
388+
return { commentOwner, parameters };
385389
}
386390
}
387391
}

tests/cases/fourslash/docCommentTemplateObjectLiteralMethods01.ts

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const multiLineOffset = 12;
1010
//// }
1111
//// /*1*/
1212
//// [1 + 2 + 3 + Math.rand()](x: number, y: string, z = true) { }
13+
//// /*2*/
14+
//// m: function(a) {}
1315
////}
1416

1517
verify.docCommentTemplateAt("0", singleLineOffset, "/** */");
@@ -21,3 +23,9 @@ verify.docCommentTemplateAt("1", multiLineOffset,
2123
* @param y
2224
* @param z
2325
*/`);
26+
27+
verify.docCommentTemplateAt("2", multiLineOffset,
28+
`/**
29+
*
30+
* @param a
31+
*/`);

0 commit comments

Comments
 (0)