Skip to content

Commit b8329a0

Browse files
committed
basic support for declaring properties on funcitons
1 parent f6ee80c commit b8329a0

File tree

5 files changed

+71
-12
lines changed

5 files changed

+71
-12
lines changed

src/compiler/binder.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ namespace ts {
265265
return "export=";
266266
case SpecialPropertyAssignmentKind.ExportsProperty:
267267
case SpecialPropertyAssignmentKind.ThisProperty:
268+
case SpecialPropertyAssignmentKind.Property:
268269
// exports.x = ... or this.y = ...
269270
return ((node as BinaryExpression).left as PropertyAccessExpression).name.text;
270271
case SpecialPropertyAssignmentKind.PrototypeProperty:
@@ -1921,6 +1922,9 @@ namespace ts {
19211922
case SpecialPropertyAssignmentKind.ThisProperty:
19221923
bindThisPropertyAssignment(<BinaryExpression>node);
19231924
break;
1925+
case SpecialPropertyAssignmentKind.Property:
1926+
bindPropertyAssignment(<BinaryExpression>node);
1927+
break;
19241928
case SpecialPropertyAssignmentKind.None:
19251929
// Nothing to do
19261930
break;
@@ -2225,6 +2229,32 @@ namespace ts {
22252229
declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
22262230
}
22272231

2232+
function bindPropertyAssignment(node: BinaryExpression) {
2233+
// We saw a node of the form 'x.y = z'. Declare a 'member' y on x if x was a function.
2234+
2235+
// Look up the function in the local scope, since prototype assignments should
2236+
// follow the function declaration
2237+
const leftSideOfAssignment = node.left as PropertyAccessExpression;
2238+
const target = leftSideOfAssignment.expression as Identifier;
2239+
2240+
// Fix up parent pointers since we're going to use these nodes before we bind into them
2241+
leftSideOfAssignment.parent = node;
2242+
target.parent = leftSideOfAssignment;
2243+
2244+
const funcSymbol = container.locals[target.text];
2245+
if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
2246+
return;
2247+
}
2248+
2249+
// Set up the members collection if it doesn't exist already
2250+
if (!funcSymbol.exports) {
2251+
funcSymbol.exports = createMap<Symbol>();
2252+
}
2253+
2254+
// Declare the method/property
2255+
declareSymbol(funcSymbol.exports, funcSymbol, leftSideOfAssignment, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
2256+
}
2257+
22282258
function bindCallExpression(node: CallExpression) {
22292259
// We're only inspecting call expressions to detect CommonJS modules, so we can skip
22302260
// this check if we've already seen the module indicator

src/compiler/checker.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4580,7 +4580,7 @@ namespace ts {
45804580
// Combinations of function, class, enum and module
45814581
let members = emptySymbols;
45824582
let constructSignatures: Signature[] = emptyArray;
4583-
if (symbol.flags & SymbolFlags.HasExports) {
4583+
if (symbol.exports) {
45844584
members = getExportsOfSymbol(symbol);
45854585
}
45864586
if (symbol.flags & SymbolFlags.Class) {
@@ -19871,22 +19871,29 @@ namespace ts {
1987119871
return getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined;
1987219872
}
1987319873

19874+
function getSpecialPropertyAssignmentSymbolFromEntityName(entityName: EntityName | PropertyAccessExpression) {
19875+
const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent);
19876+
switch (specialPropertyAssignmentKind) {
19877+
case SpecialPropertyAssignmentKind.ExportsProperty:
19878+
case SpecialPropertyAssignmentKind.PrototypeProperty:
19879+
return getSymbolOfNode(entityName.parent);
19880+
case SpecialPropertyAssignmentKind.ThisProperty:
19881+
case SpecialPropertyAssignmentKind.ModuleExports:
19882+
case SpecialPropertyAssignmentKind.Property:
19883+
return getSymbolOfNode(entityName.parent.parent);
19884+
}
19885+
}
19886+
1987419887
function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol | undefined {
1987519888
if (isDeclarationName(entityName)) {
1987619889
return getSymbolOfNode(entityName.parent);
1987719890
}
1987819891

1987919892
if (isInJavaScriptFile(entityName) && entityName.parent.kind === SyntaxKind.PropertyAccessExpression) {
19880-
const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent);
19881-
switch (specialPropertyAssignmentKind) {
19882-
case SpecialPropertyAssignmentKind.ExportsProperty:
19883-
case SpecialPropertyAssignmentKind.PrototypeProperty:
19884-
return getSymbolOfNode(entityName.parent);
19885-
case SpecialPropertyAssignmentKind.ThisProperty:
19886-
case SpecialPropertyAssignmentKind.ModuleExports:
19887-
return getSymbolOfNode(entityName.parent.parent);
19888-
default:
19889-
// Fall through if it is not a special property assignment
19893+
// Check if this is a special property assignment
19894+
const specialPropertyAssignmentSymbol = getSpecialPropertyAssignmentSymbolFromEntityName(entityName);
19895+
if (specialPropertyAssignmentSymbol) {
19896+
return specialPropertyAssignmentSymbol;
1989019897
}
1989119898
}
1989219899

src/compiler/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3133,7 +3133,9 @@
31333133
/// className.prototype.name = expr
31343134
PrototypeProperty,
31353135
/// this.name = expr
3136-
ThisProperty
3136+
ThisProperty,
3137+
// F.name = expr
3138+
Property
31373139
}
31383140

31393141
export interface FileExtensionInfo {

src/compiler/utilities.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,10 @@ namespace ts {
13741374
return false;
13751375
}
13761376

1377+
export function isValidSpecialPropertyAssignmentParent(parentSymbol: Symbol) {
1378+
return parentSymbol && (parentSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(parentSymbol));
1379+
}
1380+
13771381
/// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property
13781382
/// assignments we treat as special in the binder
13791383
export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind {
@@ -1398,6 +1402,10 @@ namespace ts {
13981402
// module.exports = expr
13991403
return SpecialPropertyAssignmentKind.ModuleExports;
14001404
}
1405+
else {
1406+
// F.x = expr
1407+
return SpecialPropertyAssignmentKind.Property;
1408+
}
14011409
}
14021410
else if (lhs.expression.kind === SyntaxKind.ThisKeyword) {
14031411
return SpecialPropertyAssignmentKind.ThisProperty;
@@ -1417,6 +1425,7 @@ namespace ts {
14171425
}
14181426
}
14191427

1428+
14201429
return SpecialPropertyAssignmentKind.None;
14211430
}
14221431

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @allowJs: true
4+
// @Filename: a.js
5+
////function bar() {
6+
////}
7+
////bar.[|foo|] = "foo";
8+
////console.log(bar./**/[|foo|]);
9+
10+
goTo.marker();
11+
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);

0 commit comments

Comments
 (0)