@@ -669,6 +669,12 @@ namespace ts {
669669 case SyntaxKind . CallExpression :
670670 bindCallExpressionFlow ( < CallExpression > node ) ;
671671 break ;
672+ case SyntaxKind . JSDocComment :
673+ bindJSDocComment ( < JSDoc > node ) ;
674+ break ;
675+ case SyntaxKind . JSDocTypedefTag :
676+ bindJSDocTypedefTag ( < JSDocTypedefTag > node ) ;
677+ break ;
672678 default :
673679 bindEachChild ( node ) ;
674680 break ;
@@ -1298,6 +1304,26 @@ namespace ts {
12981304 }
12991305 }
13001306
1307+ function bindJSDocComment ( node : JSDoc ) {
1308+ forEachChild ( node , n => {
1309+ if ( n . kind !== SyntaxKind . JSDocTypedefTag ) {
1310+ bind ( n ) ;
1311+ }
1312+ } ) ;
1313+ }
1314+
1315+ function bindJSDocTypedefTag ( node : JSDocTypedefTag ) {
1316+ forEachChild ( node , n => {
1317+ // if the node has a fullName "A.B.C", that means symbol "C" was already bond
1318+ // when we visit "fullName"; so when we visit the name "C" as the next child of
1319+ // the jsDocTypedefTag, we should skip binding it.
1320+ if ( n === node . name && node . fullName . kind !== SyntaxKind . Identifier ) {
1321+ return ;
1322+ }
1323+ bind ( n ) ;
1324+ } ) ;
1325+ }
1326+
13011327 function bindCallExpressionFlow ( node : CallExpression ) {
13021328 // If the target of the call expression is a function expression or arrow function we have
13031329 // an immediately invoked function expression (IIFE). Initialize the flowNode property to
@@ -1827,6 +1853,18 @@ namespace ts {
18271853 }
18281854 node . parent = parent ;
18291855 const saveInStrictMode = inStrictMode ;
1856+
1857+ // Even though in the AST the jsdoc @typedef node belongs to the current node,
1858+ // its symbol might be in the same scope with the current node's symbol. Consider:
1859+ //
1860+ // /** @typedef {string | number } MyType */
1861+ // function foo();
1862+ //
1863+ // Here the current node is "foo", which is a container, but the scope of "MyType" should
1864+ // not be inside "foo". Therefore we always bind @typedef before bind the parent node,
1865+ // and skip binding this tag later when binding all the other jsdoc tags.
1866+ bindJSDocTypedefTagIfAny ( node ) ;
1867+
18301868 // First we bind declaration nodes to a symbol if possible. We'll both create a symbol
18311869 // and then potentially add the symbol to an appropriate symbol table. Possible
18321870 // destination symbol tables are:
@@ -1861,6 +1899,27 @@ namespace ts {
18611899 inStrictMode = saveInStrictMode ;
18621900 }
18631901
1902+ function bindJSDocTypedefTagIfAny ( node : Node ) {
1903+ if ( ! node . jsDoc ) {
1904+ return ;
1905+ }
1906+
1907+ for ( const jsDoc of node . jsDoc ) {
1908+ if ( ! jsDoc . tags ) {
1909+ continue ;
1910+ }
1911+
1912+ for ( const tag of jsDoc . tags ) {
1913+ if ( tag . kind === SyntaxKind . JSDocTypedefTag ) {
1914+ const savedParent = parent ;
1915+ parent = jsDoc ;
1916+ bind ( tag ) ;
1917+ parent = savedParent ;
1918+ }
1919+ }
1920+ }
1921+ }
1922+
18641923 function updateStrictModeStatementList ( statements : NodeArray < Statement > ) {
18651924 if ( ! inStrictMode ) {
18661925 for ( const statement of statements ) {
0 commit comments