Skip to content

Commit 955f2f2

Browse files
authored
Merge pull request #10671 from Microsoft/new-jsdoc-parser
Remove service's jsdoc parser and enhance parser's jsdoc parser
2 parents 9d8d2b6 + f8f244f commit 955f2f2

File tree

68 files changed

+1288
-912
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1288
-912
lines changed

src/compiler/binder.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ namespace ts {
268268
Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType);
269269
let functionType = <JSDocFunctionType>node.parent;
270270
let index = indexOf(functionType.parameters, node);
271-
return "p" + index;
271+
return "arg" + index;
272272
case SyntaxKind.JSDocTypedefTag:
273273
const parentNode = node.parent && node.parent.parent;
274274
let nameFromParentNode: string;
@@ -540,9 +540,7 @@ namespace ts {
540540
// because the scope of JsDocComment should not be affected by whether the current node is a
541541
// container or not.
542542
if (isInJavaScriptFile(node) && node.jsDocComments) {
543-
for (const jsDocComment of node.jsDocComments) {
544-
bind(jsDocComment);
545-
}
543+
forEach(node.jsDocComments, bind);
546544
}
547545
if (checkUnreachable(node)) {
548546
forEachChild(node, bind);

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5653,12 +5653,13 @@ namespace ts {
56535653
case SyntaxKind.JSDocThisType:
56545654
case SyntaxKind.JSDocOptionalType:
56555655
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode>node).type);
5656+
case SyntaxKind.JSDocRecordType:
5657+
return getTypeFromTypeNode((node as JSDocRecordType).literal);
56565658
case SyntaxKind.FunctionType:
56575659
case SyntaxKind.ConstructorType:
56585660
case SyntaxKind.TypeLiteral:
56595661
case SyntaxKind.JSDocTypeLiteral:
56605662
case SyntaxKind.JSDocFunctionType:
5661-
case SyntaxKind.JSDocRecordType:
56625663
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments);
56635664
// This function assumes that an identifier or qualified name is a type expression
56645665
// Callers should first ensure this by calling isTypeNode

src/compiler/parser.ts

Lines changed: 271 additions & 141 deletions
Large diffs are not rendered by default.

src/compiler/scanner.ts

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,40 +1759,46 @@ namespace ts {
17591759
}
17601760

17611761
startPos = pos;
1762-
1763-
// Eat leading whitespace
1764-
let ch = text.charCodeAt(pos);
1765-
while (pos < end) {
1766-
ch = text.charCodeAt(pos);
1767-
if (isWhiteSpaceSingleLine(ch)) {
1768-
pos++;
1769-
}
1770-
else {
1771-
break;
1772-
}
1773-
}
17741762
tokenPos = pos;
17751763

1764+
const ch = text.charCodeAt(pos);
17761765
switch (ch) {
1766+
case CharacterCodes.tab:
1767+
case CharacterCodes.verticalTab:
1768+
case CharacterCodes.formFeed:
1769+
case CharacterCodes.space:
1770+
while (pos < end && isWhiteSpaceSingleLine(text.charCodeAt(pos))) {
1771+
pos++;
1772+
}
1773+
return token = SyntaxKind.WhitespaceTrivia;
17771774
case CharacterCodes.at:
1778-
return pos += 1, token = SyntaxKind.AtToken;
1775+
pos++;
1776+
return token = SyntaxKind.AtToken;
17791777
case CharacterCodes.lineFeed:
17801778
case CharacterCodes.carriageReturn:
1781-
return pos += 1, token = SyntaxKind.NewLineTrivia;
1779+
pos++;
1780+
return token = SyntaxKind.NewLineTrivia;
17821781
case CharacterCodes.asterisk:
1783-
return pos += 1, token = SyntaxKind.AsteriskToken;
1782+
pos++;
1783+
return token = SyntaxKind.AsteriskToken;
17841784
case CharacterCodes.openBrace:
1785-
return pos += 1, token = SyntaxKind.OpenBraceToken;
1785+
pos++;
1786+
return token = SyntaxKind.OpenBraceToken;
17861787
case CharacterCodes.closeBrace:
1787-
return pos += 1, token = SyntaxKind.CloseBraceToken;
1788+
pos++;
1789+
return token = SyntaxKind.CloseBraceToken;
17881790
case CharacterCodes.openBracket:
1789-
return pos += 1, token = SyntaxKind.OpenBracketToken;
1791+
pos++;
1792+
return token = SyntaxKind.OpenBracketToken;
17901793
case CharacterCodes.closeBracket:
1791-
return pos += 1, token = SyntaxKind.CloseBracketToken;
1794+
pos++;
1795+
return token = SyntaxKind.CloseBracketToken;
17921796
case CharacterCodes.equals:
1793-
return pos += 1, token = SyntaxKind.EqualsToken;
1797+
pos++;
1798+
return token = SyntaxKind.EqualsToken;
17941799
case CharacterCodes.comma:
1795-
return pos += 1, token = SyntaxKind.CommaToken;
1800+
pos++;
1801+
return token = SyntaxKind.CommaToken;
17961802
}
17971803

17981804
if (isIdentifierStart(ch, ScriptTarget.Latest)) {

src/compiler/types.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ namespace ts {
487487
parent?: Node; // Parent node (initialized by binding)
488488
/* @internal */ original?: Node; // The original node if this is an updated node.
489489
/* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
490-
/* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files.
490+
/* @internal */ jsDocComments?: JSDoc[]; // JSDoc for the node, if it has any.
491491
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
492492
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
493493
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
@@ -1555,7 +1555,7 @@ namespace ts {
15551555

15561556
// @kind(SyntaxKind.JSDocRecordType)
15571557
export interface JSDocRecordType extends JSDocType, TypeLiteralNode {
1558-
members: NodeArray<JSDocRecordMember>;
1558+
literal: TypeLiteralNode;
15591559
}
15601560

15611561
// @kind(SyntaxKind.JSDocTypeReference)
@@ -1603,14 +1603,16 @@ namespace ts {
16031603
}
16041604

16051605
// @kind(SyntaxKind.JSDocComment)
1606-
export interface JSDocComment extends Node {
1606+
export interface JSDoc extends Node {
16071607
tags: NodeArray<JSDocTag>;
1608+
comment: string | undefined;
16081609
}
16091610

16101611
// @kind(SyntaxKind.JSDocTag)
16111612
export interface JSDocTag extends Node {
16121613
atToken: Node;
16131614
tagName: Identifier;
1615+
comment: string | undefined;
16141616
}
16151617

16161618
// @kind(SyntaxKind.JSDocTemplateTag)
@@ -1649,9 +1651,13 @@ namespace ts {
16491651

16501652
// @kind(SyntaxKind.JSDocParameterTag)
16511653
export interface JSDocParameterTag extends JSDocTag {
1654+
/** the parameter name, if provided *before* the type (TypeScript-style) */
16521655
preParameterName?: Identifier;
16531656
typeExpression?: JSDocTypeExpression;
1657+
/** the parameter name, if provided *after* the type (JSDoc-standard) */
16541658
postParameterName?: Identifier;
1659+
/** the parameter name, regardless of the location it was provided */
1660+
parameterName: Identifier;
16551661
isBracketed: boolean;
16561662
}
16571663

src/compiler/utilities.ts

Lines changed: 115 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,39 +1426,75 @@ namespace ts {
14261426
return undefined;
14271427
}
14281428

1429-
const jsDocComments = getJSDocComments(node, checkParentVariableStatement);
1430-
if (!jsDocComments) {
1429+
const jsDocTags = getJSDocTags(node, checkParentVariableStatement);
1430+
if (!jsDocTags) {
14311431
return undefined;
14321432
}
14331433

1434-
for (const jsDocComment of jsDocComments) {
1435-
for (const tag of jsDocComment.tags) {
1436-
if (tag.kind === kind) {
1437-
return tag;
1438-
}
1434+
for (const tag of jsDocTags) {
1435+
if (tag.kind === kind) {
1436+
return tag;
14391437
}
14401438
}
14411439
}
14421440

1443-
function getJSDocComments(node: Node, checkParentVariableStatement: boolean): JSDocComment[] {
1444-
if (node.jsDocComments) {
1445-
return node.jsDocComments;
1446-
}
1447-
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
1448-
// /**
1449-
// * @param {number} name
1450-
// * @returns {number}
1451-
// */
1452-
// var x = function(name) { return name.length; }
1441+
function append<T>(previous: T[] | undefined, additional: T[] | undefined): T[] | undefined {
1442+
if (additional) {
1443+
if (!previous) {
1444+
previous = [];
1445+
}
1446+
for (const x of additional) {
1447+
previous.push(x);
1448+
}
1449+
}
1450+
return previous;
1451+
}
1452+
1453+
export function getJSDocComments(node: Node, checkParentVariableStatement: boolean): string[] {
1454+
return getJSDocs(node, checkParentVariableStatement, docs => map(docs, doc => doc.comment), tags => map(tags, tag => tag.comment));
1455+
}
1456+
1457+
function getJSDocTags(node: Node, checkParentVariableStatement: boolean): JSDocTag[] {
1458+
return getJSDocs(node, checkParentVariableStatement, docs => {
1459+
const result: JSDocTag[] = [];
1460+
for (const doc of docs) {
1461+
if (doc.tags) {
1462+
result.push(...doc.tags);
1463+
}
1464+
}
1465+
return result;
1466+
}, tags => tags);
1467+
}
1468+
1469+
function getJSDocs<T>(node: Node, checkParentVariableStatement: boolean, getDocs: (docs: JSDoc[]) => T[], getTags: (tags: JSDocTag[]) => T[]): T[] {
1470+
// TODO: Get rid of getJsDocComments and friends (note the lowercase 's' in Js)
1471+
// TODO: A lot of this work should be cached, maybe. I guess it's only used in services right now...
1472+
let result: T[] = undefined;
1473+
// prepend documentation from parent sources
14531474
if (checkParentVariableStatement) {
1475+
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
1476+
// /**
1477+
// * @param {number} name
1478+
// * @returns {number}
1479+
// */
1480+
// var x = function(name) { return name.length; }
14541481
const isInitializerOfVariableDeclarationInStatement =
1455-
node.parent.kind === SyntaxKind.VariableDeclaration &&
1456-
(<VariableDeclaration>node.parent).initializer === node &&
1482+
isVariableLike(node.parent) &&
1483+
(node.parent).initializer === node &&
14571484
node.parent.parent.parent.kind === SyntaxKind.VariableStatement;
1485+
const isVariableOfVariableDeclarationStatement = isVariableLike(node) &&
1486+
node.parent.parent.kind === SyntaxKind.VariableStatement;
14581487

1459-
const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent : undefined;
1488+
const variableStatementNode =
1489+
isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent :
1490+
isVariableOfVariableDeclarationStatement ? node.parent.parent :
1491+
undefined;
14601492
if (variableStatementNode) {
1461-
return variableStatementNode.jsDocComments;
1493+
result = append(result, getJSDocs(variableStatementNode, checkParentVariableStatement, getDocs, getTags));
1494+
}
1495+
if (node.kind === SyntaxKind.ModuleDeclaration &&
1496+
node.parent && node.parent.kind === SyntaxKind.ModuleDeclaration) {
1497+
result = append(result, getJSDocs(node.parent, checkParentVariableStatement, getDocs, getTags));
14621498
}
14631499

14641500
// Also recognize when the node is the RHS of an assignment expression
@@ -1469,16 +1505,62 @@ namespace ts {
14691505
(parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken &&
14701506
parent.parent.kind === SyntaxKind.ExpressionStatement;
14711507
if (isSourceOfAssignmentExpressionStatement) {
1472-
return parent.parent.jsDocComments;
1508+
result = append(result, getJSDocs(parent.parent, checkParentVariableStatement, getDocs, getTags));
14731509
}
14741510

14751511
const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment;
14761512
if (isPropertyAssignmentExpression) {
1477-
return parent.jsDocComments;
1513+
result = append(result, getJSDocs(parent, checkParentVariableStatement, getDocs, getTags));
1514+
}
1515+
1516+
// Pull parameter comments from declaring function as well
1517+
if (node.kind === SyntaxKind.Parameter) {
1518+
const paramTags = getJSDocParameterTag(node as ParameterDeclaration, checkParentVariableStatement);
1519+
if (paramTags) {
1520+
result = append(result, getTags(paramTags));
1521+
}
14781522
}
14791523
}
14801524

1481-
return undefined;
1525+
if (isVariableLike(node) && node.initializer) {
1526+
result = append(result, getJSDocs(node.initializer, /*checkParentVariableStatement*/ false, getDocs, getTags));
1527+
}
1528+
1529+
if (node.jsDocComments) {
1530+
if (result) {
1531+
result = append(result, getDocs(node.jsDocComments));
1532+
}
1533+
else {
1534+
return getDocs(node.jsDocComments);
1535+
}
1536+
}
1537+
1538+
return result;
1539+
}
1540+
1541+
function getJSDocParameterTag(param: ParameterDeclaration, checkParentVariableStatement: boolean): JSDocTag[] {
1542+
const func = param.parent as FunctionLikeDeclaration;
1543+
const tags = getJSDocTags(func, checkParentVariableStatement);
1544+
if (!param.name) {
1545+
// this is an anonymous jsdoc param from a `function(type1, type2): type3` specification
1546+
const i = func.parameters.indexOf(param);
1547+
const paramTags = filter(tags, tag => tag.kind === SyntaxKind.JSDocParameterTag);
1548+
if (paramTags && 0 <= i && i < paramTags.length) {
1549+
return [paramTags[i]];
1550+
}
1551+
}
1552+
else if (param.name.kind === SyntaxKind.Identifier) {
1553+
const name = (param.name as Identifier).text;
1554+
const paramTags = filter(tags, tag => tag.kind === SyntaxKind.JSDocParameterTag && (tag as JSDocParameterTag).parameterName.text === name);
1555+
if (paramTags) {
1556+
return paramTags;
1557+
}
1558+
}
1559+
else {
1560+
// TODO: it's a destructured parameter, so it should look up an "object type" series of multiple lines
1561+
// But multi-line object types aren't supported yet either
1562+
return undefined;
1563+
}
14821564
}
14831565

14841566
export function getJSDocTypeTag(node: Node): JSDocTypeTag {
@@ -1499,17 +1581,15 @@ namespace ts {
14991581
// annotation.
15001582
const parameterName = (<Identifier>parameter.name).text;
15011583

1502-
const jsDocComments = getJSDocComments(parameter.parent, /*checkParentVariableStatement*/ true);
1503-
if (jsDocComments) {
1504-
for (const jsDocComment of jsDocComments) {
1505-
for (const tag of jsDocComment.tags) {
1506-
if (tag.kind === SyntaxKind.JSDocParameterTag) {
1507-
const parameterTag = <JSDocParameterTag>tag;
1508-
const name = parameterTag.preParameterName || parameterTag.postParameterName;
1509-
if (name.text === parameterName) {
1510-
return parameterTag;
1511-
}
1512-
}
1584+
const jsDocTags = getJSDocTags(parameter.parent, /*checkParentVariableStatement*/ true);
1585+
if (!jsDocTags) {
1586+
return undefined;
1587+
}
1588+
for (const tag of jsDocTags) {
1589+
if (tag.kind === SyntaxKind.JSDocParameterTag) {
1590+
const parameterTag = <JSDocParameterTag>tag;
1591+
if (parameterTag.parameterName.text === parameterName) {
1592+
return parameterTag;
15131593
}
15141594
}
15151595
}

0 commit comments

Comments
 (0)