diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index b1a9bc1dfdb..9927cb9d98e 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -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()' instead." }, Multiple_constructor_implementations_are_not_allowed: { code: 2070, category: DiagnosticCategory.Error, key: "Multiple constructor implementations are not allowed." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1f4f2102163..d61be84b5de 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -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 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b3d3f6c760b..3a40e487970 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -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 ((node).expression).text === "use strict"; } @@ -1416,7 +1417,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 @@ -1948,23 +1949,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 = {}; - 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 = (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 = {}; + 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); } @@ -2134,7 +2173,9 @@ module ts { function parseWithStatement(): WithStatement { var node = createNode(SyntaxKind.WithStatement); + var startPos = scanner.getTokenPos(); parseExpected(SyntaxKind.WithKeyword); + var endPos = scanner.getStartPos(); parseExpected(SyntaxKind.OpenParenToken); node.expression = parseExpression(); parseExpected(SyntaxKind.CloseParenToken); @@ -2142,8 +2183,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; } diff --git a/tests/baselines/reference/duplicateObjectLiteralProperty.errors.txt b/tests/baselines/reference/duplicateObjectLiteralProperty.errors.txt index ec1752971c9..51e9bae840b 100644 --- a/tests/baselines/reference/duplicateObjectLiteralProperty.errors.txt +++ b/tests/baselines/reference/duplicateObjectLiteralProperty.errors.txt @@ -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'. }; \ No newline at end of file diff --git a/tests/baselines/reference/duplicatePropertiesInStrictMode.errors.txt b/tests/baselines/reference/duplicatePropertiesInStrictMode.errors.txt index ba6ab28d777..e184b7761ea 100644 --- a/tests/baselines/reference/duplicatePropertiesInStrictMode.errors.txt +++ b/tests/baselines/reference/duplicatePropertiesInStrictMode.errors.txt @@ -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'. } \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralErrors.errors.txt b/tests/baselines/reference/objectLiteralErrors.errors.txt index a36e532b4be..8e1b64f1758 100644 --- a/tests/baselines/reference/objectLiteralErrors.errors.txt +++ b/tests/baselines/reference/objectLiteralErrors.errors.txt @@ -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 diff --git a/tests/baselines/reference/objectLiteralErrors.js b/tests/baselines/reference/objectLiteralErrors.js deleted file mode 100644 index 151cf10a6ab..00000000000 --- a/tests/baselines/reference/objectLiteralErrors.js +++ /dev/null @@ -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) { -} }; diff --git a/tests/baselines/reference/parserStrictMode14.errors.txt b/tests/baselines/reference/parserStrictMode14.errors.txt index 03cd5223229..a30a8863229 100644 --- a/tests/baselines/reference/parserStrictMode14.errors.txt +++ b/tests/baselines/reference/parserStrictMode14.errors.txt @@ -1,11 +1,10 @@ ==== tests/cases/conformance/parser/ecmascript5/StrictMode/parserStrictMode14.ts (2 errors) ==== "use strict"; with (a) { - ~~~~~~~~~~ + ~~~~ +!!! 'with' statements are not allowed in strict mode. ~ !!! All symbols within a 'with' block will be resolved to 'any'. ~ !!! Cannot find name 'a'. } - ~ -!!! 'with' statements are not allowed in strict mode. diff --git a/tests/baselines/reference/twoAccessorsWithSameName.errors.txt b/tests/baselines/reference/twoAccessorsWithSameName.errors.txt index e3631f617d9..edac011d718 100644 --- a/tests/baselines/reference/twoAccessorsWithSameName.errors.txt +++ b/tests/baselines/reference/twoAccessorsWithSameName.errors.txt @@ -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; }