@@ -670,6 +670,12 @@ namespace ts {
670
670
case SyntaxKind . CallExpression :
671
671
bindCallExpressionFlow ( < CallExpression > node ) ;
672
672
break ;
673
+ case SyntaxKind . JSDocComment :
674
+ bindJSDocComment ( < JSDoc > node ) ;
675
+ break ;
676
+ case SyntaxKind . JSDocTypedefTag :
677
+ bindJSDocTypedefTag ( < JSDocTypedefTag > node ) ;
678
+ break ;
673
679
default :
674
680
bindEachChild ( node ) ;
675
681
break ;
@@ -1335,6 +1341,26 @@ namespace ts {
1335
1341
}
1336
1342
}
1337
1343
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
+
1338
1364
function bindCallExpressionFlow ( node : CallExpression ) {
1339
1365
// If the target of the call expression is a function expression or arrow function we have
1340
1366
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
@@ -1874,6 +1900,18 @@ namespace ts {
1874
1900
}
1875
1901
node . parent = parent ;
1876
1902
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
+
1877
1915
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
1878
1916
// and then potentially add the symbol to an appropriate symbol table. Possible
1879
1917
// destination symbol tables are:
@@ -1908,6 +1946,27 @@ namespace ts {
1908
1946
inStrictMode = saveInStrictMode ;
1909
1947
}
1910
1948
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
+
1911
1970
function updateStrictModeStatementList ( statements : NodeArray < Statement > ) {
1912
1971
if ( ! inStrictMode ) {
1913
1972
for ( const statement of statements ) {
0 commit comments