Skip to content

Commit 81ce532

Browse files
committed
Change how typedef tag is parsed
1 parent 5f9fa69 commit 81ce532

File tree

3 files changed

+103
-132
lines changed

3 files changed

+103
-132
lines changed

src/compiler/binder.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1702,8 +1702,9 @@ namespace ts {
17021702
case SyntaxKind.PropertyDeclaration:
17031703
case SyntaxKind.PropertySignature:
17041704
case SyntaxKind.JSDocRecordMember:
1705-
case SyntaxKind.JSDocPropertyTag:
17061705
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property | ((<PropertyDeclaration>node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
1706+
case SyntaxKind.JSDocPropertyTag:
1707+
return bindJSDocProperty(<JSDocPropertyTag>node);
17071708
case SyntaxKind.PropertyAssignment:
17081709
case SyntaxKind.ShorthandPropertyAssignment:
17091710
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
@@ -2085,6 +2086,10 @@ namespace ts {
20852086
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
20862087
}
20872088

2089+
function bindJSDocProperty(node: JSDocPropertyTag) {
2090+
return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
2091+
}
2092+
20882093
// reachability checks
20892094

20902095
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {

src/compiler/parser.ts

Lines changed: 94 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ namespace ts {
406406
visitNode(cbNode, (<JSDocTypedefTag>node).name) ||
407407
visitNode(cbNode, (<JSDocTypedefTag>node).type);
408408
case SyntaxKind.JSDocTypeLiteral:
409-
return visitNodes(cbNodes, (<JSDocTypeLiteral>node).members);
409+
return visitNodes(cbNodes, (<JSDocTypeLiteral>node).jsDocPropertyTags);
410410
case SyntaxKind.JSDocPropertyTag:
411411
return visitNode(cbNode, (<JSDocPropertyTag>node).typeExpression) ||
412412
visitNode(cbNode, (<JSDocPropertyTag>node).name);
@@ -4113,9 +4113,9 @@ namespace ts {
41134113
const isAsync = !!(node.flags & NodeFlags.Async);
41144114
node.name =
41154115
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
4116-
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
4117-
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
4118-
parseOptionalIdentifier();
4116+
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
4117+
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
4118+
parseOptionalIdentifier();
41194119

41204120
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
41214121
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
@@ -6066,9 +6066,6 @@ namespace ts {
60666066
Debug.assert(end <= content.length);
60676067

60686068
let tags: NodeArray<JSDocTag>;
6069-
let currentParentJSDocTag: JSDocParentTag;
6070-
let currentParentJSDocTagEnd: number;
6071-
60726069
let result: JSDocComment;
60736070

60746071
// Check for /** (JSDoc opening part)
@@ -6125,10 +6122,6 @@ namespace ts {
61256122
nextJSDocToken();
61266123
}
61276124

6128-
if (currentParentJSDocTag) {
6129-
finishCurrentParentTag();
6130-
}
6131-
61326125
result = createJSDocComment();
61336126

61346127
});
@@ -6152,40 +6145,6 @@ namespace ts {
61526145
}
61536146
}
61546147

6155-
function finishCurrentParentTag(): void {
6156-
if (!currentParentJSDocTag) {
6157-
return;
6158-
}
6159-
6160-
if (currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) {
6161-
const typedefTag = <JSDocTypedefTag>currentParentJSDocTag;
6162-
if (typedefTag.jsDocTypeTag) {
6163-
if (typedefTag.jsDocTypeTag.typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
6164-
const typeTagtype = <JSDocTypeReference>typedefTag.jsDocTypeTag.typeExpression.type;
6165-
if ((typeTagtype.name.kind !== SyntaxKind.Identifier) ||
6166-
(<Identifier>typeTagtype.name).text !== "Object") {
6167-
typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type;
6168-
}
6169-
}
6170-
else {
6171-
typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type;
6172-
}
6173-
}
6174-
if (!typedefTag.type && typedefTag.jsDocPropertyTags && typedefTag.jsDocPropertyTags.length > 0) {
6175-
const pos = typedefTag.jsDocPropertyTags[0].pos;
6176-
const end = typedefTag.jsDocPropertyTags[typedefTag.jsDocPropertyTags.length - 1].end;
6177-
const jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, pos);
6178-
jsdocTypeLiteral.members = <NodeArray<JSDocPropertyTag>>[];
6179-
addRange(jsdocTypeLiteral.members, typedefTag.jsDocPropertyTags);
6180-
typedefTag.type = finishNode(jsdocTypeLiteral, end);
6181-
}
6182-
}
6183-
6184-
addTag(finishNode(currentParentJSDocTag, currentParentJSDocTagEnd));
6185-
currentParentJSDocTag = undefined;
6186-
currentParentJSDocTagEnd = undefined;
6187-
}
6188-
61896148
function parseTag(): void {
61906149
Debug.assert(token === SyntaxKind.AtToken);
61916150
const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos());
@@ -6198,34 +6157,23 @@ namespace ts {
61986157
}
61996158

62006159
const tag = handleTag(atToken, tagName) || handleUnknownTag(atToken, tagName);
6201-
if (!currentParentJSDocTag) {
6202-
addTag(tag);
6203-
}
6160+
addTag(tag);
62046161
}
62056162

62066163
function handleTag(atToken: Node, tagName: Identifier): JSDocTag {
62076164
if (tagName) {
62086165
switch (tagName.text) {
62096166
case "param":
6210-
finishCurrentParentTag();
62116167
return handleParamTag(atToken, tagName);
62126168
case "return":
62136169
case "returns":
6214-
finishCurrentParentTag();
62156170
return handleReturnTag(atToken, tagName);
62166171
case "template":
6217-
finishCurrentParentTag();
62186172
return handleTemplateTag(atToken, tagName);
62196173
case "type":
6220-
// @typedef tag is allowed to have one @type tag, therefore seeing
6221-
// a @type tag may not indicate the end of the current parent tag.
62226174
return handleTypeTag(atToken, tagName);
62236175
case "typedef":
6224-
finishCurrentParentTag();
62256176
return handleTypedefTag(atToken, tagName);
6226-
case "property":
6227-
case "prop":
6228-
return handlePropertyTag(atToken, tagName);
62296177
}
62306178
}
62316179

@@ -6251,25 +6199,6 @@ namespace ts {
62516199
}
62526200
}
62536201

6254-
function addToCurrentParentTag(tag: JSDocTag): void {
6255-
if (!currentParentJSDocTag) {
6256-
return;
6257-
}
6258-
switch (tag.kind) {
6259-
case SyntaxKind.JSDocPropertyTag:
6260-
if (!currentParentJSDocTag.jsDocPropertyTags) {
6261-
currentParentJSDocTag.jsDocPropertyTags = <NodeArray<JSDocPropertyTag>>[];
6262-
}
6263-
currentParentJSDocTag.jsDocPropertyTags.push(<JSDocPropertyTag>tag);
6264-
break;
6265-
case SyntaxKind.JSDocTypeTag:
6266-
if (!currentParentJSDocTag.jsDocTypeTag) {
6267-
currentParentJSDocTag.jsDocTypeTag = <JSDocTypeTag>tag;
6268-
}
6269-
break;
6270-
}
6271-
}
6272-
62736202
function tryParseTypeExpression(): JSDocTypeExpression {
62746203
if (token !== SyntaxKind.OpenBraceToken) {
62756204
return undefined;
@@ -6345,32 +6274,14 @@ namespace ts {
63456274
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);
63466275
}
63476276

6348-
let result = <JSDocTypeTag>createNode(SyntaxKind.JSDocTypeTag, atToken.pos);
6277+
const result = <JSDocTypeTag>createNode(SyntaxKind.JSDocTypeTag, atToken.pos);
63496278
result.atToken = atToken;
63506279
result.tagName = tagName;
63516280
result.typeExpression = tryParseTypeExpression();
6352-
result = finishNode(result);
6353-
6354-
if (currentParentJSDocTag && currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) {
6355-
const parentTag = <JSDocTypedefTag>currentParentJSDocTag;
6356-
if (!parentTag.typeExpression && !parentTag.jsDocTypeTag) {
6357-
parentTag.jsDocTypeTag = result;
6358-
currentParentJSDocTagEnd = scanner.getStartPos();
6359-
return result;
6360-
}
6361-
}
6362-
// If this @type tag is not part of the current parent tag, then
6363-
// it denotes the end of the current parent tag.
6364-
finishCurrentParentTag();
6365-
return result;
6281+
return finishNode(result);
63666282
}
63676283

63686284
function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag {
6369-
if (!currentParentJSDocTag) {
6370-
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_cannot_be_used_independently_as_a_top_level_JSDoc_tag, tagName.text);
6371-
return undefined;
6372-
}
6373-
63746285
const typeExpression = tryParseTypeExpression();
63756286
skipWhitespace();
63766287
const name = parseJSDocIdentifierName();
@@ -6379,17 +6290,12 @@ namespace ts {
63796290
return undefined;
63806291
}
63816292

6382-
let result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
6293+
const result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
63836294
result.atToken = atToken;
63846295
result.tagName = tagName;
63856296
result.name = name;
63866297
result.typeExpression = typeExpression;
6387-
result.type = typeExpression.type;
6388-
result = finishNode(result);
6389-
6390-
addToCurrentParentTag(result);
6391-
currentParentJSDocTagEnd = scanner.getStartPos();
6392-
return undefined;
6298+
return finishNode(result);
63936299
}
63946300

63956301
function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
@@ -6413,32 +6319,99 @@ namespace ts {
64136319
}
64146320
}
64156321

6416-
const result = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
6417-
result.atToken = atToken;
6418-
result.tagName = tagName;
6419-
result.name = name;
6420-
result.typeExpression = typeExpression;
6322+
const typedefTag = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
6323+
typedefTag.atToken = atToken;
6324+
typedefTag.tagName = tagName;
6325+
typedefTag.name = name;
6326+
typedefTag.typeExpression = typeExpression;
64216327

6422-
if (typeExpression && typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
6423-
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
6424-
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
6425-
const name = <Identifier>jsDocTypeReference.name;
6426-
if (name.text === "Object") {
6427-
currentParentJSDocTag = result;
6328+
if (typeExpression) {
6329+
if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
6330+
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
6331+
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
6332+
const name = <Identifier>jsDocTypeReference.name;
6333+
if (name.text === "Object") {
6334+
typedefTag.type = scanChildTags();
6335+
}
64286336
}
64296337
}
6338+
if (!typedefTag.type) {
6339+
typedefTag.type = typeExpression.type;
6340+
}
6341+
}
6342+
else {
6343+
typedefTag.type = scanChildTags();
64306344
}
6431-
else if (!typeExpression) {
6432-
currentParentJSDocTag = result;
6345+
6346+
return finishNode(typedefTag);
6347+
6348+
function scanChildTags(): JSDocTypeLiteral {
6349+
const jsDocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos());
6350+
let resumePos = scanner.getStartPos();
6351+
let canParseTag = true;
6352+
let seenAsterisk = false;
6353+
let parentTagTerminated = false;
6354+
6355+
while (token !== SyntaxKind.EndOfFileToken && !parentTagTerminated) {
6356+
nextJSDocToken();
6357+
switch (token) {
6358+
case SyntaxKind.AtToken:
6359+
if (canParseTag) {
6360+
parentTagTerminated = !tryParseChildTag(jsDocTypeLiteral);
6361+
}
6362+
seenAsterisk = false;
6363+
break;
6364+
case SyntaxKind.NewLineTrivia:
6365+
resumePos = scanner.getStartPos() - 1;
6366+
canParseTag = true;
6367+
seenAsterisk = false;
6368+
break;
6369+
case SyntaxKind.AsteriskToken:
6370+
if (seenAsterisk) {
6371+
canParseTag = false;
6372+
}
6373+
seenAsterisk = true;
6374+
break;
6375+
case SyntaxKind.Identifier:
6376+
canParseTag = false;
6377+
case SyntaxKind.EndOfFileToken:
6378+
break;
6379+
}
6380+
}
6381+
scanner.setTextPos(resumePos);
6382+
return finishNode(jsDocTypeLiteral);
64336383
}
6384+
}
6385+
6386+
function tryParseChildTag(parentTag: JSDocTypeLiteral): boolean {
6387+
Debug.assert(token === SyntaxKind.AtToken);
6388+
const atToken = createNode(SyntaxKind.AtToken, scanner.getStartPos());
6389+
atToken.end = scanner.getTextPos();
6390+
nextJSDocToken();
64346391

6435-
if (!currentParentJSDocTag) {
6436-
result.type = result.typeExpression.type;
6437-
return finishNode(result);
6392+
const tagName = parseJSDocIdentifierName();
6393+
if (!tagName) {
6394+
return false;
64386395
}
64396396

6440-
currentParentJSDocTagEnd = scanner.getStartPos();
6441-
return undefined;
6397+
switch (tagName.text) {
6398+
case "type":
6399+
if (parentTag.jsDocTypeTag) {
6400+
// already has a @type tag, terminate the parent tag now.
6401+
return false;
6402+
}
6403+
parentTag.jsDocTypeTag = handleTypeTag(atToken, tagName);
6404+
return true;
6405+
case "prop":
6406+
case "property":
6407+
if (!parentTag.jsDocPropertyTags) {
6408+
parentTag.jsDocPropertyTags = <NodeArray<JSDocPropertyTag>>[];
6409+
}
6410+
const propertyTag = handlePropertyTag(atToken, tagName);
6411+
parentTag.jsDocPropertyTags.push(propertyTag);
6412+
return true;
6413+
}
6414+
return false;
64426415
}
64436416

64446417
function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {

src/compiler/types.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,29 +1517,22 @@ namespace ts {
15171517
}
15181518

15191519
// @kind(SyntaxKind.JSDocTypedefTag)
1520-
export interface JSDocTypedefTag extends JSDocTag, Declaration, JSDocParentTag {
1520+
export interface JSDocTypedefTag extends JSDocTag, Declaration {
15211521
name: Identifier;
15221522
typeExpression?: JSDocTypeExpression;
15231523
type: JSDocType;
15241524
}
15251525

1526-
export interface JSDocParentTag extends JSDocTag {
1527-
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
1528-
jsDocTypeTag?: JSDocTypeTag;
1529-
}
1530-
15311526
// @kind(SyntaxKind.JSDocPropertyTag)
15321527
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
15331528
name: Identifier;
15341529
typeExpression: JSDocTypeExpression;
1535-
// Add a "type" property here to make the interface compatible
1536-
// with the "VariableLikeDeclaration" definition
1537-
type: TypeNode;
15381530
}
15391531

15401532
// @kind(SyntaxKind.JSDocTypeLiteral)
15411533
export interface JSDocTypeLiteral extends JSDocType {
1542-
members: NodeArray<TypeElement>;
1534+
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
1535+
jsDocTypeTag?: JSDocTypeTag;
15431536
}
15441537

15451538
// @kind(SyntaxKind.JSDocParameterTag)

0 commit comments

Comments
 (0)