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

View file

@ -406,7 +406,7 @@ namespace ts {
visitNode(cbNode, (<JSDocTypedefTag>node).name) || visitNode(cbNode, (<JSDocTypedefTag>node).name) ||
visitNode(cbNode, (<JSDocTypedefTag>node).type); visitNode(cbNode, (<JSDocTypedefTag>node).type);
case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JSDocTypeLiteral:
return visitNodes(cbNodes, (<JSDocTypeLiteral>node).members); return visitNodes(cbNodes, (<JSDocTypeLiteral>node).jsDocPropertyTags);
case SyntaxKind.JSDocPropertyTag: case SyntaxKind.JSDocPropertyTag:
return visitNode(cbNode, (<JSDocPropertyTag>node).typeExpression) || return visitNode(cbNode, (<JSDocPropertyTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocPropertyTag>node).name); visitNode(cbNode, (<JSDocPropertyTag>node).name);
@ -4113,9 +4113,9 @@ namespace ts {
const isAsync = !!(node.flags & NodeFlags.Async); const isAsync = !!(node.flags & NodeFlags.Async);
node.name = node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) : isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) : isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) : isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier(); parseOptionalIdentifier();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node); fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false); node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
@ -6066,9 +6066,6 @@ namespace ts {
Debug.assert(end <= content.length); Debug.assert(end <= content.length);
let tags: NodeArray<JSDocTag>; let tags: NodeArray<JSDocTag>;
let currentParentJSDocTag: JSDocParentTag;
let currentParentJSDocTagEnd: number;
let result: JSDocComment; let result: JSDocComment;
// Check for /** (JSDoc opening part) // Check for /** (JSDoc opening part)
@ -6125,10 +6122,6 @@ namespace ts {
nextJSDocToken(); nextJSDocToken();
} }
if (currentParentJSDocTag) {
finishCurrentParentTag();
}
result = createJSDocComment(); 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 { function parseTag(): void {
Debug.assert(token === SyntaxKind.AtToken); Debug.assert(token === SyntaxKind.AtToken);
const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos()); const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos());
@ -6198,34 +6157,23 @@ namespace ts {
} }
const tag = handleTag(atToken, tagName) || handleUnknownTag(atToken, tagName); const tag = handleTag(atToken, tagName) || handleUnknownTag(atToken, tagName);
if (!currentParentJSDocTag) { addTag(tag);
addTag(tag);
}
} }
function handleTag(atToken: Node, tagName: Identifier): JSDocTag { function handleTag(atToken: Node, tagName: Identifier): JSDocTag {
if (tagName) { if (tagName) {
switch (tagName.text) { switch (tagName.text) {
case "param": case "param":
finishCurrentParentTag();
return handleParamTag(atToken, tagName); return handleParamTag(atToken, tagName);
case "return": case "return":
case "returns": case "returns":
finishCurrentParentTag();
return handleReturnTag(atToken, tagName); return handleReturnTag(atToken, tagName);
case "template": case "template":
finishCurrentParentTag();
return handleTemplateTag(atToken, tagName); return handleTemplateTag(atToken, tagName);
case "type": 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); return handleTypeTag(atToken, tagName);
case "typedef": case "typedef":
finishCurrentParentTag();
return handleTypedefTag(atToken, tagName); 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 { function tryParseTypeExpression(): JSDocTypeExpression {
if (token !== SyntaxKind.OpenBraceToken) { if (token !== SyntaxKind.OpenBraceToken) {
return undefined; return undefined;
@ -6345,32 +6274,14 @@ namespace ts {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text); 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.atToken = atToken;
result.tagName = tagName; result.tagName = tagName;
result.typeExpression = tryParseTypeExpression(); result.typeExpression = tryParseTypeExpression();
result = finishNode(result); return 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;
} }
function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag { 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(); const typeExpression = tryParseTypeExpression();
skipWhitespace(); skipWhitespace();
const name = parseJSDocIdentifierName(); const name = parseJSDocIdentifierName();
@ -6379,17 +6290,12 @@ namespace ts {
return undefined; return undefined;
} }
let result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos); const result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
result.atToken = atToken; result.atToken = atToken;
result.tagName = tagName; result.tagName = tagName;
result.name = name; result.name = name;
result.typeExpression = typeExpression; result.typeExpression = typeExpression;
result.type = typeExpression.type; return finishNode(result);
result = finishNode(result);
addToCurrentParentTag(result);
currentParentJSDocTagEnd = scanner.getStartPos();
return undefined;
} }
function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag { function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
@ -6413,32 +6319,99 @@ namespace ts {
} }
} }
const result = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos); const typedefTag = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
result.atToken = atToken; typedefTag.atToken = atToken;
result.tagName = tagName; typedefTag.tagName = tagName;
result.name = name; typedefTag.name = name;
result.typeExpression = typeExpression; typedefTag.typeExpression = typeExpression;
if (typeExpression && typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { if (typeExpression) {
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type; if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) { const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
const name = <Identifier>jsDocTypeReference.name; if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
if (name.text === "Object") { const name = <Identifier>jsDocTypeReference.name;
currentParentJSDocTag = result; if (name.text === "Object") {
typedefTag.type = scanChildTags();
}
} }
} }
if (!typedefTag.type) {
typedefTag.type = typeExpression.type;
}
} }
else if (!typeExpression) { else {
currentParentJSDocTag = result; typedefTag.type = scanChildTags();
} }
if (!currentParentJSDocTag) { return finishNode(typedefTag);
result.type = result.typeExpression.type;
return finishNode(result); 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(); switch (tagName.text) {
return undefined; 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 { function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {

View file

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