@@ -119,6 +119,7 @@ namespace ts {
119119 let languageVersion : ScriptTarget ;
120120 let parent : Node ;
121121 let container : Node ;
122+ let containerContainer : Node ; // Container one level up
122123 let blockScopeContainer : Node ;
123124 let inferenceContainer : Node ;
124125 let lastContainer : Node ;
@@ -187,6 +188,7 @@ namespace ts {
187188 languageVersion = undefined ;
188189 parent = undefined ;
189190 container = undefined ;
191+ containerContainer = undefined ;
190192 blockScopeContainer = undefined ;
191193 inferenceContainer = undefined ;
192194 lastContainer = undefined ;
@@ -224,13 +226,7 @@ namespace ts {
224226 symbol . flags |= symbolFlags ;
225227
226228 node . symbol = symbol ;
227-
228- if ( ! symbol . declarations ) {
229- symbol . declarations = [ node ] ;
230- }
231- else {
232- symbol . declarations . push ( node ) ;
233- }
229+ symbol . declarations = append ( symbol . declarations , node ) ;
234230
235231 if ( symbolFlags & SymbolFlags . HasExports && ! symbol . exports ) {
236232 symbol . exports = createSymbolTable ( ) ;
@@ -486,8 +482,11 @@ namespace ts {
486482 // and block-container. Then after we pop out of processing the children, we restore
487483 // these saved values.
488484 const saveContainer = container ;
485+ const saveContainerContainer = containerContainer ;
489486 const savedBlockScopeContainer = blockScopeContainer ;
490487
488+ containerContainer = container ;
489+
491490 // Depending on what kind of node this is, we may have to adjust the current container
492491 // and block-container. If the current node is a container, then it is automatically
493492 // considered the current block-container as well. Also, for containers that we know
@@ -580,7 +579,9 @@ namespace ts {
580579 else {
581580 bindChildren ( node ) ;
582581 }
582+
583583 container = saveContainer ;
584+ containerContainer = saveContainerContainer ;
584585 blockScopeContainer = savedBlockScopeContainer ;
585586 }
586587
@@ -2327,14 +2328,23 @@ namespace ts {
23272328
23282329 function bindThisPropertyAssignment ( node : BinaryExpression | PropertyAccessExpression ) {
23292330 Debug . assert ( isInJavaScriptFile ( node ) ) ;
2330- const container = getThisContainer ( node , /*includeArrowFunctions*/ false ) ;
2331- switch ( container . kind ) {
2331+ const thisContainer = getThisContainer ( node , /*includeArrowFunctions*/ false ) ;
2332+ switch ( thisContainer . kind ) {
23322333 case SyntaxKind . FunctionDeclaration :
23332334 case SyntaxKind . FunctionExpression :
2335+ let constructorSymbol = thisContainer . symbol ;
2336+ // For `f.prototype.m = function() { this.x = 0; }`, `this.x = 0` should modify `f`'s members, not the function expression.
2337+ if ( isBinaryExpression ( thisContainer . parent ) && thisContainer . parent . operatorToken . kind === SyntaxKind . EqualsToken ) {
2338+ const l = thisContainer . parent . left ;
2339+ if ( isPropertyAccessExpression ( l ) && isPropertyAccessExpression ( l . expression ) && l . expression . name . escapedText === "prototype" && isEntityNameExpression ( l . expression . expression ) ) {
2340+ constructorSymbol = getJSInitializerSymbolFromName ( l . expression . expression , containerContainer ) ;
2341+ }
2342+ }
2343+
23342344 // Declare a 'member' if the container is an ES5 class or ES6 constructor
2335- container . symbol . members = container . symbol . members || createSymbolTable ( ) ;
2345+ constructorSymbol . members = constructorSymbol . members || createSymbolTable ( ) ;
23362346 // It's acceptable for multiple 'this' assignments of the same identifier to occur
2337- declareSymbol ( container . symbol . members , container . symbol , node , SymbolFlags . Property , SymbolFlags . PropertyExcludes & ~ SymbolFlags . Property ) ;
2347+ declareSymbol ( constructorSymbol . members , constructorSymbol , node , SymbolFlags . Property , SymbolFlags . PropertyExcludes & ~ SymbolFlags . Property ) ;
23382348 break ;
23392349
23402350 case SyntaxKind . Constructor :
@@ -2344,10 +2354,13 @@ namespace ts {
23442354 case SyntaxKind . SetAccessor :
23452355 // this.foo assignment in a JavaScript class
23462356 // Bind this property to the containing class
2347- const containingClass = container . parent ;
2348- const symbolTable = hasModifier ( container , ModifierFlags . Static ) ? containingClass . symbol . exports : containingClass . symbol . members ;
2357+ const containingClass = thisContainer . parent ;
2358+ const symbolTable = hasModifier ( thisContainer , ModifierFlags . Static ) ? containingClass . symbol . exports : containingClass . symbol . members ;
23492359 declareSymbol ( symbolTable , containingClass . symbol , node , SymbolFlags . Property , SymbolFlags . None , /*isReplaceableByMethod*/ true ) ;
23502360 break ;
2361+
2362+ default :
2363+ Debug . fail ( Debug . showSyntaxKind ( thisContainer ) ) ;
23512364 }
23522365 }
23532366
@@ -2417,8 +2430,12 @@ namespace ts {
24172430 bindPropertyAssignment ( node . expression , node , /*isPrototypeProperty*/ false ) ;
24182431 }
24192432
2433+ function getJSInitializerSymbolFromName ( name : EntityNameExpression , lookupContainer ?: Node ) : Symbol {
2434+ return getJSInitializerSymbol ( lookupSymbolForPropertyAccess ( name , lookupContainer ) ) ;
2435+ }
2436+
24202437 function bindPropertyAssignment ( name : EntityNameExpression , propertyAccess : PropertyAccessEntityNameExpression , isPrototypeProperty : boolean ) {
2421- let symbol = getJSInitializerSymbol ( lookupSymbolForPropertyAccess ( name ) ) ;
2438+ let symbol = getJSInitializerSymbolFromName ( name ) ;
24222439 let isToplevelNamespaceableInitializer : boolean ;
24232440 if ( isBinaryExpression ( propertyAccess . parent ) ) {
24242441 const isPrototypeAssignment = isPropertyAccessExpression ( propertyAccess . parent . left ) && propertyAccess . parent . left . name . escapedText === "prototype" ;
@@ -2458,9 +2475,9 @@ namespace ts {
24582475 declareSymbol ( symbolTable , symbol , propertyAccess , symbolFlags , symbolExcludes ) ;
24592476 }
24602477
2461- function lookupSymbolForPropertyAccess ( node : EntityNameExpression ) : Symbol | undefined {
2478+ function lookupSymbolForPropertyAccess ( node : EntityNameExpression , lookupContainer : Node = container ) : Symbol | undefined {
24622479 if ( isIdentifier ( node ) ) {
2463- return lookupSymbolForNameWorker ( container , node . escapedText ) ;
2480+ return lookupSymbolForNameWorker ( lookupContainer , node . escapedText ) ;
24642481 }
24652482 else {
24662483 const symbol = getJSInitializerSymbol ( lookupSymbolForPropertyAccess ( node . expression ) ) ;
0 commit comments