update check for object literal properties according to ECMA spec

This commit is contained in:
Vladimir Matveev 2014-07-23 16:45:23 -07:00
parent 259f8a2091
commit ddfd0fe074
9 changed files with 118 additions and 163 deletions

View file

@ -99,7 +99,9 @@ module ts {
A_constructor_implementation_cannot_be_declared_in_an_ambient_context: { code: 1111, category: DiagnosticCategory.Error, key: "A constructor implementation cannot be declared in an ambient context." },
A_class_member_cannot_be_declared_optional: { code: 1112, category: DiagnosticCategory.Error, key: "A class member cannot be declared optional." },
A_default_clause_cannot_appear_more_than_once_in_a_switch_statement: { code: 1113, category: DiagnosticCategory.Error, key: "A 'default' clause cannot appear more than once in a 'switch' statement." },
Object_literal_cannot_contain_more_than_one_property_with_the_same_name_in_the_strict_mode: { code: 1114, category: DiagnosticCategory.Error, key: "Object literal cannot contain more than one property with the same name in the strict mode." },
An_object_literal_cannot_have_multiple_properties_with_the_same_name_in_strict_mode: { code: 1114, category: DiagnosticCategory.Error, key: "An object literal cannot have multiple properties with the same name in strict mode." },
An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name: { code: 1115, category: DiagnosticCategory.Error, key: "An object literal cannot have multiple get/set accessors with the same name." },
An_object_literal_cannot_have_property_and_accessor_with_the_same_name: { code: 1116, category: DiagnosticCategory.Error, key: "An object literal cannot have property and accessor with the same name." },
Duplicate_identifier_0: { code: 2000, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead: { code: 2068, category: DiagnosticCategory.Error, key: "'new T[]' cannot be used to create an array. Use 'new Array<T>()' instead." },
Multiple_constructor_implementations_are_not_allowed: { code: 2070, category: DiagnosticCategory.Error, key: "Multiple constructor implementations are not allowed." },

View file

@ -388,10 +388,18 @@
"category": "Error",
"code": 1113
},
"Object literal cannot contain more than one property with the same name in the strict mode.": {
"An object literal cannot have multiple properties with the same name in strict mode.": {
"category": "Error",
"code": 1114
},
"An object literal cannot have multiple get/set accessors with the same name.": {
"category": "Error",
"code": 1115
},
"An object literal cannot have property and accessor with the same name.": {
"category": "Error",
"code": 1116
},
"Duplicate identifier '{0}'.": {
"category": "Error",
"code": 2000

View file

@ -131,6 +131,7 @@ module ts {
/// Should be called only on prologue directives (isPrologueDirective(node) should be true)
function isUseStrictPrologueDirective(node: Node): boolean {
Debug.assert(isPrologueDirective(node));
return (<Identifier>(<ExpressionStatement>node).expression).text === "use strict";
}
@ -1415,7 +1416,7 @@ module ts {
// Now see if we might be in cases '2' or '3'.
// If the expression was a LHS expression, and we have an assignment operator, then
// we're in '2' or '3'. Consume the assignement and return.
// we're in '2' or '3'. Consume the assignment and return.
if (isLeftHandSideExpression(expr) && isAssignmentOperator()) {
if (isInStrictMode && isEvalOrArgumentsIdentifier(expr)) {
// ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an
@ -1947,23 +1948,61 @@ module ts {
if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine;
node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, TrailingCommaBehavior.Preserve);
parseExpected(SyntaxKind.CloseBraceToken);
if (isInStrictMode) {
var seen: Map<boolean> = {};
forEach(node.properties, (p: Node) => {
// ECMA-262 11.1.5 Object Initialiser
// If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
// a.This production is contained in strict code and IsDataDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true.
if (p.kind === SyntaxKind.PropertyAssignment) {
var name = (<PropertyDeclaration>p).name;
if (hasProperty(seen, name.text)) {
grammarErrorOnNode(name, Diagnostics.Object_literal_cannot_contain_more_than_one_property_with_the_same_name_in_the_strict_mode);
}
else {
seen[name.text] = true;
var seen: Map<SymbolFlags> = {};
var Property = 1;
var GetAccessor = 2;
var SetAccesor = 4;
var GetOrSetAccessor = GetAccessor | SetAccesor;
forEach(node.properties, (p: Declaration) => {
if (p.kind === SyntaxKind.OmittedExpression) {
return;
}
// ECMA-262 11.1.5 Object Initialiser
// If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
// a.This production is contained in strict code and IsDataDescriptor(previous) is true and
// IsDataDescriptor(propId.descriptor) is true.
// b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true.
// c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true.
// d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true
// and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields
var currentKind: number;
if (p.kind === SyntaxKind.PropertyAssignment) {
currentKind = Property;
}
else if (p.kind === SyntaxKind.GetAccessor) {
currentKind = GetAccessor;
}
else if (p.kind === SyntaxKind.SetAccessor) {
currentKind = SetAccesor;
}
else {
Debug.fail("Unexpected syntax kind:" + SyntaxKind[p.kind]);
}
if (!hasProperty(seen, p.name.text)) {
seen[p.name.text] = currentKind;
}
else {
var existingKind = seen[p.name.text];
if (currentKind === Property && existingKind === Property) {
if (isInStrictMode) {
grammarErrorOnNode(p.name, Diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name_in_strict_mode);
}
}
});
}
else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) {
if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) {
seen[p.name.text] = currentKind | existingKind;
}
else {
grammarErrorOnNode(p.name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name);
}
}
else {
grammarErrorOnNode(p.name, Diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name);
}
}
});
return finishNode(node);
}
@ -2133,7 +2172,9 @@ module ts {
function parseWithStatement(): WithStatement {
var node = <WithStatement>createNode(SyntaxKind.WithStatement);
var startPos = scanner.getTokenPos();
parseExpected(SyntaxKind.WithKeyword);
var endPos = scanner.getStartPos();
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
@ -2141,8 +2182,8 @@ module ts {
node = finishNode(node);
if (isInStrictMode) {
// Strict mode code may not include a WithStatement. The occurrence of a WithStatement in such
// a context is an SyntaxError(12.10)
grammarErrorOnNode(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode);
// a context is an
grammarErrorAtPos(startPos, endPos - startPos, Diagnostics.with_statements_are_not_allowed_in_strict_mode)
}
return node;
}

View file

@ -1,4 +1,4 @@
==== tests/cases/compiler/duplicateObjectLiteralProperty.ts (8 errors) ====
==== tests/cases/compiler/duplicateObjectLiteralProperty.ts (9 errors) ====
var x = {
a: 1,
b: true, // OK
@ -30,6 +30,8 @@
~
!!! Accessors are only available when targeting ECMAScript 5 and higher.
~
!!! An object literal cannot have multiple get/set accessors with the same name.
~
!!! Duplicate identifier 'a'.
};

View file

@ -4,7 +4,7 @@
x: 1,
x: 2
~
!!! Object literal cannot contain more than one property with the same name in the strict mode.
!!! An object literal cannot have multiple properties with the same name in strict mode.
~
!!! Duplicate identifier 'x'.
}

View file

@ -1,4 +1,4 @@
==== tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts (41 errors) ====
==== tests/cases/conformance/expressions/objectLiterals/objectLiteralErrors.ts (59 errors) ====
// Multiple properties with the same name
var e1 = { a: 0, a: 0 };
@ -59,57 +59,93 @@
// Accessor and property with the same name
var f1 = { a: 0, get a() { return 0; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier 'a'.
var f2 = { a: '', get a() { return ''; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier 'a'.
var f3 = { a: 0, get a() { return ''; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier 'a'.
var f4 = { a: true, get a() { return false; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier 'a'.
var f5 = { a: {}, get a() { return {}; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier 'a'.
var f6 = { a: 0, get 'a'() { return 0; } };
~~~
!!! An object literal cannot have property and accessor with the same name.
~~~
!!! Duplicate identifier ''a''.
var f7 = { 'a': 0, get a() { return 0; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier 'a'.
var f8 = { 'a': 0, get "a"() { return 0; } };
~~~
!!! An object literal cannot have property and accessor with the same name.
~~~
!!! Duplicate identifier '"a"'.
var f9 = { 'a': 0, get 'a'() { return 0; } };
~~~
!!! An object literal cannot have property and accessor with the same name.
~~~
!!! Duplicate identifier ''a''.
var f10 = { "a": 0, get 'a'() { return 0; } };
~~~
!!! An object literal cannot have property and accessor with the same name.
~~~
!!! Duplicate identifier ''a''.
var f11 = { 1.0: 0, get '1'() { return 0; } };
~~~
!!! An object literal cannot have property and accessor with the same name.
~~~
!!! Duplicate identifier ''1''.
var f12 = { 0: 0, get 0() { return 0; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier '0'.
var f13 = { 0: 0, get 0() { return 0; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier '0'.
var f14 = { 0: 0, get 0x0() { return 0; } };
~~~
!!! An object literal cannot have property and accessor with the same name.
~~~
!!! Duplicate identifier '0x0'.
var f14 = { 0: 0, get 000() { return 0; } };
~~~
!!! An object literal cannot have property and accessor with the same name.
~~~
!!! Duplicate identifier '000'.
var f15 = { "100": 0, get 1e2() { return 0; } };
~~~
!!! An object literal cannot have property and accessor with the same name.
~~~
!!! Duplicate identifier '1e2'.
var f16 = { 0x20: 0, get 3.2e1() { return 0; } };
~~~~~
!!! An object literal cannot have property and accessor with the same name.
~~~~~
!!! Duplicate identifier '3.2e1'.
var f17 = { a: 0, get b() { return 1; }, get a() { return 0; } };
~
!!! An object literal cannot have property and accessor with the same name.
~
!!! Duplicate identifier 'a'.
// Get and set accessor with mismatched type annotations

View file

@ -1,135 +0,0 @@
//// [objectLiteralErrors.ts]
// Multiple properties with the same name
var e1 = { a: 0, a: 0 };
var e2 = { a: '', a: '' };
var e3 = { a: 0, a: '' };
var e4 = { a: true, a: false };
var e5 = { a: {}, a: {} };
var e6 = { a: 0, 'a': 0 };
var e7 = { 'a': 0, a: 0 };
var e8 = { 'a': 0, "a": 0 };
var e9 = { 'a': 0, 'a': 0 };
var e10 = { "a": 0, 'a': 0 };
var e11 = { 1.0: 0, '1': 0 };
var e12 = { 0: 0, 0: 0 };
var e13 = { 0: 0, 0: 0 };
var e14 = { 0: 0, 0x0: 0 };
var e14 = { 0: 0, 000: 0 };
var e15 = { "100": 0, 1e2: 0 };
var e16 = { 0x20: 0, 3.2e1: 0 };
var e17 = { a: 0, b: 1, a: 0 };
// Accessor and property with the same name
var f1 = { a: 0, get a() { return 0; } };
var f2 = { a: '', get a() { return ''; } };
var f3 = { a: 0, get a() { return ''; } };
var f4 = { a: true, get a() { return false; } };
var f5 = { a: {}, get a() { return {}; } };
var f6 = { a: 0, get 'a'() { return 0; } };
var f7 = { 'a': 0, get a() { return 0; } };
var f8 = { 'a': 0, get "a"() { return 0; } };
var f9 = { 'a': 0, get 'a'() { return 0; } };
var f10 = { "a": 0, get 'a'() { return 0; } };
var f11 = { 1.0: 0, get '1'() { return 0; } };
var f12 = { 0: 0, get 0() { return 0; } };
var f13 = { 0: 0, get 0() { return 0; } };
var f14 = { 0: 0, get 0x0() { return 0; } };
var f14 = { 0: 0, get 000() { return 0; } };
var f15 = { "100": 0, get 1e2() { return 0; } };
var f16 = { 0x20: 0, get 3.2e1() { return 0; } };
var f17 = { a: 0, get b() { return 1; }, get a() { return 0; } };
// Get and set accessor with mismatched type annotations
var g1 = { get a(): number { return 4; }, set a(n: string) { } };
var g2 = { get a() { return 4; }, set a(n: string) { } };
var g3 = { get a(): number { return undefined; }, set a(n: string) { } };
//// [objectLiteralErrors.js]
var e1 = { a: 0, a: 0 };
var e2 = { a: '', a: '' };
var e3 = { a: 0, a: '' };
var e4 = { a: true, a: false };
var e5 = { a: {}, a: {} };
var e6 = { a: 0, 'a': 0 };
var e7 = { 'a': 0, a: 0 };
var e8 = { 'a': 0, "a": 0 };
var e9 = { 'a': 0, 'a': 0 };
var e10 = { "a": 0, 'a': 0 };
var e11 = { 1.0: 0, '1': 0 };
var e12 = { 0: 0, 0: 0 };
var e13 = { 0: 0, 0: 0 };
var e14 = { 0: 0, 0x0: 0 };
var e14 = { 0: 0, 000: 0 };
var e15 = { "100": 0, 1e2: 0 };
var e16 = { 0x20: 0, 3.2e1: 0 };
var e17 = { a: 0, b: 1, a: 0 };
var f1 = { a: 0, get a() {
return 0;
} };
var f2 = { a: '', get a() {
return '';
} };
var f3 = { a: 0, get a() {
return '';
} };
var f4 = { a: true, get a() {
return false;
} };
var f5 = { a: {}, get a() {
return {};
} };
var f6 = { a: 0, get 'a'() {
return 0;
} };
var f7 = { 'a': 0, get a() {
return 0;
} };
var f8 = { 'a': 0, get "a"() {
return 0;
} };
var f9 = { 'a': 0, get 'a'() {
return 0;
} };
var f10 = { "a": 0, get 'a'() {
return 0;
} };
var f11 = { 1.0: 0, get '1'() {
return 0;
} };
var f12 = { 0: 0, get 0() {
return 0;
} };
var f13 = { 0: 0, get 0() {
return 0;
} };
var f14 = { 0: 0, get 0x0() {
return 0;
} };
var f14 = { 0: 0, get 000() {
return 0;
} };
var f15 = { "100": 0, get 1e2() {
return 0;
} };
var f16 = { 0x20: 0, get 3.2e1() {
return 0;
} };
var f17 = { a: 0, get b() {
return 1;
}, get a() {
return 0;
} };
var g1 = { get a() {
return 4;
}, set a(n) {
} };
var g2 = { get a() {
return 4;
}, set a(n) {
} };
var g3 = { get a() {
return undefined;
}, set a(n) {
} };

View file

@ -1,9 +1,8 @@
==== tests/cases/conformance/parser/ecmascript5/StrictMode/parserStrictMode14.ts (2 errors) ====
"use strict";
with (a) {
~~~~~~~~~~
~~~~
!!! 'with' statements are not allowed in strict mode.
~
!!! Cannot find name 'a'.
}
~
!!! 'with' statements are not allowed in strict mode.
}

View file

@ -1,4 +1,4 @@
==== tests/cases/conformance/classes/propertyMemberDeclarations/twoAccessorsWithSameName.ts (13 errors) ====
==== tests/cases/conformance/classes/propertyMemberDeclarations/twoAccessorsWithSameName.ts (14 errors) ====
class C {
get x() { return 1; }
~
@ -44,6 +44,8 @@
~
!!! Accessors are only available when targeting ECMAScript 5 and higher.
~
!!! An object literal cannot have multiple get/set accessors with the same name.
~
!!! Duplicate identifier 'x'.
return 1;
}