Skip to content

Commit fde4c18

Browse files
committed
Address more PR comments
1 parent 58ad164 commit fde4c18

File tree

5 files changed

+105
-94
lines changed

5 files changed

+105
-94
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4493,8 +4493,8 @@ namespace ts {
44934493
if (declaration.kind === SyntaxKind.ExportAssignment) {
44944494
return links.type = checkExpression((<ExportAssignment>declaration).expression);
44954495
}
4496-
if (isInJavaScriptFile(declaration) && declaration.kind === SyntaxKind.JSDocPropertyTag && (<JSDocPropertyTag>declaration).typeExpression) {
4497-
return links.type = getTypeFromTypeNode((<JSDocPropertyTag>declaration).typeExpression.type);
4496+
if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.typeExpression) {
4497+
return links.type = getTypeFromTypeNode(declaration.typeExpression.type);
44984498
}
44994499
// Handle variable, parameter or property
45004500
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {

src/compiler/parser.ts

Lines changed: 82 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -412,12 +412,12 @@ namespace ts {
412412
case SyntaxKind.JSDocParameterTag:
413413
case SyntaxKind.JSDocPropertyTag:
414414
if ((node as JSDocPropertyLikeTag).isNameFirst) {
415-
return visitNode(cbNode, (<JSDocPropertyLikeTag>node).fullName) ||
415+
return visitNode(cbNode, (<JSDocPropertyLikeTag>node).name) ||
416416
visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression);
417417
}
418418
else {
419419
return visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression) ||
420-
visitNode(cbNode, (<JSDocPropertyLikeTag>node).fullName);
420+
visitNode(cbNode, (<JSDocPropertyLikeTag>node).name);
421421
}
422422
case SyntaxKind.JSDocReturnTag:
423423
return visitNode(cbNode, (<JSDocReturnTag>node).typeExpression);
@@ -438,7 +438,10 @@ namespace ts {
438438
visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression);
439439
}
440440
case SyntaxKind.JSDocTypeLiteral:
441-
return visitNodes(cbNode, cbNodes, (<JSDocTypeLiteral>node).jsDocPropertyTags);
441+
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
442+
visitNode(cbNode, tag);
443+
}
444+
return;
442445
case SyntaxKind.PartiallyEmittedExpression:
443446
return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression);
444447
}
@@ -1943,14 +1946,18 @@ namespace ts {
19431946
break;
19441947
}
19451948
dotPos = scanner.getStartPos();
1946-
const node: QualifiedName = <QualifiedName>createNode(SyntaxKind.QualifiedName, entity.pos);
1947-
node.left = entity;
1948-
node.right = parseRightSideOfDot(allowReservedWords);
1949-
entity = finishNode(node);
1949+
entity = createQualifiedName(entity, parseRightSideOfDot(allowReservedWords));
19501950
}
19511951
return entity;
19521952
}
19531953

1954+
function createQualifiedName(entity: EntityName, name: Identifier): QualifiedName {
1955+
const node = createNode(SyntaxKind.QualifiedName, entity.pos) as QualifiedName;
1956+
node.left = entity;
1957+
node.right = name;
1958+
return finishNode(node);
1959+
}
1960+
19541961
function parseRightSideOfDot(allowIdentifierNames: boolean): Identifier {
19551962
// Technically a keyword is valid here as all identifiers and keywords are identifier names.
19561963
// However, often we'll encounter this in error situations when the identifier or keyword
@@ -6473,10 +6480,10 @@ namespace ts {
64736480
});
64746481
}
64756482

6476-
function parseBracketNameInPropertyAndParamTag(): { fullName: EntityName, isBracketed: boolean } {
6483+
function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } {
64776484
// Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar'
64786485
const isBracketed = parseOptional(SyntaxKind.OpenBracketToken);
6479-
const fullName = parseJSDocEntityName(/*createIfMissing*/ true);
6486+
const name = parseJSDocEntityName();
64806487
if (isBracketed) {
64816488
skipWhitespace();
64826489

@@ -6488,68 +6495,68 @@ namespace ts {
64886495
parseExpected(SyntaxKind.CloseBracketToken);
64896496
}
64906497

6491-
return { fullName, isBracketed };
6498+
return { name, isBracketed };
64926499
}
64936500

64946501
function isObjectOrObjectArrayTypeReference(node: TypeNode): boolean {
6495-
return node.kind === SyntaxKind.ObjectKeyword ||
6496-
isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.text === "Object" ||
6497-
node.kind === SyntaxKind.ArrayType && isObjectOrObjectArrayTypeReference((node as ArrayTypeNode).elementType);
6502+
switch (node.kind) {
6503+
case SyntaxKind.ObjectKeyword:
6504+
return true;
6505+
case SyntaxKind.ArrayType:
6506+
return isObjectOrObjectArrayTypeReference((node as ArrayTypeNode).elementType);
6507+
default:
6508+
return isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.text === "Object";
6509+
}
64986510
}
64996511

65006512
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse.Parameter): JSDocParameterTag;
65016513
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse.Property): JSDocPropertyTag;
65026514
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse): JSDocPropertyLikeTag {
65036515
let typeExpression = tryParseTypeExpression();
6516+
let isNameFirst = !typeExpression;
65046517
skipWhitespace();
65056518

6506-
const { fullName, isBracketed } = parseBracketNameInPropertyAndParamTag();
6519+
const { name, isBracketed } = parseBracketNameInPropertyAndParamTag();
65076520
skipWhitespace();
65086521

6509-
let preName: EntityName, postName: EntityName;
6510-
if (typeExpression) {
6511-
postName = fullName;
6512-
}
6513-
else {
6514-
preName = fullName;
6522+
if (isNameFirst) {
65156523
typeExpression = tryParseTypeExpression();
65166524
}
65176525

6518-
const result: JSDocPropertyLikeTag = target ?
6526+
const result: JSDocPropertyLikeTag = target === PropertyLikeParse.Parameter ?
65196527
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, atToken.pos) :
65206528
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
6521-
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, fullName);
6529+
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name);
65226530
if (nestedTypeLiteral) {
65236531
typeExpression = nestedTypeLiteral;
6532+
isNameFirst = true;
65246533
}
65256534
result.atToken = atToken;
65266535
result.tagName = tagName;
65276536
result.typeExpression = typeExpression;
6528-
if (typeExpression) {
6529-
result.type = typeExpression.type;
6530-
}
6531-
result.fullName = postName || preName;
6532-
result.name = ts.isIdentifier(result.fullName) ? result.fullName : result.fullName.right;
6533-
result.isNameFirst = !!nestedTypeLiteral || (postName ? false : !!preName);
6537+
result.name = name;
6538+
result.isNameFirst = isNameFirst;
65346539
result.isBracketed = isBracketed;
65356540
return finishNode(result);
65366541

65376542
}
65386543

6539-
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, fullName: EntityName) {
6544+
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName) {
65406545
if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) {
65416546
const typeLiteralExpression = <JSDocTypeExpression>createNode(SyntaxKind.JSDocTypeExpression, scanner.getTokenPos());
6542-
let child: JSDocPropertyLikeTag | false;
6547+
let child: JSDocParameterTag | false;
65436548
let jsdocTypeLiteral: JSDocTypeLiteral;
65446549
const start = scanner.getStartPos();
6545-
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Parameter, fullName))) {
6546-
if (!jsdocTypeLiteral) {
6547-
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
6548-
jsdocTypeLiteral.jsDocPropertyTags = [] as MutableNodeArray<JSDocPropertyTag>;
6550+
let children: JSDocParameterTag[];
6551+
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Parameter, name))) {
6552+
if (!children) {
6553+
children = [];
65496554
}
6550-
(jsdocTypeLiteral.jsDocPropertyTags as MutableNodeArray<JSDocPropertyTag>).push(child as JSDocPropertyTag);
6555+
children.push(child);
65516556
}
6552-
if (jsdocTypeLiteral) {
6557+
if (children) {
6558+
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
6559+
jsdocTypeLiteral.jsDocPropertyTags = children;
65536560
if (typeExpression.type.kind === SyntaxKind.ArrayType) {
65546561
jsdocTypeLiteral.isArrayType = true;
65556562
}
@@ -6678,61 +6685,58 @@ namespace ts {
66786685
}
66796686
}
66806687

6681-
function textsEqual(parent: EntityName, name: EntityName): boolean {
6682-
while (!ts.isIdentifier(parent) || !ts.isIdentifier(name)) {
6683-
if (!ts.isIdentifier(parent) && !ts.isIdentifier(name) && parent.right.text === name.right.text) {
6684-
parent = parent.left;
6685-
name = name.left;
6688+
function textsEqual(a: EntityName, b: EntityName): boolean {
6689+
while (!ts.isIdentifier(a) || !ts.isIdentifier(b)) {
6690+
if (!ts.isIdentifier(a) && !ts.isIdentifier(b) && a.right.text === b.right.text) {
6691+
a = a.left;
6692+
b = b.left;
66866693
}
66876694
else {
66886695
return false;
66896696
}
66906697
}
6691-
return parent.text === name.text;
6698+
return a.text === b.text;
66926699
}
66936700

66946701
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Property): JSDocTypeTag | JSDocPropertyTag | false;
6695-
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Parameter, fullName: EntityName): JSDocPropertyTag | JSDocParameterTag | false;
6696-
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, fullName?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
6697-
let resumePos = scanner.getStartPos();
6702+
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Parameter, name: EntityName): JSDocParameterTag | false;
6703+
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
66986704
let canParseTag = true;
66996705
let seenAsterisk = false;
6700-
while (token() !== SyntaxKind.EndOfFileToken) {
6706+
while (true) {
67016707
nextJSDocToken();
67026708
switch (token()) {
6703-
case SyntaxKind.AtToken:
6704-
if (canParseTag) {
6705-
const child = tryParseChildTag(target);
6706-
if (child && child.kind === SyntaxKind.JSDocParameterTag &&
6707-
(ts.isIdentifier(child.fullName) || !textsEqual(fullName, child.fullName.left))) {
6708-
break;
6709+
case SyntaxKind.AtToken:
6710+
if (canParseTag) {
6711+
const child = tryParseChildTag(target);
6712+
if (child && child.kind === SyntaxKind.JSDocParameterTag &&
6713+
(ts.isIdentifier(child.name) || !textsEqual(name, child.name.left))) {
6714+
return false;
6715+
}
6716+
return child;
67096717
}
6710-
return child;
6711-
}
6712-
seenAsterisk = false;
6713-
break;
6714-
case SyntaxKind.NewLineTrivia:
6715-
resumePos = scanner.getStartPos() - 1;
6716-
canParseTag = true;
6717-
seenAsterisk = false;
6718-
break;
6719-
case SyntaxKind.AsteriskToken:
6720-
if (seenAsterisk) {
6718+
seenAsterisk = false;
6719+
break;
6720+
case SyntaxKind.NewLineTrivia:
6721+
canParseTag = true;
6722+
seenAsterisk = false;
6723+
break;
6724+
case SyntaxKind.AsteriskToken:
6725+
if (seenAsterisk) {
6726+
canParseTag = false;
6727+
}
6728+
seenAsterisk = true;
6729+
break;
6730+
case SyntaxKind.Identifier:
67216731
canParseTag = false;
6722-
}
6723-
seenAsterisk = true;
6724-
break;
6725-
case SyntaxKind.Identifier:
6726-
canParseTag = false;
6727-
break;
6728-
case SyntaxKind.EndOfFileToken:
6729-
break;
6732+
break;
6733+
case SyntaxKind.EndOfFileToken:
6734+
return false;
67306735
}
67316736
}
6732-
scanner.setTextPos(resumePos);
67336737
}
67346738

6735-
function tryParseChildTag(target: PropertyLikeParse, alreadyHasTypeTag?: boolean): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
6739+
function tryParseChildTag(target: PropertyLikeParse): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
67366740
Debug.assert(token() === SyntaxKind.AtToken);
67376741
const atToken = <AtToken>createNode(SyntaxKind.AtToken, scanner.getStartPos());
67386742
atToken.end = scanner.getTextPos();
@@ -6745,7 +6749,7 @@ namespace ts {
67456749
}
67466750
switch (tagName.text) {
67476751
case "type":
6748-
return !alreadyHasTypeTag && target === PropertyLikeParse.Property && parseTypeTag(atToken, tagName);
6752+
return target === PropertyLikeParse.Property && parseTypeTag(atToken, tagName);
67496753
case "prop":
67506754
case "property":
67516755
return target === PropertyLikeParse.Property && parseParameterOrPropertyTag(atToken, tagName, target);
@@ -6801,22 +6805,20 @@ namespace ts {
68016805
return currentToken = scanner.scanJSDocToken();
68026806
}
68036807

6804-
function parseJSDocEntityName(createIfMissing = false): EntityName {
6805-
let entity: EntityName = parseJSDocIdentifierName(createIfMissing);
6808+
function parseJSDocEntityName(): EntityName {
6809+
let entity: EntityName = parseJSDocIdentifierName(/*createIfMissing*/ true);
68066810
if (parseOptional(SyntaxKind.OpenBracketToken)) {
68076811
parseExpected(SyntaxKind.CloseBracketToken);
68086812
// Note that y[] is accepted as an entity name, but the postfix brackets are not saved for checking.
68096813
// Technically usejsdoc.org requires them for specifying a property of a type equivalent to Array<{ x: ...}>
68106814
// but it's not worth it to enforce that restriction.
68116815
}
68126816
while (parseOptional(SyntaxKind.DotToken)) {
6813-
const node: QualifiedName = createNode(SyntaxKind.QualifiedName, entity.pos) as QualifiedName;
6814-
node.left = entity;
6815-
node.right = parseJSDocIdentifierName(createIfMissing);
6817+
const name = parseJSDocIdentifierName(/*createIfMissing*/ true);
68166818
if (parseOptional(SyntaxKind.OpenBracketToken)) {
68176819
parseExpected(SyntaxKind.CloseBracketToken);
68186820
}
6819-
entity = finishNode(node);
6821+
entity = createQualifiedName(entity, name);
68206822
}
68216823
return entity;
68226824
}

src/compiler/types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,10 +2129,9 @@ namespace ts {
21292129
typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
21302130
}
21312131

2132-
export interface JSDocPropertyLikeTag extends JSDocTag, VariableLikeDeclaration {
2132+
export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
21332133
parent: JSDoc;
2134-
fullName?: EntityName;
2135-
name: Identifier;
2134+
name: EntityName;
21362135
typeExpression: JSDocTypeExpression;
21372136
/** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */
21382137
isNameFirst: boolean;
@@ -2149,7 +2148,7 @@ namespace ts {
21492148

21502149
export interface JSDocTypeLiteral extends JSDocType {
21512150
kind: SyntaxKind.JSDocTypeLiteral;
2152-
jsDocPropertyTags?: NodeArray<JSDocPropertyLikeTag>;
2151+
jsDocPropertyTags?: ReadonlyArray<JSDocPropertyLikeTag>;
21532152
jsDocTypeTag?: JSDocTypeTag;
21542153
/** If true, then this type literal represents an *array* of its type. */
21552154
isArrayType?: boolean;

0 commit comments

Comments
 (0)