Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,10 @@ namespace ts {
const name = isDefaultExport && parent ? "default" : getDeclarationName(node);

let symbol: Symbol;
if (name !== undefined) {

if (name === undefined) {
symbol = createSymbol(SymbolFlags.None, "__missing");
}
else {
// Check and see if the symbol table already has a symbol with this name. If not,
// create a new symbol with this name and add it to the table. Note that we don't
// give the new symbol any flags *yet*. This ensures that it will not conflict
Expand Down Expand Up @@ -327,33 +329,37 @@ namespace ts {
}

if (symbol.flags & excludes) {
if (node.name) {
node.name.parent = node;
if (symbol.isDiscardable) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you may want to mention this change in the comment above

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// Javascript constructor-declared symbols can be discarded in favor of
// prototype symbols like methods.
symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name);
}
else {
if (node.name) {
node.name.parent = node;
}

// Report errors every position with duplicate declaration
// Report errors on previous encountered declarations
let message = symbol.flags & SymbolFlags.BlockScopedVariable
? Diagnostics.Cannot_redeclare_block_scoped_variable_0
: Diagnostics.Duplicate_identifier_0;
// Report errors every position with duplicate declaration
// Report errors on previous encountered declarations
let message = symbol.flags & SymbolFlags.BlockScopedVariable
? Diagnostics.Cannot_redeclare_block_scoped_variable_0
: Diagnostics.Duplicate_identifier_0;

forEach(symbol.declarations, declaration => {
if (declaration.flags & NodeFlags.Default) {
message = Diagnostics.A_module_cannot_have_multiple_default_exports;
}
});
forEach(symbol.declarations, declaration => {
if (declaration.flags & NodeFlags.Default) {
message = Diagnostics.A_module_cannot_have_multiple_default_exports;
}
});

forEach(symbol.declarations, declaration => {
file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration)));
});
file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node)));
forEach(symbol.declarations, declaration => {
file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration)));
});
file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node)));

symbol = createSymbol(SymbolFlags.None, name);
symbol = createSymbol(SymbolFlags.None, name);
}
}
}
else {
symbol = createSymbol(SymbolFlags.None, "__missing");
}

addDeclarationToSymbol(symbol, node, includes);
symbol.parent = parent;
Expand Down Expand Up @@ -1967,7 +1973,7 @@ namespace ts {
}

function bindThisPropertyAssignment(node: BinaryExpression) {
// Declare a 'member' in case it turns out the container was an ES5 class or ES6 constructor
// Declare a 'member' if the container is an ES5 class or ES6 constructor
let assignee: Node;
if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionExpression) {
assignee = container;
Expand All @@ -1980,7 +1986,9 @@ namespace ts {
}
assignee.symbol.members = assignee.symbol.members || {};
// It's acceptable for multiple 'this' assignments of the same identifier to occur
declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
// AND it can be overwritten by subsequent method declarations
const symbol = declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
symbol.isDiscardable = true;
}

function bindPrototypePropertyAssignment(node: BinaryExpression) {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2137,6 +2137,7 @@ namespace ts {
/* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
/* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums
/* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere
/* @internal */ isDiscardable?: boolean; // True if a Javascript class property can be overwritten by a method
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could you name the property ? (e.g isMethodOverwritable etc.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. I ended up with isReplaceableByMethod but tell if you want me to change it.

}

/* @internal */
Expand Down
61 changes: 59 additions & 2 deletions tests/baselines/reference/multipleDeclarations.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
//// [input.js]

function C() {
this.m = null;
}
C.prototype.m = function() {
this.nothing();
};
}
class X {
constructor() {
this.m = this.m.bind(this);
this.mistake = 'frankly, complete nonsense';
}
m() {
}
mistake() {
}
}
let x = new X();
X.prototype.mistake = false;
x.m();
x.mistake;
class Y {
mistake() {
}
m() {
}
constructor() {
this.m = this.m.bind(this);
this.mistake = 'even more nonsense';
}
}
Y.prototype.mistake = true;
let y = new Y();
y.m();
y.mistake();


//// [output.js]
Expand All @@ -15,3 +42,33 @@ function C() {
C.prototype.m = function () {
this.nothing();
};
var X = (function () {
function X() {
this.m = this.m.bind(this);
this.mistake = 'frankly, complete nonsense';
}
X.prototype.m = function () {
};
X.prototype.mistake = function () {
};
return X;
}());
var x = new X();
X.prototype.mistake = false;
x.m();
x.mistake;
var Y = (function () {
function Y() {
this.m = this.m.bind(this);
this.mistake = 'even more nonsense';
}
Y.prototype.mistake = function () {
};
Y.prototype.m = function () {
};
return Y;
}());
Y.prototype.mistake = true;
var y = new Y();
y.m();
y.mistake();
97 changes: 92 additions & 5 deletions tests/baselines/reference/multipleDeclarations.symbols
Original file line number Diff line number Diff line change
@@ -1,19 +1,106 @@
=== tests/cases/conformance/salsa/input.js ===

function C() {
>C : Symbol(C, Decl(input.js, 0, 0))

this.m = null;
>m : Symbol(C.m, Decl(input.js, 1, 14), Decl(input.js, 3, 1))
>m : Symbol(C.m, Decl(input.js, 0, 14), Decl(input.js, 2, 1))
}
C.prototype.m = function() {
>C.prototype : Symbol(C.m, Decl(input.js, 1, 14), Decl(input.js, 3, 1))
>C.prototype : Symbol(C.m, Decl(input.js, 0, 14), Decl(input.js, 2, 1))
>C : Symbol(C, Decl(input.js, 0, 0))
>prototype : Symbol(Function.prototype, Decl(lib.d.ts, --, --))
>m : Symbol(C.m, Decl(input.js, 1, 14), Decl(input.js, 3, 1))
>m : Symbol(C.m, Decl(input.js, 0, 14), Decl(input.js, 2, 1))

this.nothing();
>this : Symbol(C, Decl(input.js, 0, 0))
}
class X {
>X : Symbol(X, Decl(input.js, 5, 1))

constructor() {
this.m = this.m.bind(this);
>this.m : Symbol(X.m, Decl(input.js, 10, 5))
>this : Symbol(X, Decl(input.js, 5, 1))
>m : Symbol(X.m, Decl(input.js, 7, 19))
>this.m.bind : Symbol(Function.bind, Decl(lib.d.ts, --, --))
>this.m : Symbol(X.m, Decl(input.js, 10, 5))
>this : Symbol(X, Decl(input.js, 5, 1))
>m : Symbol(X.m, Decl(input.js, 10, 5))
>bind : Symbol(Function.bind, Decl(lib.d.ts, --, --))
>this : Symbol(X, Decl(input.js, 5, 1))

this.mistake = 'frankly, complete nonsense';
>this.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
>this : Symbol(X, Decl(input.js, 5, 1))
>mistake : Symbol(X.mistake, Decl(input.js, 8, 35))
}
m() {
>m : Symbol(X.m, Decl(input.js, 10, 5))
}
mistake() {
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
}
}
let x = new X();
>x : Symbol(x, Decl(input.js, 16, 3))
>X : Symbol(X, Decl(input.js, 5, 1))

X.prototype.mistake = false;
>X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
>X : Symbol(X, Decl(input.js, 5, 1))
>prototype : Symbol(X.prototype)

x.m();
>x.m : Symbol(X.m, Decl(input.js, 10, 5))
>x : Symbol(x, Decl(input.js, 16, 3))
>m : Symbol(X.m, Decl(input.js, 10, 5))

x.mistake;
>x.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
>x : Symbol(x, Decl(input.js, 16, 3))
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5))

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))
}
m() {
>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
}
constructor() {
this.m = this.m.bind(this);
>this.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
>this : Symbol(Y, Decl(input.js, 19, 10))
>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
>this.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
>this : Symbol(Y, Decl(input.js, 19, 10))
>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
>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 : Symbol(Y, Decl(input.js, 19, 10))
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
}
}
Y.prototype.mistake = true;
>Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
>Y : Symbol(Y, Decl(input.js, 19, 10))
>prototype : Symbol(Y.prototype)

let y = new Y();
>y : Symbol(y, Decl(input.js, 31, 3))
>Y : Symbol(Y, Decl(input.js, 19, 10))

y.m();
>y.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
>y : Symbol(y, Decl(input.js, 31, 3))
>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 : Symbol(y, Decl(input.js, 31, 3))
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))

Loading