Skip to content

Commit 2e58435

Browse files
Add tests for parsing JSDoc comments.
1 parent 604988c commit 2e58435

File tree

3 files changed

+666
-254
lines changed

3 files changed

+666
-254
lines changed

src/compiler/parser.ts

Lines changed: 86 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -5389,18 +5389,20 @@ module ts {
53895389
// Parses out a JSDoc type expression. The starting position should be right at the open
53905390
// curly in the type expression. Returns 'undefined' if it encounters any errors while parsing.
53915391
/* @internal */
5392-
export function parseJSDocTypeExpression(content: string, start?: number, length?: number): JSDocType {
5392+
export function parseJSDocTypeExpression(content: string, start?: number, length?: number): JSDocTypeExpression {
53935393
let scanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ true, content, /*onError:*/ undefined, start, length);
53945394

53955395
// Prime the first token for us to start processing.
53965396
let token = nextToken();
53975397
let error = false;
53985398

5399+
let result = <JSDocTypeExpression>createNode(SyntaxKind.JSDocTypeExpression);
5400+
53995401
parseExpected(SyntaxKind.OpenBraceToken);
5400-
let type = parseJSDocType();
5402+
result.type = parseJSDocType();
54015403
parseExpected(SyntaxKind.CloseBraceToken);
54025404

5403-
return error ? undefined : type;
5405+
return error ? undefined : finishNode(result);
54045406

54055407
function nextToken(): SyntaxKind {
54065408
return token = scanner.scan();
@@ -5738,10 +5740,12 @@ module ts {
57385740

57395741
let error = false;
57405742
let type : JSDocType;
5741-
let parameterTypes: JSDocType[];
5743+
let parameters: JSDocParameter[];
57425744
let returnType: JSDocType;
57435745
let typeParameterNames: string[];
57445746

5747+
let pos: number;
5748+
57455749
if (length >= "/** */".length) {
57465750
if (content.charCodeAt(start) === CharacterCodes.slash &&
57475751
content.charCodeAt(start + 1) === CharacterCodes.asterisk &&
@@ -5753,9 +5757,21 @@ module ts {
57535757
let canParseTag = true;
57545758
let seenAsterisk = true;
57555759

5756-
for (let i = start + 3; !error && i < end; i++) {
5757-
let ch = content.charCodeAt(i);
5760+
for (pos = start + 3; !error && pos < end; ) {
5761+
let ch = content.charCodeAt(pos);
5762+
5763+
if (ch === CharacterCodes.at && canParseTag) {
5764+
// Don't update 'pos' in this block. that will be handled inside the
5765+
// parseTag function.
5766+
5767+
parseTag();
5768+
5769+
// Once we parse out a tag, we cannot keep parsing out tags on this line.
5770+
canParseTag = false;
5771+
continue;
5772+
}
57585773

5774+
pos++;
57595775
if (isLineBreak(ch)) {
57605776
// After a line break, we can parse a tag, and we haven't seen as asterisk
57615777
// on the next line yet.
@@ -5769,14 +5785,6 @@ module ts {
57695785
continue;
57705786
}
57715787

5772-
if (ch === CharacterCodes.at && canParseTag) {
5773-
i = parseTag(i);
5774-
5775-
// Once we parse out a tag, we cannot keep parsing out tags on this line.
5776-
canParseTag = false;
5777-
continue;
5778-
}
5779-
57805788
// Ignore the first asterisk on a line.
57815789
if (ch === CharacterCodes.asterisk) {
57825790
if (seenAsterisk) {
@@ -5799,126 +5807,125 @@ module ts {
57995807
return error ? undefined : createJSDocComment();
58005808

58015809
function createJSDocComment(): JSDocComment {
5802-
if (!returnType && !type && !parameterTypes && !typeParameterNames) {
5810+
if (!returnType && !type && !parameters && !typeParameterNames) {
58035811
return undefined;
58045812
}
58055813

5806-
return { returnType, type, parameterTypes, typeParameterNames };
5814+
return { returnType, type, parameters, typeParameterNames };
58075815
}
58085816

5809-
function skipWhitespace(pos: number): number {
5817+
function skipWhitespace(): void {
58105818
while (pos < end && isWhiteSpace(content.charCodeAt(pos))) {
58115819
pos++;
58125820
}
5813-
5814-
return pos;
58155821
}
58165822

5817-
function parseTag(pos: number): number {
5823+
function parseTag(): void {
58185824
Debug.assert(content.charCodeAt(pos) === CharacterCodes.at);
5825+
5826+
// Skip the @ sign.
5827+
pos++;
58195828

5820-
// Scan forward to figure out the tag name.
5821-
for (var i = pos + 1; i < end; i++) {
5822-
if (!isIdentifierStart(content.charCodeAt(i), ScriptTarget.Latest)) {
5823-
break;
5824-
}
5825-
}
5826-
5827-
let tagName = content.substring(pos + 1, i);
5829+
let tagName = scanIdentifier();
58285830
switch (tagName) {
58295831
case "param":
5830-
return handleParamTag(i);
5832+
return handleParamTag();
58315833
case "return":
5832-
return handleReturnTag(i);
5834+
return handleReturnTag();
58335835
case "template":
5834-
return handleTemplateTag(i);
5836+
return handleTemplateTag();
58355837
case "type":
5836-
return handleTypeTag(i);
5838+
return handleTypeTag();
58375839
}
5838-
5839-
return i;
58405840
}
58415841

5842-
function handleParamTag(pos: number) {
5843-
let typeExpr = parseJSDocTypeExpression(content, pos, end - pos);
5844-
if (!typeExpr) {
5842+
function parseType(): JSDocType {
5843+
let typeExpression = parseJSDocTypeExpression(content, pos, end - pos);
5844+
if (!typeExpression) {
58455845
error = true;
5846-
return pos;
5846+
return undefined;
5847+
}
5848+
5849+
pos = typeExpression.end;
5850+
return typeExpression.type;
5851+
}
5852+
5853+
function handleParamTag() {
5854+
let type = parseType();
5855+
if (!type) {
5856+
return;
58475857
}
58485858

5849-
parameterTypes = parameterTypes || [];
5850-
parameterTypes.push(typeExpr);
5851-
return typeExpr.end;
5859+
skipWhitespace();
5860+
5861+
let name = scanIdentifier();
5862+
parameters = parameters || [];
5863+
parameters.push({ name, type });
58525864
}
58535865

5854-
function handleReturnTag(pos: number) {
5866+
function handleReturnTag(): void {
58555867
if (!returnType) {
5856-
let typeExpr = parseJSDocTypeExpression(content, pos, end - pos);
5857-
if (typeExpr) {
5858-
returnType = typeExpr;
5859-
return typeExpr.end;
5868+
returnType = parseType();
5869+
if (returnType) {
5870+
return;
58605871
}
58615872
}
58625873

58635874
error = true;
5864-
return pos;
58655875
}
58665876

5867-
function handleTypeTag(pos: number) {
5877+
function handleTypeTag(): void {
58685878
if (!type) {
5869-
let typeExpr = parseJSDocTypeExpression(content, pos, end - pos);
5870-
if (typeExpr) {
5871-
type = typeExpr;
5872-
return typeExpr.end;
5879+
type = parseType();
5880+
if (type) {
5881+
return;
58735882
}
58745883
}
58755884

58765885
error = true;
5877-
return pos;
58785886
}
58795887

5880-
function handleTemplateTag(pos: number): number {
5888+
function handleTemplateTag(): void {
58815889
if (!typeParameterNames) {
58825890
typeParameterNames = [];
58835891

58845892
while (!error) {
5885-
pos = skipWhitespace(pos);
5886-
scanTypeParameterName();
5887-
pos = skipWhitespace(pos);
5893+
skipWhitespace();
5894+
let name = scanIdentifier();
5895+
typeParameterNames.push(name);
5896+
5897+
skipWhitespace();
58885898
if (content.charCodeAt(pos) !== CharacterCodes.comma) {
5889-
break;
5899+
return;
58905900
}
5891-
}
58925901

5893-
if (typeParameterNames.length > 0) {
5894-
return pos;
5902+
pos++;
58955903
}
58965904
}
58975905

58985906
error = true;
5899-
return pos;
5900-
5901-
function scanTypeParameterName() {
5902-
for (var i = pos; i < end; i++) {
5903-
let ch = content.charCodeAt(i);
5904-
if (i === pos && isIdentifierStart(ch, ScriptTarget.Latest)) {
5905-
continue;
5906-
}
5907-
else if (i > pos && isIdentifierPart(ch, ScriptTarget.Latest)) {
5908-
continue;
5909-
}
5907+
}
59105908

5911-
break;
5909+
function scanIdentifier(): string {
5910+
let startPos = pos;
5911+
for (;pos < end; pos++) {
5912+
let ch = content.charCodeAt(pos);
5913+
if (pos === startPos && isIdentifierStart(ch, ScriptTarget.Latest)) {
5914+
continue;
59125915
}
5913-
5914-
if (i === pos) {
5915-
error = true;
5916-
return;
5916+
else if (pos > startPos && isIdentifierPart(ch, ScriptTarget.Latest)) {
5917+
continue;
59175918
}
59185919

5919-
typeParameterNames.push(content.substring(pos, i));
5920-
pos = i;
5920+
break;
5921+
}
5922+
5923+
if (startPos === pos) {
5924+
error = true;
5925+
return;
59215926
}
5927+
5928+
return content.substring(startPos, pos);
59225929
}
59235930
}
59245931

src/compiler/types.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ module ts {
270270
SourceFile,
271271

272272
// JSDoc nodes.
273+
JSDocTypeExpression,
273274
// The * type.
274275
JSDocAllType,
275276
// The ? type.
@@ -1010,6 +1011,11 @@ module ts {
10101011
hasTrailingNewLine?: boolean;
10111012
}
10121013

1014+
// represents a top level: { type } expression in a JSDoc comment.
1015+
export interface JSDocTypeExpression extends Node {
1016+
type: JSDocType;
1017+
}
1018+
10131019
export interface JSDocType extends Node {
10141020
_jsDocTypeBrand: any;
10151021
}
@@ -1069,9 +1075,14 @@ module ts {
10691075
type?: JSDocType
10701076
}
10711077

1078+
export interface JSDocParameter {
1079+
name: string;
1080+
type: JSDocType;
1081+
}
1082+
10721083
export interface JSDocComment {
10731084
type?: JSDocType;
1074-
parameterTypes?: JSDocType[];
1085+
parameters?: JSDocParameter[];
10751086
returnType?: JSDocType;
10761087
typeParameterNames?: string[];
10771088
}

0 commit comments

Comments
 (0)