basic support for declaring properties on funcitons

This commit is contained in:
Mohamed Hegazy 2017-01-22 10:45:23 -08:00
parent f6ee80c675
commit b8329a05c3
5 changed files with 71 additions and 12 deletions

View file

@ -265,6 +265,7 @@ namespace ts {
return "export=";
case SpecialPropertyAssignmentKind.ExportsProperty:
case SpecialPropertyAssignmentKind.ThisProperty:
case SpecialPropertyAssignmentKind.Property:
// exports.x = ... or this.y = ...
return ((node as BinaryExpression).left as PropertyAccessExpression).name.text;
case SpecialPropertyAssignmentKind.PrototypeProperty:
@ -1921,6 +1922,9 @@ namespace ts {
case SpecialPropertyAssignmentKind.ThisProperty:
bindThisPropertyAssignment(<BinaryExpression>node);
break;
case SpecialPropertyAssignmentKind.Property:
bindPropertyAssignment(<BinaryExpression>node);
break;
case SpecialPropertyAssignmentKind.None:
// Nothing to do
break;
@ -2225,6 +2229,32 @@ namespace ts {
declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
}
function bindPropertyAssignment(node: BinaryExpression) {
// We saw a node of the form 'x.y = z'. Declare a 'member' y on x if x was a function.
// Look up the function in the local scope, since prototype assignments should
// follow the function declaration
const leftSideOfAssignment = node.left as PropertyAccessExpression;
const target = leftSideOfAssignment.expression as Identifier;
// Fix up parent pointers since we're going to use these nodes before we bind into them
leftSideOfAssignment.parent = node;
target.parent = leftSideOfAssignment;
const funcSymbol = container.locals[target.text];
if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
return;
}
// Set up the members collection if it doesn't exist already
if (!funcSymbol.exports) {
funcSymbol.exports = createMap<Symbol>();
}
// Declare the method/property
declareSymbol(funcSymbol.exports, funcSymbol, leftSideOfAssignment, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
}
function bindCallExpression(node: CallExpression) {
// We're only inspecting call expressions to detect CommonJS modules, so we can skip
// this check if we've already seen the module indicator

View file

@ -4580,7 +4580,7 @@ namespace ts {
// Combinations of function, class, enum and module
let members = emptySymbols;
let constructSignatures: Signature[] = emptyArray;
if (symbol.flags & SymbolFlags.HasExports) {
if (symbol.exports) {
members = getExportsOfSymbol(symbol);
}
if (symbol.flags & SymbolFlags.Class) {
@ -19871,22 +19871,29 @@ namespace ts {
return getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined;
}
function getSpecialPropertyAssignmentSymbolFromEntityName(entityName: EntityName | PropertyAccessExpression) {
const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent);
switch (specialPropertyAssignmentKind) {
case SpecialPropertyAssignmentKind.ExportsProperty:
case SpecialPropertyAssignmentKind.PrototypeProperty:
return getSymbolOfNode(entityName.parent);
case SpecialPropertyAssignmentKind.ThisProperty:
case SpecialPropertyAssignmentKind.ModuleExports:
case SpecialPropertyAssignmentKind.Property:
return getSymbolOfNode(entityName.parent.parent);
}
}
function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol | undefined {
if (isDeclarationName(entityName)) {
return getSymbolOfNode(entityName.parent);
}
if (isInJavaScriptFile(entityName) && entityName.parent.kind === SyntaxKind.PropertyAccessExpression) {
const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent);
switch (specialPropertyAssignmentKind) {
case SpecialPropertyAssignmentKind.ExportsProperty:
case SpecialPropertyAssignmentKind.PrototypeProperty:
return getSymbolOfNode(entityName.parent);
case SpecialPropertyAssignmentKind.ThisProperty:
case SpecialPropertyAssignmentKind.ModuleExports:
return getSymbolOfNode(entityName.parent.parent);
default:
// Fall through if it is not a special property assignment
// Check if this is a special property assignment
const specialPropertyAssignmentSymbol = getSpecialPropertyAssignmentSymbolFromEntityName(entityName);
if (specialPropertyAssignmentSymbol) {
return specialPropertyAssignmentSymbol;
}
}

View file

@ -3133,7 +3133,9 @@
/// className.prototype.name = expr
PrototypeProperty,
/// this.name = expr
ThisProperty
ThisProperty,
// F.name = expr
Property
}
export interface FileExtensionInfo {

View file

@ -1374,6 +1374,10 @@ namespace ts {
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 {
@ -1398,6 +1402,10 @@ namespace ts {
// module.exports = expr
return SpecialPropertyAssignmentKind.ModuleExports;
}
else {
// F.x = expr
return SpecialPropertyAssignmentKind.Property;
}
}
else if (lhs.expression.kind === SyntaxKind.ThisKeyword) {
return SpecialPropertyAssignmentKind.ThisProperty;
@ -1417,6 +1425,7 @@ namespace ts {
}
}
return SpecialPropertyAssignmentKind.None;
}

View file

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