Handel defining properties on function and class expressions in .js files

This commit is contained in:
Mohamed Hegazy 2017-01-22 10:45:23 -08:00
parent b8329a05c3
commit 39b3ecb78c
10 changed files with 94 additions and 31 deletions

View file

@ -2215,8 +2215,12 @@ namespace ts {
constructorFunction.parent = classPrototype;
classPrototype.parent = leftSideOfAssignment;
const funcSymbol = container.locals.get(constructorFunction.text);
if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
let funcSymbol = container.locals.get(constructorFunction.text);
if (isDeclarationOfFunctionOrClassExpression(funcSymbol)) {
funcSymbol = (funcSymbol.valueDeclaration as VariableDeclaration).initializer.symbol;
}
if (!funcSymbol || !(funcSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class))) {
return;
}
@ -2241,8 +2245,12 @@ namespace ts {
leftSideOfAssignment.parent = node;
target.parent = leftSideOfAssignment;
const funcSymbol = container.locals[target.text];
if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
let funcSymbol = container.locals.get(target.text);
if (isDeclarationOfFunctionOrClassExpression(funcSymbol)) {
funcSymbol = (funcSymbol.valueDeclaration as VariableDeclaration).initializer.symbol;
}
if (!funcSymbol || !(funcSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class))) {
return;
}

View file

@ -13886,10 +13886,13 @@ namespace ts {
// in a JS file
// Note:JS inferred classes might come from a variable declaration instead of a function declaration.
// In this case, using getResolvedSymbol directly is required to avoid losing the members from the declaration.
const funcSymbol = node.expression.kind === SyntaxKind.Identifier ?
let funcSymbol = node.expression.kind === SyntaxKind.Identifier ?
getResolvedSymbol(node.expression as Identifier) :
checkExpression(node.expression).symbol;
if (funcSymbol && funcSymbol.members && (funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
if (funcSymbol && isDeclarationOfFunctionOrClassExpression(funcSymbol)) {
funcSymbol = getSymbolOfNode((<VariableDeclaration>funcSymbol.valueDeclaration).initializer);
}
if (funcSymbol && funcSymbol.members && funcSymbol.flags & SymbolFlags.Function) {
return getInferredClassType(funcSymbol);
}
else if (compilerOptions.noImplicitAny) {

View file

@ -1366,18 +1366,14 @@ namespace ts {
* Returns true if the node is a variable declaration whose initializer is a function expression.
* This function does not test if the node is in a JavaScript file or not.
*/
export function isDeclarationOfFunctionExpression(s: Symbol) {
export function isDeclarationOfFunctionOrClassExpression(s: Symbol) {
if (s.valueDeclaration && s.valueDeclaration.kind === SyntaxKind.VariableDeclaration) {
const declaration = s.valueDeclaration as VariableDeclaration;
return declaration.initializer && declaration.initializer.kind === SyntaxKind.FunctionExpression;
return declaration.initializer && (declaration.initializer.kind === SyntaxKind.FunctionExpression || declaration.initializer.kind === SyntaxKind.ClassExpression);
}
return false;
}
export function isValidSpecialPropertyAssignmentParent(parentSymbol: Symbol) {
return parentSymbol && (parentSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(parentSymbol));
}
/// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property
/// assignments we treat as special in the binder
export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind {

View file

@ -30,7 +30,7 @@ class X {
>this : Symbol(X, Decl(input.js, 5, 1))
this.mistake = 'frankly, complete nonsense';
>this.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
>this.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
>this : Symbol(X, Decl(input.js, 5, 1))
>mistake : Symbol(X.mistake, Decl(input.js, 8, 35))
}
@ -38,7 +38,7 @@ class X {
>m : Symbol(X.m, Decl(input.js, 10, 5))
}
mistake() {
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
}
}
let x = new X();
@ -46,9 +46,11 @@ let x = new X();
>X : Symbol(X, Decl(input.js, 5, 1))
X.prototype.mistake = false;
>X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
>X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
>X.prototype : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
>X : Symbol(X, Decl(input.js, 5, 1))
>prototype : Symbol(X.prototype)
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
x.m();
>x.m : Symbol(X.m, Decl(input.js, 10, 5))
@ -56,15 +58,15 @@ x.m();
>m : Symbol(X.m, Decl(input.js, 10, 5))
x.mistake;
>x.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
>x.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
>x : Symbol(x, Decl(input.js, 16, 3))
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
class Y {
>Y : Symbol(Y, Decl(input.js, 19, 10))
mistake() {
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
}
m() {
>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
@ -80,15 +82,17 @@ class Y {
>this : Symbol(Y, Decl(input.js, 19, 10))
this.mistake = 'even more nonsense';
>this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
>this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
>this : Symbol(Y, Decl(input.js, 19, 10))
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
}
}
Y.prototype.mistake = true;
>Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
>Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
>Y.prototype : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
>Y : Symbol(Y, Decl(input.js, 19, 10))
>prototype : Symbol(Y.prototype)
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
let y = new Y();
>y : Symbol(y, Decl(input.js, 31, 3))
@ -100,7 +104,7 @@ y.m();
>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
y.mistake();
>y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
>y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
>y : Symbol(y, Decl(input.js, 31, 3))
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))

View file

@ -43,16 +43,16 @@ class X {
this.mistake = 'frankly, complete nonsense';
>this.mistake = 'frankly, complete nonsense' : "frankly, complete nonsense"
>this.mistake : () => void
>this.mistake : any
>this : this
>mistake : () => void
>mistake : any
>'frankly, complete nonsense' : "frankly, complete nonsense"
}
m() {
>m : () => void
}
mistake() {
>mistake : () => void
>mistake : any
}
}
let x = new X();
@ -62,11 +62,11 @@ let x = new X();
X.prototype.mistake = false;
>X.prototype.mistake = false : false
>X.prototype.mistake : () => void
>X.prototype.mistake : any
>X.prototype : X
>X : typeof X
>prototype : X
>mistake : () => void
>mistake : any
>false : false
x.m();
@ -76,9 +76,9 @@ x.m();
>m : () => void
x.mistake;
>x.mistake : () => void
>x.mistake : any
>x : X
>mistake : () => void
>mistake : any
class Y {
>Y : Y

View file

@ -18,4 +18,4 @@
//// Person.getNa/**/ = 10;
goTo.marker();
verify.not.completionListContains('getNa');
verify.completionListContains('getName');

View file

@ -0,0 +1,11 @@
/// <reference path='fourslash.ts'/>
// @allowJs: true
// @Filename: a.js
////class Minimatch {
////}
////Minimatch.[|staticProperty|] = "string";
////console.log(Minimatch./**/[|staticProperty|]);
goTo.marker();
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);

View file

@ -0,0 +1,11 @@
/// <reference path='fourslash.ts'/>
// @allowJs: true
// @Filename: a.js
////var C = class {
////}
////C.[|staticProperty|] = "string";
////console.log(C./**/[|staticProperty|]);
goTo.marker();
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);

View file

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts'/>
// @allowJs: true
// @Filename: a.js
////class C {
//// constructor(y) {
//// this.x = y;
//// }
////}
////C.prototype.[|z|] = 1;
////var t = new C(12);
////t./**/[|z|] = 11;
goTo.marker();
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);

View file

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts'/>
// @allowJs: true
// @Filename: a.js
////var C = class {
//// constructor(y) {
//// this.x = y;
//// }
////}
////C.prototype.[|z|] = 1;
////var t = new C(12);
////t./**/[|z|] = 11;
goTo.marker();
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);