Change how typedef tag is parsed

This commit is contained in:
zhengbli 2016-05-25 23:05:20 -07:00
parent 5f9fa69587
commit 81ce532cde
3 changed files with 103 additions and 132 deletions

View file

@ -1702,8 +1702,9 @@ namespace ts {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.JSDocRecordMember:
case SyntaxKind.JSDocPropertyTag:
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property | ((<PropertyDeclaration>node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
case SyntaxKind.JSDocPropertyTag:
return bindJSDocProperty(<JSDocPropertyTag>node);
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
@ -2085,6 +2086,10 @@ namespace ts {
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
}
function bindJSDocProperty(node: JSDocPropertyTag) {
return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
}
// reachability checks
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {

View file

@ -406,7 +406,7 @@ namespace ts {
visitNode(cbNode, (<JSDocTypedefTag>node).name) ||
visitNode(cbNode, (<JSDocTypedefTag>node).type);
case SyntaxKind.JSDocTypeLiteral:
return visitNodes(cbNodes, (<JSDocTypeLiteral>node).members);
return visitNodes(cbNodes, (<JSDocTypeLiteral>node).jsDocPropertyTags);
case SyntaxKind.JSDocPropertyTag:
return visitNode(cbNode, (<JSDocPropertyTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocPropertyTag>node).name);
@ -4113,9 +4113,9 @@ namespace ts {
const isAsync = !!(node.flags & NodeFlags.Async);
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
@ -6066,9 +6066,6 @@ namespace ts {
Debug.assert(end <= content.length);
let tags: NodeArray<JSDocTag>;
let currentParentJSDocTag: JSDocParentTag;
let currentParentJSDocTagEnd: number;
let result: JSDocComment;
// Check for /** (JSDoc opening part)
@ -6125,10 +6122,6 @@ namespace ts {
nextJSDocToken();
}
if (currentParentJSDocTag) {
finishCurrentParentTag();
}
result = createJSDocComment();
});
@ -6152,40 +6145,6 @@ namespace ts {
}
}
function finishCurrentParentTag(): void {
if (!currentParentJSDocTag) {
return;
}
if (currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) {
const typedefTag = <JSDocTypedefTag>currentParentJSDocTag;
if (typedefTag.jsDocTypeTag) {
if (typedefTag.jsDocTypeTag.typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
const typeTagtype = <JSDocTypeReference>typedefTag.jsDocTypeTag.typeExpression.type;
if ((typeTagtype.name.kind !== SyntaxKind.Identifier) ||
(<Identifier>typeTagtype.name).text !== "Object") {
typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type;
}
}
else {
typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type;
}
}
if (!typedefTag.type && typedefTag.jsDocPropertyTags && typedefTag.jsDocPropertyTags.length > 0) {
const pos = typedefTag.jsDocPropertyTags[0].pos;
const end = typedefTag.jsDocPropertyTags[typedefTag.jsDocPropertyTags.length - 1].end;
const jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, pos);
jsdocTypeLiteral.members = <NodeArray<JSDocPropertyTag>>[];
addRange(jsdocTypeLiteral.members, typedefTag.jsDocPropertyTags);
typedefTag.type = finishNode(jsdocTypeLiteral, end);
}
}
addTag(finishNode(currentParentJSDocTag, currentParentJSDocTagEnd));
currentParentJSDocTag = undefined;
currentParentJSDocTagEnd = undefined;
}
function parseTag(): void {
Debug.assert(token === SyntaxKind.AtToken);
const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos());
@ -6198,34 +6157,23 @@ namespace ts {
}
const tag = handleTag(atToken, tagName) || handleUnknownTag(atToken, tagName);
if (!currentParentJSDocTag) {
addTag(tag);
}
addTag(tag);
}
function handleTag(atToken: Node, tagName: Identifier): JSDocTag {
if (tagName) {
switch (tagName.text) {
case "param":
finishCurrentParentTag();
return handleParamTag(atToken, tagName);
case "return":
case "returns":
finishCurrentParentTag();
return handleReturnTag(atToken, tagName);
case "template":
finishCurrentParentTag();
return handleTemplateTag(atToken, tagName);
case "type":
// @typedef tag is allowed to have one @type tag, therefore seeing
// a @type tag may not indicate the end of the current parent tag.
return handleTypeTag(atToken, tagName);
case "typedef":
finishCurrentParentTag();
return handleTypedefTag(atToken, tagName);
case "property":
case "prop":
return handlePropertyTag(atToken, tagName);
}
}
@ -6251,25 +6199,6 @@ namespace ts {
}
}
function addToCurrentParentTag(tag: JSDocTag): void {
if (!currentParentJSDocTag) {
return;
}
switch (tag.kind) {
case SyntaxKind.JSDocPropertyTag:
if (!currentParentJSDocTag.jsDocPropertyTags) {
currentParentJSDocTag.jsDocPropertyTags = <NodeArray<JSDocPropertyTag>>[];
}
currentParentJSDocTag.jsDocPropertyTags.push(<JSDocPropertyTag>tag);
break;
case SyntaxKind.JSDocTypeTag:
if (!currentParentJSDocTag.jsDocTypeTag) {
currentParentJSDocTag.jsDocTypeTag = <JSDocTypeTag>tag;
}
break;
}
}
function tryParseTypeExpression(): JSDocTypeExpression {
if (token !== SyntaxKind.OpenBraceToken) {
return undefined;
@ -6345,32 +6274,14 @@ namespace ts {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);
}
let result = <JSDocTypeTag>createNode(SyntaxKind.JSDocTypeTag, atToken.pos);
const result = <JSDocTypeTag>createNode(SyntaxKind.JSDocTypeTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
result.typeExpression = tryParseTypeExpression();
result = finishNode(result);
if (currentParentJSDocTag && currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) {
const parentTag = <JSDocTypedefTag>currentParentJSDocTag;
if (!parentTag.typeExpression && !parentTag.jsDocTypeTag) {
parentTag.jsDocTypeTag = result;
currentParentJSDocTagEnd = scanner.getStartPos();
return result;
}
}
// If this @type tag is not part of the current parent tag, then
// it denotes the end of the current parent tag.
finishCurrentParentTag();
return result;
return finishNode(result);
}
function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag {
if (!currentParentJSDocTag) {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_cannot_be_used_independently_as_a_top_level_JSDoc_tag, tagName.text);
return undefined;
}
const typeExpression = tryParseTypeExpression();
skipWhitespace();
const name = parseJSDocIdentifierName();
@ -6379,17 +6290,12 @@ namespace ts {
return undefined;
}
let result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
const result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
result.name = name;
result.typeExpression = typeExpression;
result.type = typeExpression.type;
result = finishNode(result);
addToCurrentParentTag(result);
currentParentJSDocTagEnd = scanner.getStartPos();
return undefined;
return finishNode(result);
}
function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
@ -6413,32 +6319,99 @@ namespace ts {
}
}
const result = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
result.name = name;
result.typeExpression = typeExpression;
const typedefTag = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
typedefTag.atToken = atToken;
typedefTag.tagName = tagName;
typedefTag.name = name;
typedefTag.typeExpression = typeExpression;
if (typeExpression && typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
const name = <Identifier>jsDocTypeReference.name;
if (name.text === "Object") {
currentParentJSDocTag = result;
if (typeExpression) {
if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
const name = <Identifier>jsDocTypeReference.name;
if (name.text === "Object") {
typedefTag.type = scanChildTags();
}
}
}
if (!typedefTag.type) {
typedefTag.type = typeExpression.type;
}
}
else if (!typeExpression) {
currentParentJSDocTag = result;
else {
typedefTag.type = scanChildTags();
}
if (!currentParentJSDocTag) {
result.type = result.typeExpression.type;
return finishNode(result);
return finishNode(typedefTag);
function scanChildTags(): JSDocTypeLiteral {
const jsDocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos());
let resumePos = scanner.getStartPos();
let canParseTag = true;
let seenAsterisk = false;
let parentTagTerminated = false;
while (token !== SyntaxKind.EndOfFileToken && !parentTagTerminated) {
nextJSDocToken();
switch (token) {
case SyntaxKind.AtToken:
if (canParseTag) {
parentTagTerminated = !tryParseChildTag(jsDocTypeLiteral);
}
seenAsterisk = false;
break;
case SyntaxKind.NewLineTrivia:
resumePos = scanner.getStartPos() - 1;
canParseTag = true;
seenAsterisk = false;
break;
case SyntaxKind.AsteriskToken:
if (seenAsterisk) {
canParseTag = false;
}
seenAsterisk = true;
break;
case SyntaxKind.Identifier:
canParseTag = false;
case SyntaxKind.EndOfFileToken:
break;
}
}
scanner.setTextPos(resumePos);
return finishNode(jsDocTypeLiteral);
}
}
function tryParseChildTag(parentTag: JSDocTypeLiteral): boolean {
Debug.assert(token === SyntaxKind.AtToken);
const atToken = createNode(SyntaxKind.AtToken, scanner.getStartPos());
atToken.end = scanner.getTextPos();
nextJSDocToken();
const tagName = parseJSDocIdentifierName();
if (!tagName) {
return false;
}
currentParentJSDocTagEnd = scanner.getStartPos();
return undefined;
switch (tagName.text) {
case "type":
if (parentTag.jsDocTypeTag) {
// already has a @type tag, terminate the parent tag now.
return false;
}
parentTag.jsDocTypeTag = handleTypeTag(atToken, tagName);
return true;
case "prop":
case "property":
if (!parentTag.jsDocPropertyTags) {
parentTag.jsDocPropertyTags = <NodeArray<JSDocPropertyTag>>[];
}
const propertyTag = handlePropertyTag(atToken, tagName);
parentTag.jsDocPropertyTags.push(propertyTag);
return true;
}
return false;
}
function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {

View file

@ -1517,29 +1517,22 @@ namespace ts {
}
// @kind(SyntaxKind.JSDocTypedefTag)
export interface JSDocTypedefTag extends JSDocTag, Declaration, JSDocParentTag {
export interface JSDocTypedefTag extends JSDocTag, Declaration {
name: Identifier;
typeExpression?: JSDocTypeExpression;
type: JSDocType;
}
export interface JSDocParentTag extends JSDocTag {
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
jsDocTypeTag?: JSDocTypeTag;
}
// @kind(SyntaxKind.JSDocPropertyTag)
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
name: Identifier;
typeExpression: JSDocTypeExpression;
// Add a "type" property here to make the interface compatible
// with the "VariableLikeDeclaration" definition
type: TypeNode;
}
// @kind(SyntaxKind.JSDocTypeLiteral)
export interface JSDocTypeLiteral extends JSDocType {
members: NodeArray<TypeElement>;
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
jsDocTypeTag?: JSDocTypeTag;
}
// @kind(SyntaxKind.JSDocParameterTag)