@@ -2796,18 +2796,26 @@ namespace ts {
2796
2796
return isRightSideOfPropertyAccess ( node ) ? node . parent : node ;
2797
2797
}
2798
2798
2799
- function climbPastManyPropertyAccesses ( node : Node ) : Node {
2800
- return isRightSideOfPropertyAccess ( node ) ? climbPastManyPropertyAccesses ( node . parent ) : node ;
2799
+ /** Get `C` given `N` if `N` is in the position `class C extends N` or `class C extends foo.N` where `N` is an identifier. */
2800
+ function tryGetClassByExtendingIdentifier ( node : Node ) : ClassLikeDeclaration | undefined {
2801
+ return tryGetClassExtendingExpressionWithTypeArguments ( climbPastPropertyAccess ( node ) . parent ) ;
2801
2802
}
2802
2803
2803
2804
function isCallExpressionTarget ( node : Node ) : boolean {
2804
- node = climbPastPropertyAccess ( node ) ;
2805
- return node && node . parent && node . parent . kind === SyntaxKind . CallExpression && ( < CallExpression > node . parent ) . expression === node ;
2805
+ return isCallOrNewExpressionTarget ( node , SyntaxKind . CallExpression ) ;
2806
2806
}
2807
2807
2808
2808
function isNewExpressionTarget ( node : Node ) : boolean {
2809
- node = climbPastPropertyAccess ( node ) ;
2810
- return node && node . parent && node . parent . kind === SyntaxKind . NewExpression && ( < CallExpression > node . parent ) . expression === node ;
2809
+ return isCallOrNewExpressionTarget ( node , SyntaxKind . NewExpression ) ;
2810
+ }
2811
+
2812
+ function isCallOrNewExpressionTarget ( node : Node , kind : SyntaxKind ) {
2813
+ const target = climbPastPropertyAccess ( node ) ;
2814
+ return target && target . parent && target . parent . kind === kind && ( < CallExpression > target . parent ) . expression === target ;
2815
+ }
2816
+
2817
+ function climbPastManyPropertyAccesses ( node : Node ) : Node {
2818
+ return isRightSideOfPropertyAccess ( node ) ? climbPastManyPropertyAccesses ( node . parent ) : node ;
2811
2819
}
2812
2820
2813
2821
/** Returns a CallLikeExpression where `node` is the target being invoked. */
@@ -4625,7 +4633,7 @@ namespace ts {
4625
4633
const symbolFlags = symbol . flags ;
4626
4634
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar ( symbol , symbolFlags , location ) ;
4627
4635
let hasAddedSymbolInfo : boolean ;
4628
- const isThisExpression : boolean = location . kind === SyntaxKind . ThisKeyword && isExpression ( location ) ;
4636
+ const isThisExpression = location . kind === SyntaxKind . ThisKeyword && isExpression ( location ) ;
4629
4637
let type : Type ;
4630
4638
4631
4639
// Class at constructor site need to be shown as constructor apart from property,method, vars
@@ -6045,6 +6053,7 @@ namespace ts {
6045
6053
case SyntaxKind . Identifier :
6046
6054
case SyntaxKind . ThisKeyword :
6047
6055
// case SyntaxKind.SuperKeyword: TODO:GH#9268
6056
+ case SyntaxKind . ConstructorKeyword :
6048
6057
case SyntaxKind . StringLiteral :
6049
6058
return getReferencedSymbolsForNode ( node , program . getSourceFiles ( ) , findInStrings , findInComments ) ;
6050
6059
}
@@ -6089,6 +6098,8 @@ namespace ts {
6089
6098
return getReferencesForSuperKeyword ( node ) ;
6090
6099
}
6091
6100
6101
+ // `getSymbolAtLocation` normally returns the symbol of the class when given the constructor keyword,
6102
+ // so we have to specify that we want the constructor symbol.
6092
6103
const symbol = typeChecker . getSymbolAtLocation ( node ) ;
6093
6104
6094
6105
if ( ! symbol && node . kind === SyntaxKind . StringLiteral ) {
@@ -6163,7 +6174,7 @@ namespace ts {
6163
6174
} ;
6164
6175
}
6165
6176
6166
- function getAliasSymbolForPropertyNameSymbol ( symbol : Symbol , location : Node ) : Symbol {
6177
+ function getAliasSymbolForPropertyNameSymbol ( symbol : Symbol , location : Node ) : Symbol | undefined {
6167
6178
if ( symbol . flags & SymbolFlags . Alias ) {
6168
6179
// Default import get alias
6169
6180
const defaultImport = getDeclarationOfKind ( symbol , SyntaxKind . ImportClause ) ;
@@ -6189,6 +6200,10 @@ namespace ts {
6189
6200
return undefined ;
6190
6201
}
6191
6202
6203
+ function followAliasIfNecessary ( symbol : Symbol , location : Node ) : Symbol {
6204
+ return getAliasSymbolForPropertyNameSymbol ( symbol , location ) || symbol ;
6205
+ }
6206
+
6192
6207
function getPropertySymbolOfDestructuringAssignment ( location : Node ) {
6193
6208
return isArrayLiteralOrObjectLiteralDestructuringPattern ( location . parent . parent ) &&
6194
6209
typeChecker . getPropertySymbolOfDestructuringAssignment ( < Identifier > location ) ;
@@ -6453,7 +6468,8 @@ namespace ts {
6453
6468
if ( referenceSymbol ) {
6454
6469
const referenceSymbolDeclaration = referenceSymbol . valueDeclaration ;
6455
6470
const shorthandValueSymbol = typeChecker . getShorthandAssignmentValueSymbol ( referenceSymbolDeclaration ) ;
6456
- const relatedSymbol = getRelatedSymbol ( searchSymbols , referenceSymbol , referenceLocation ) ;
6471
+ const relatedSymbol = getRelatedSymbol ( searchSymbols , referenceSymbol , referenceLocation ,
6472
+ /*searchLocationIsConstructor*/ searchLocation . kind === SyntaxKind . ConstructorKeyword ) ;
6457
6473
6458
6474
if ( relatedSymbol ) {
6459
6475
const referencedSymbol = getReferencedSymbol ( relatedSymbol ) ;
@@ -6469,12 +6485,94 @@ namespace ts {
6469
6485
const referencedSymbol = getReferencedSymbol ( shorthandValueSymbol ) ;
6470
6486
referencedSymbol . references . push ( getReferenceEntryFromNode ( referenceSymbolDeclaration . name ) ) ;
6471
6487
}
6488
+ else if ( searchLocation . kind === SyntaxKind . ConstructorKeyword ) {
6489
+ findAdditionalConstructorReferences ( referenceSymbol , referenceLocation ) ;
6490
+ }
6472
6491
}
6473
6492
} ) ;
6474
6493
}
6475
6494
6476
6495
return ;
6477
6496
6497
+ /** Adds references when a constructor is used with `new this()` in its own class and `super()` calls in subclasses. */
6498
+ function findAdditionalConstructorReferences ( referenceSymbol : Symbol , referenceLocation : Node ) : void {
6499
+ Debug . assert ( isClassLike ( searchSymbol . valueDeclaration ) ) ;
6500
+
6501
+ const referenceClass = referenceLocation . parent ;
6502
+ if ( referenceSymbol === searchSymbol && isClassLike ( referenceClass ) ) {
6503
+ Debug . assert ( referenceClass . name === referenceLocation ) ;
6504
+ // This is the class declaration containing the constructor.
6505
+ addReferences ( findOwnConstructorCalls ( searchSymbol ) ) ;
6506
+ }
6507
+ else {
6508
+ // If this class appears in `extends C`, then the extending class' "super" calls are references.
6509
+ const classExtending = tryGetClassByExtendingIdentifier ( referenceLocation ) ;
6510
+ if ( classExtending && isClassLike ( classExtending ) && followAliasIfNecessary ( referenceSymbol , referenceLocation ) === searchSymbol ) {
6511
+ addReferences ( superConstructorAccesses ( classExtending ) ) ;
6512
+ }
6513
+ }
6514
+ }
6515
+
6516
+ function addReferences ( references : Node [ ] ) : void {
6517
+ if ( references . length ) {
6518
+ const referencedSymbol = getReferencedSymbol ( searchSymbol ) ;
6519
+ addRange ( referencedSymbol . references , map ( references , getReferenceEntryFromNode ) ) ;
6520
+ }
6521
+ }
6522
+
6523
+ /** `classSymbol` is the class where the constructor was defined.
6524
+ * Reference the constructor and all calls to `new this()`.
6525
+ */
6526
+ function findOwnConstructorCalls ( classSymbol : Symbol ) : Node [ ] {
6527
+ const result : Node [ ] = [ ] ;
6528
+
6529
+ for ( const decl of classSymbol . members [ "__constructor" ] . declarations ) {
6530
+ Debug . assert ( decl . kind === SyntaxKind . Constructor ) ;
6531
+ const ctrKeyword = decl . getChildAt ( 0 ) ;
6532
+ Debug . assert ( ctrKeyword . kind === SyntaxKind . ConstructorKeyword ) ;
6533
+ result . push ( ctrKeyword ) ;
6534
+ }
6535
+
6536
+ forEachProperty ( classSymbol . exports , member => {
6537
+ const decl = member . valueDeclaration ;
6538
+ if ( decl && decl . kind === SyntaxKind . MethodDeclaration ) {
6539
+ const body = ( < MethodDeclaration > decl ) . body ;
6540
+ if ( body ) {
6541
+ forEachDescendantOfKind ( body , SyntaxKind . ThisKeyword , thisKeyword => {
6542
+ if ( isNewExpressionTarget ( thisKeyword ) ) {
6543
+ result . push ( thisKeyword ) ;
6544
+ }
6545
+ } ) ;
6546
+ }
6547
+ }
6548
+ } ) ;
6549
+
6550
+ return result ;
6551
+ }
6552
+
6553
+ /** Find references to `super` in the constructor of an extending class. */
6554
+ function superConstructorAccesses ( cls : ClassLikeDeclaration ) : Node [ ] {
6555
+ const symbol = cls . symbol ;
6556
+ const ctr = symbol . members [ "__constructor" ] ;
6557
+ if ( ! ctr ) {
6558
+ return [ ] ;
6559
+ }
6560
+
6561
+ const result : Node [ ] = [ ] ;
6562
+ for ( const decl of ctr . declarations ) {
6563
+ Debug . assert ( decl . kind === SyntaxKind . Constructor ) ;
6564
+ const body = ( < ConstructorDeclaration > decl ) . body ;
6565
+ if ( body ) {
6566
+ forEachDescendantOfKind ( body , SyntaxKind . SuperKeyword , node => {
6567
+ if ( isCallExpressionTarget ( node ) ) {
6568
+ result . push ( node ) ;
6569
+ }
6570
+ } ) ;
6571
+ }
6572
+ } ;
6573
+ return result ;
6574
+ }
6575
+
6478
6576
function getReferencedSymbol ( symbol : Symbol ) : ReferencedSymbol {
6479
6577
const symbolId = getSymbolId ( symbol ) ;
6480
6578
let index = symbolToIndex [ symbolId ] ;
@@ -6855,16 +6953,17 @@ namespace ts {
6855
6953
}
6856
6954
}
6857
6955
6858
- function getRelatedSymbol ( searchSymbols : Symbol [ ] , referenceSymbol : Symbol , referenceLocation : Node ) : Symbol {
6859
- if ( searchSymbols . indexOf ( referenceSymbol ) >= 0 ) {
6860
- return referenceSymbol ;
6956
+ function getRelatedSymbol ( searchSymbols : Symbol [ ] , referenceSymbol : Symbol , referenceLocation : Node , searchLocationIsConstructor : boolean ) : Symbol | undefined {
6957
+ if ( contains ( searchSymbols , referenceSymbol ) ) {
6958
+ // If we are searching for constructor uses, they must be 'new' expressions.
6959
+ return ( ! searchLocationIsConstructor || isNewExpressionTarget ( referenceLocation ) ) && referenceSymbol ;
6861
6960
}
6862
6961
6863
6962
// If the reference symbol is an alias, check if what it is aliasing is one of the search
6864
6963
// symbols but by looking up for related symbol of this alias so it can handle multiple level of indirectness.
6865
6964
const aliasSymbol = getAliasSymbolForPropertyNameSymbol ( referenceSymbol , referenceLocation ) ;
6866
6965
if ( aliasSymbol ) {
6867
- return getRelatedSymbol ( searchSymbols , aliasSymbol , referenceLocation ) ;
6966
+ return getRelatedSymbol ( searchSymbols , aliasSymbol , referenceLocation , searchLocationIsConstructor ) ;
6868
6967
}
6869
6968
6870
6969
// If the reference location is in an object literal, try to get the contextual type for the
@@ -8388,6 +8487,15 @@ namespace ts {
8388
8487
} ;
8389
8488
}
8390
8489
8490
+ function forEachDescendantOfKind ( node : Node , kind : SyntaxKind , action : ( node : Node ) => void ) {
8491
+ forEachChild ( node , child => {
8492
+ if ( child . kind === kind ) {
8493
+ action ( child ) ;
8494
+ }
8495
+ forEachDescendantOfKind ( child , kind , action ) ;
8496
+ } ) ;
8497
+ }
8498
+
8391
8499
/* @internal */
8392
8500
export function getNameTable ( sourceFile : SourceFile ) : Map < number > {
8393
8501
if ( ! sourceFile . nameTable ) {
0 commit comments