@@ -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 }
0 commit comments