@@ -670,6 +670,12 @@ namespace ts {
670670 case SyntaxKind . CallExpression :
671671 bindCallExpressionFlow ( < CallExpression > node ) ;
672672 break ;
673+ case SyntaxKind . JSDocComment :
674+ bindJSDocComment ( < JSDoc > node ) ;
675+ break ;
676+ case SyntaxKind . JSDocTypedefTag :
677+ bindJSDocTypedefTag ( < JSDocTypedefTag > node ) ;
678+ break ;
673679 default :
674680 bindEachChild ( node ) ;
675681 break ;
@@ -1335,6 +1341,26 @@ namespace ts {
13351341 }
13361342 }
13371343
1344+ function bindJSDocComment ( node : JSDoc ) {
1345+ forEachChild ( node , n => {
1346+ if ( n . kind !== SyntaxKind . JSDocTypedefTag ) {
1347+ bind ( n ) ;
1348+ }
1349+ } ) ;
1350+ }
1351+
1352+ function bindJSDocTypedefTag ( node : JSDocTypedefTag ) {
1353+ forEachChild ( node , n => {
1354+ // if the node has a fullName "A.B.C", that means symbol "C" was already bound
1355+ // when we visit "fullName"; so when we visit the name "C" as the next child of
1356+ // the jsDocTypedefTag, we should skip binding it.
1357+ if ( node . fullName && n === node . name && node . fullName . kind !== SyntaxKind . Identifier ) {
1358+ return ;
1359+ }
1360+ bind ( n ) ;
1361+ } ) ;
1362+ }
1363+
13381364 function bindCallExpressionFlow ( node : CallExpression ) {
13391365 // If the target of the call expression is a function expression or arrow function we have
13401366 // an immediately invoked function expression (IIFE). Initialize the flowNode property to
@@ -1874,6 +1900,18 @@ namespace ts {
18741900 }
18751901 node . parent = parent ;
18761902 const saveInStrictMode = inStrictMode ;
1903+
1904+ // Even though in the AST the jsdoc @typedef node belongs to the current node,
1905+ // its symbol might be in the same scope with the current node's symbol. Consider:
1906+ //
1907+ // /** @typedef {string | number } MyType */
1908+ // function foo();
1909+ //
1910+ // Here the current node is "foo", which is a container, but the scope of "MyType" should
1911+ // not be inside "foo". Therefore we always bind @typedef before bind the parent node,
1912+ // and skip binding this tag later when binding all the other jsdoc tags.
1913+ bindJSDocTypedefTagIfAny ( node ) ;
1914+
18771915 // First we bind declaration nodes to a symbol if possible. We'll both create a symbol
18781916 // and then potentially add the symbol to an appropriate symbol table. Possible
18791917 // destination symbol tables are:
@@ -1908,6 +1946,27 @@ namespace ts {
19081946 inStrictMode = saveInStrictMode ;
19091947 }
19101948
1949+ function bindJSDocTypedefTagIfAny ( node : Node ) {
1950+ if ( ! node . jsDoc ) {
1951+ return ;
1952+ }
1953+
1954+ for ( const jsDoc of node . jsDoc ) {
1955+ if ( ! jsDoc . tags ) {
1956+ continue ;
1957+ }
1958+
1959+ for ( const tag of jsDoc . tags ) {
1960+ if ( tag . kind === SyntaxKind . JSDocTypedefTag ) {
1961+ const savedParent = parent ;
1962+ parent = jsDoc ;
1963+ bind ( tag ) ;
1964+ parent = savedParent ;
1965+ }
1966+ }
1967+ }
1968+ }
1969+
19111970 function updateStrictModeStatementList ( statements : NodeArray < Statement > ) {
19121971 if ( ! inStrictMode ) {
19131972 for ( const statement of statements ) {
0 commit comments