Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing #442: Impossible to define static 'length' function on class #12065

Merged
merged 17 commits into from Jan 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 50 additions & 16 deletions src/compiler/checker.ts
Expand Up @@ -2160,24 +2160,25 @@ namespace ts {
return type.flags & TypeFlags.StringLiteral ? `"${escapeString((<LiteralType>type).text)}"` : (<LiteralType>type).text;
}

function getSymbolDisplayBuilder(): SymbolDisplayBuilder {

function getNameOfSymbol(symbol: Symbol): string {
if (symbol.declarations && symbol.declarations.length) {
const declaration = symbol.declarations[0];
if (declaration.name) {
return declarationNameToString(declaration.name);
}
switch (declaration.kind) {
case SyntaxKind.ClassExpression:
return "(Anonymous class)";
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return "(Anonymous function)";
}
function getNameOfSymbol(symbol: Symbol): string {
if (symbol.declarations && symbol.declarations.length) {
const declaration = symbol.declarations[0];
if (declaration.name) {
return declarationNameToString(declaration.name);
}
switch (declaration.kind) {
case SyntaxKind.ClassExpression:
return "(Anonymous class)";
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return "(Anonymous function)";
}
return symbol.name;
}
return symbol.name;
}

function getSymbolDisplayBuilder(): SymbolDisplayBuilder {

/**
* Writes only the name of the symbol out to the writer. Uses the original source text
Expand Down Expand Up @@ -15654,7 +15655,7 @@ namespace ts {
}
}
else {
const isStatic = forEach(member.modifiers, m => m.kind === SyntaxKind.StaticKeyword);
const isStatic = getModifierFlags(member) & ModifierFlags.Static;
const names = isStatic ? staticNames : instanceNames;

const memberName = member.name && getPropertyNameForPropertyNameNode(member.name);
Expand Down Expand Up @@ -15692,6 +15693,38 @@ namespace ts {
}
}

/**
* Static members being set on a constructor function may conflict with built-in properties
* of Function. Esp. in ECMAScript 5 there are non-configurable and non-writable
* built-in properties. This check issues a transpile error when a class has a static
* member with the same name as a non-writable built-in property.
*
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.3
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5
* @see http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-function-constructor
* @see http://www.ecma-international.org/ecma-262/6.0/#sec-function-instances
*/
function checkClassForStaticPropertyNameConflicts(node: ClassLikeDeclaration) {
for (const member of node.members) {
const memberNameNode = member.name;
const isStatic = getModifierFlags(member) & ModifierFlags.Static;
if (isStatic && memberNameNode) {
const memberName = getPropertyNameForPropertyNameNode(memberNameNode);
switch (memberName) {
case "name":
case "length":
case "caller":
case "arguments":
case "prototype":
const message = Diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1;
const className = getNameOfSymbol(getSymbolOfNode(node));
error(memberNameNode, message, memberName, className);
break;
}
}
}
}

function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) {
const names = createMap<boolean>();
for (const member of node.members) {
Expand Down Expand Up @@ -18267,6 +18300,7 @@ namespace ts {
const staticType = <ObjectType>getTypeOfSymbol(symbol);
checkTypeParameterListsIdentical(node, symbol);
checkClassForDuplicateDeclarations(node);
checkClassForStaticPropertyNameConflicts(node);

const baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -2015,6 +2015,10 @@
"category": "Error",
"code": 2698
},
"Static property '{0}' conflicts with built-in property 'Function.{0}' of constructor function '{1}'.": {
"category": "Error",
"code": 2699
},
"Rest types may only be created from object types.": {
"category": "Error",
"code": 2700
Expand Down
10 changes: 10 additions & 0 deletions tests/baselines/reference/propertyNamedPrototype.errors.txt
@@ -0,0 +1,10 @@
tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedPrototype.ts(3,12): error TS2699: Static property 'prototype' conflicts with built-in property 'Function.prototype' of constructor function 'C'.


==== tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedPrototype.ts (1 errors) ====
class C {
prototype: number; // ok
static prototype: C; // error
~~~~~~~~~
!!! error TS2699: Static property 'prototype' conflicts with built-in property 'Function.prototype' of constructor function 'C'.
}
@@ -1,43 +1,43 @@
tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts(12,1): error TS2322: Type 'C' is not assignable to type 'A'.
Property 'name' is missing in type 'C'.
Property 'prop' is missing in type 'C'.
tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts(13,1): error TS2322: Type 'typeof B' is not assignable to type 'A'.
Property 'name' is missing in type 'typeof B'.
Property 'prop' is missing in type 'typeof B'.
tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts(16,5): error TS2322: Type 'C' is not assignable to type 'B'.
Property 'name' is missing in type 'C'.
Property 'prop' is missing in type 'C'.
tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts(17,1): error TS2322: Type 'typeof B' is not assignable to type 'B'.
Property 'name' is missing in type 'typeof B'.
Property 'prop' is missing in type 'typeof B'.


==== tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts (4 errors) ====
interface A {
name();
prop();
}
class B {
public name() { }
public prop() { }
}
class C {
public static name() { }
public static prop() { }
}

var a: A = new B();
a = new C(); // error name is missing
a = new C(); // error prop is missing
~
!!! error TS2322: Type 'C' is not assignable to type 'A'.
!!! error TS2322: Property 'name' is missing in type 'C'.
a = B; // error name is missing
!!! error TS2322: Property 'prop' is missing in type 'C'.
a = B; // error prop is missing
~
!!! error TS2322: Type 'typeof B' is not assignable to type 'A'.
!!! error TS2322: Property 'name' is missing in type 'typeof B'.
!!! error TS2322: Property 'prop' is missing in type 'typeof B'.
a = C;

var b: B = new C(); // error name is missing
var b: B = new C(); // error prop is missing
~
!!! error TS2322: Type 'C' is not assignable to type 'B'.
!!! error TS2322: Property 'name' is missing in type 'C'.
b = B; // error name is missing
!!! error TS2322: Property 'prop' is missing in type 'C'.
b = B; // error prop is missing
~
!!! error TS2322: Type 'typeof B' is not assignable to type 'B'.
!!! error TS2322: Property 'name' is missing in type 'typeof B'.
!!! error TS2322: Property 'prop' is missing in type 'typeof B'.
b = C;
b = a;

Expand Down
@@ -1,21 +1,21 @@
//// [staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts]
interface A {
name();
prop();
}
class B {
public name() { }
public prop() { }
}
class C {
public static name() { }
public static prop() { }
}

var a: A = new B();
a = new C(); // error name is missing
a = B; // error name is missing
a = new C(); // error prop is missing
a = B; // error prop is missing
a = C;

var b: B = new C(); // error name is missing
b = B; // error name is missing
var b: B = new C(); // error prop is missing
b = B; // error prop is missing
b = C;
b = a;

Expand All @@ -29,21 +29,21 @@ c = a;
var B = (function () {
function B() {
}
B.prototype.name = function () { };
B.prototype.prop = function () { };
return B;
}());
var C = (function () {
function C() {
}
C.name = function () { };
C.prop = function () { };
return C;
}());
var a = new B();
a = new C(); // error name is missing
a = B; // error name is missing
a = new C(); // error prop is missing
a = B; // error prop is missing
a = C;
var b = new C(); // error name is missing
b = B; // error name is missing
var b = new C(); // error prop is missing
b = B; // error prop is missing
b = C;
b = a;
var c = new B();
Expand Down