diff --git a/tsc/internal/parser/parser.go b/tsc/internal/parser/parser.go index db1bb03defb8c..05dc9a7584a0c 100644 --- a/tsc/internal/parser/parser.go +++ b/tsc/internal/parser/parser.go @@ -5781,7 +5781,12 @@ func (p *Parser) parseArrayLiteralExpression() *ast.Expression { openBracketPosition := p.scanner.TokenStart() openBracketParsed := p.parseExpected(ast.KindOpenBracketToken) multiLine := p.hasPrecedingLineBreak() - elements := p.parseDelimitedList(PCArrayLiteralMembers, (*Parser).parseArgumentOrArrayLiteralElement) + // The [~In] of a `for` statement head does not reach into array literals: + // ElementList elements are AssignmentExpression[+In]. Parse them with `in` allowed, + // as is already done for object literal property values and call arguments. + elements := p.doInContext(ast.NodeFlagsDisallowInContext, false, func(p *Parser) *ast.NodeList { + return p.parseDelimitedList(PCArrayLiteralMembers, (*Parser).parseArgumentOrArrayLiteralElement) + }) p.parseExpectedMatchingBrackets(ast.KindOpenBracketToken, ast.KindCloseBracketToken, openBracketParsed, openBracketPosition) return p.finishNode(p.factory.NewArrayLiteralExpression(elements, multiLine), pos) } diff --git a/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.js b/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.js new file mode 100644 index 0000000000000..b277eb015983e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.js @@ -0,0 +1,52 @@ +//// [tests/cases/compiler/arrayLiteralInOperatorInForInitializer.ts] //// + +//// [arrayLiteralInOperatorInForInitializer.ts] +// The `in` operator is allowed in an array literal that appears in the head of +// a `for` statement: the for head's [~In] parameter does not reach into +// array literals (ElementList : AssignmentExpression[+In]). + +const o = { a: 1 }; +const k = "a"; + +for (let i = 0, has = [k in o]; i < 1; i++) { + console.log(has); +} + +for (let i = 0, has = [k in o, "b" in o, !(k in o)]; i < 1; i++) { + console.log(has); +} + +let x: any; +for ([x = "x" in {}] of [[]]) { + console.log(x); +} + +// The initializer of a `for` statement still ends at `in`, so that `for-in` +// statements keep parsing as before. +for (const key in o) { + console.log(key); +} + + +//// [arrayLiteralInOperatorInForInitializer.js] +"use strict"; +// The `in` operator is allowed in an array literal that appears in the head of +// a `for` statement: the for head's [~In] parameter does not reach into +// array literals (ElementList : AssignmentExpression[+In]). +const o = { a: 1 }; +const k = "a"; +for (let i = 0, has = [k in o]; i < 1; i++) { + console.log(has); +} +for (let i = 0, has = [k in o, "b" in o, !(k in o)]; i < 1; i++) { + console.log(has); +} +let x; +for ([x = "x" in {}] of [[]]) { + console.log(x); +} +// The initializer of a `for` statement still ends at `in`, so that `for-in` +// statements keep parsing as before. +for (const key in o) { + console.log(key); +} diff --git a/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.symbols b/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.symbols new file mode 100644 index 0000000000000..b6109b0b9df67 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.symbols @@ -0,0 +1,73 @@ +//// [tests/cases/compiler/arrayLiteralInOperatorInForInitializer.ts] //// + +=== arrayLiteralInOperatorInForInitializer.ts === +// The `in` operator is allowed in an array literal that appears in the head of +// a `for` statement: the for head's [~In] parameter does not reach into +// array literals (ElementList : AssignmentExpression[+In]). + +const o = { a: 1 }; +>o : Symbol(o, Decl(arrayLiteralInOperatorInForInitializer.ts, 4, 5)) +>a : Symbol(a, Decl(arrayLiteralInOperatorInForInitializer.ts, 4, 11)) + +const k = "a"; +>k : Symbol(k, Decl(arrayLiteralInOperatorInForInitializer.ts, 5, 5)) + +for (let i = 0, has = [k in o]; i < 1; i++) { +>i : Symbol(i, Decl(arrayLiteralInOperatorInForInitializer.ts, 7, 8)) +>has : Symbol(has, Decl(arrayLiteralInOperatorInForInitializer.ts, 7, 15)) +>k : Symbol(k, Decl(arrayLiteralInOperatorInForInitializer.ts, 5, 5)) +>o : Symbol(o, Decl(arrayLiteralInOperatorInForInitializer.ts, 4, 5)) +>i : Symbol(i, Decl(arrayLiteralInOperatorInForInitializer.ts, 7, 8)) +>i : Symbol(i, Decl(arrayLiteralInOperatorInForInitializer.ts, 7, 8)) + + console.log(has); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>has : Symbol(has, Decl(arrayLiteralInOperatorInForInitializer.ts, 7, 15)) +} + +for (let i = 0, has = [k in o, "b" in o, !(k in o)]; i < 1; i++) { +>i : Symbol(i, Decl(arrayLiteralInOperatorInForInitializer.ts, 11, 8)) +>has : Symbol(has, Decl(arrayLiteralInOperatorInForInitializer.ts, 11, 15)) +>k : Symbol(k, Decl(arrayLiteralInOperatorInForInitializer.ts, 5, 5)) +>o : Symbol(o, Decl(arrayLiteralInOperatorInForInitializer.ts, 4, 5)) +>o : Symbol(o, Decl(arrayLiteralInOperatorInForInitializer.ts, 4, 5)) +>k : Symbol(k, Decl(arrayLiteralInOperatorInForInitializer.ts, 5, 5)) +>o : Symbol(o, Decl(arrayLiteralInOperatorInForInitializer.ts, 4, 5)) +>i : Symbol(i, Decl(arrayLiteralInOperatorInForInitializer.ts, 11, 8)) +>i : Symbol(i, Decl(arrayLiteralInOperatorInForInitializer.ts, 11, 8)) + + console.log(has); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>has : Symbol(has, Decl(arrayLiteralInOperatorInForInitializer.ts, 11, 15)) +} + +let x: any; +>x : Symbol(x, Decl(arrayLiteralInOperatorInForInitializer.ts, 15, 3)) + +for ([x = "x" in {}] of [[]]) { +>x : Symbol(x, Decl(arrayLiteralInOperatorInForInitializer.ts, 15, 3)) + + console.log(x); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>x : Symbol(x, Decl(arrayLiteralInOperatorInForInitializer.ts, 15, 3)) +} + +// The initializer of a `for` statement still ends at `in`, so that `for-in` +// statements keep parsing as before. +for (const key in o) { +>key : Symbol(key, Decl(arrayLiteralInOperatorInForInitializer.ts, 22, 10)) +>o : Symbol(o, Decl(arrayLiteralInOperatorInForInitializer.ts, 4, 5)) + + console.log(key); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>key : Symbol(key, Decl(arrayLiteralInOperatorInForInitializer.ts, 22, 10)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.types b/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.types new file mode 100644 index 0000000000000..378479194e439 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/arrayLiteralInOperatorInForInitializer.types @@ -0,0 +1,104 @@ +//// [tests/cases/compiler/arrayLiteralInOperatorInForInitializer.ts] //// + +=== arrayLiteralInOperatorInForInitializer.ts === +// The `in` operator is allowed in an array literal that appears in the head of +// a `for` statement: the for head's [~In] parameter does not reach into +// array literals (ElementList : AssignmentExpression[+In]). + +const o = { a: 1 }; +>o : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +const k = "a"; +>k : "a" +>"a" : "a" + +for (let i = 0, has = [k in o]; i < 1; i++) { +>i : number +>0 : 0 +>has : boolean[] +>[k in o] : boolean[] +>k in o : boolean +>k : "a" +>o : { a: number; } +>i < 1 : boolean +>i : number +>1 : 1 +>i++ : number +>i : number + + console.log(has); +>console.log(has) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>has : boolean[] +} + +for (let i = 0, has = [k in o, "b" in o, !(k in o)]; i < 1; i++) { +>i : number +>0 : 0 +>has : boolean[] +>[k in o, "b" in o, !(k in o)] : boolean[] +>k in o : boolean +>k : "a" +>o : { a: number; } +>"b" in o : boolean +>"b" : "b" +>o : { a: number; } +>!(k in o) : boolean +>(k in o) : boolean +>k in o : boolean +>k : "a" +>o : { a: number; } +>i < 1 : boolean +>i : number +>1 : 1 +>i++ : number +>i : number + + console.log(has); +>console.log(has) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>has : boolean[] +} + +let x: any; +>x : any + +for ([x = "x" in {}] of [[]]) { +>[x = "x" in {}] : [boolean] +>x = "x" in {} : boolean +>x : any +>"x" in {} : boolean +>"x" : "x" +>{} : {} +>[[]] : undefined[][] +>[] : undefined[] + + console.log(x); +>console.log(x) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x : any +} + +// The initializer of a `for` statement still ends at `in`, so that `for-in` +// statements keep parsing as before. +for (const key in o) { +>key : string +>o : { a: number; } + + console.log(key); +>console.log(key) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>key : string +} + diff --git a/tsc/testdata/tests/cases/compiler/arrayLiteralInOperatorInForInitializer.ts b/tsc/testdata/tests/cases/compiler/arrayLiteralInOperatorInForInitializer.ts new file mode 100644 index 0000000000000..51dfa5e6971e9 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/arrayLiteralInOperatorInForInitializer.ts @@ -0,0 +1,28 @@ +// @target: esnext +// @strict: false + +// The `in` operator is allowed in an array literal that appears in the head of +// a `for` statement: the for head's [~In] parameter does not reach into +// array literals (ElementList : AssignmentExpression[+In]). + +const o = { a: 1 }; +const k = "a"; + +for (let i = 0, has = [k in o]; i < 1; i++) { + console.log(has); +} + +for (let i = 0, has = [k in o, "b" in o, !(k in o)]; i < 1; i++) { + console.log(has); +} + +let x: any; +for ([x = "x" in {}] of [[]]) { + console.log(x); +} + +// The initializer of a `for` statement still ends at `in`, so that `for-in` +// statements keep parsing as before. +for (const key in o) { + console.log(key); +}