diff --git a/acorn.js b/acorn.js index 3ae65e38b7..1507882e3a 100644 --- a/acorn.js +++ b/acorn.js @@ -1872,11 +1872,10 @@ // Convert existing expression atom to assignable pattern // if possible. - function toAssignable(node) { + function toAssignable(node, isBinding) { if (options.ecmaVersion >= 6 && node) { switch (node.type) { case "Identifier": - case "MemberExpression": case "ObjectPattern": case "ArrayPattern": case "AssignmentPattern": @@ -1887,13 +1886,13 @@ for (var i = 0; i < node.properties.length; i++) { var prop = node.properties[i]; if (prop.kind !== "init") raise(prop.key.start, "Object pattern can't contain getter or setter"); - toAssignable(prop.value); + toAssignable(prop.value, isBinding); } break; case "ArrayExpression": node.type = "ArrayPattern"; - toAssignableList(node.elements); + toAssignableList(node.elements, isBinding); break; case "AssignmentExpression": @@ -1904,6 +1903,9 @@ } break; + case "MemberExpression": + if (!isBinding) break; + default: raise(node.start, "Assigning to rvalue"); } @@ -1913,10 +1915,10 @@ // Convert list of expression atoms to binding list. - function toAssignableList(exprList) { + function toAssignableList(exprList, isBinding) { if (exprList.length) { for (var i = 0; i < exprList.length - 1; i++) { - toAssignable(exprList[i]); + toAssignable(exprList[i], isBinding); } var last = exprList[exprList.length - 1]; switch (last.type) { @@ -1925,12 +1927,12 @@ case "SpreadElement": last.type = "RestElement"; var arg = last.argument; - toAssignable(arg); - if (arg.type !== "Identifier" && arg.type !== "ArrayPattern") + toAssignable(arg, isBinding); + if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") unexpected(arg.start); break; default: - toAssignable(last); + toAssignable(last, isBinding); } } return exprList; @@ -1948,13 +1950,13 @@ function parseRest() { var node = startNode(); next(); - node.argument = tokType === _name || tokType === _bracketL ? parseAssignableAtom() : unexpected(); + node.argument = tokType === _name || tokType === _bracketL ? parseBindingAtom() : unexpected(); return finishNode(node, "RestElement"); } // Parses lvalue (assignable) atom. - function parseAssignableAtom() { + function parseBindingAtom() { if (options.ecmaVersion < 6) return parseIdent(); switch (tokType) { case _name: @@ -1963,7 +1965,7 @@ case _bracketL: var node = startNode(); next(); - node.elements = parseAssignableList(_bracketR, true); + node.elements = parseBindingList(_bracketR, true); return finishNode(node, "ArrayPattern"); case _braceL: @@ -1974,7 +1976,7 @@ } } - function parseAssignableList(close, allowEmpty) { + function parseBindingList(close, allowEmpty) { var elts = [], first = true; while (!eat(close)) { first ? first = false : expect(_comma); @@ -1991,9 +1993,10 @@ // Parses assignment pattern around given atom if possible. function parseMaybeDefault(startPos, left) { - left = left || parseAssignableAtom(); + startPos = startPos || storeCurrentPos(); + left = left || parseBindingAtom(); if (!eat(_eq)) return left; - var node = startPos ? startNodeAt(startPos) : startNode(); + var node = startNodeAt(startPos); node.operator = "="; node.left = left; node.right = parseMaybeAssign(); @@ -2333,7 +2336,7 @@ var clause = startNode(); next(); expect(_parenL); - clause.param = parseAssignableAtom(); + clause.param = parseBindingAtom(); checkLVal(clause.param, true); expect(_parenR); clause.guard = null; @@ -2461,7 +2464,7 @@ node.kind = kind; for (;;) { var decl = startNode(); - decl.id = parseAssignableAtom(); + decl.id = parseBindingAtom(); checkLVal(decl.id, true); decl.init = eat(_eq) ? parseMaybeAssign(noIn) : (kind === _const.keyword ? unexpected() : null); node.declarations.push(finishNode(decl, "VariableDeclarator")); @@ -2840,15 +2843,16 @@ if (options.ecmaVersion >= 6) { prop.method = false; prop.shorthand = false; - if (isPattern) { + if (isPattern || refShorthandDefaultPos) { start = storeCurrentPos(); - } else { + } + if (!isPattern) { isGenerator = eat(_star); } } parsePropertyName(prop); if (eat(_colon)) { - prop.value = isPattern ? parseMaybeDefault(start) : parseMaybeAssign(false, refShorthandDefaultPos); + prop.value = isPattern ? parseMaybeDefault() : parseMaybeAssign(false, refShorthandDefaultPos); prop.kind = "init"; } else if (options.ecmaVersion >= 6 && tokType === _parenL) { if (isPattern) unexpected(); @@ -2918,7 +2922,7 @@ node.id = parseIdent(); } expect(_parenL); - node.params = parseAssignableList(_parenR, false); + node.params = parseBindingList(_parenR, false); parseFunctionBody(node, allowExpressionBody); return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); } @@ -2929,7 +2933,7 @@ var node = startNode(); initFunction(node); expect(_parenL); - node.params = parseAssignableList(_parenR, false); + node.params = parseBindingList(_parenR, false); var allowExpressionBody; if (options.ecmaVersion >= 6) { node.generator = isGenerator; @@ -3082,7 +3086,14 @@ } else // export default ...; if (eat(_default)) { - node.declaration = parseMaybeAssign(); + var expr = parseMaybeAssign(); + if (expr.id) { + switch (expr.type) { + case "FunctionExpression": expr.type = "FunctionDeclaration"; break; + case "ClassExpression": expr.type = "ClassDeclaration"; break; + } + } + node.declaration = expr; node['default'] = true; node.specifiers = null; node.source = null; @@ -3213,7 +3224,7 @@ var block = startNode(); next(); expect(_parenL); - block.left = parseAssignableAtom(); + block.left = parseBindingAtom(); checkLVal(block.left, true); expectContextual("of"); block.right = parseExpression(); diff --git a/acorn_loose.js b/acorn_loose.js index ab0c6ea709..0f52fcc47a 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -235,13 +235,14 @@ if (options.locations) { node = new Node(pos[0]); node.loc = new SourceLocation(pos[1]); + pos = pos[0]; } else { node = new Node(pos); } if (options.directSourceFile) node.sourceFile = options.directSourceFile; if (options.ranges) - node.range = [pos[0], 0]; + node.range = [pos, 0]; return node; } @@ -365,7 +366,7 @@ } var init = parseExpression(true); if (token.type === tt._in || isContextual("of")) { - return parseForIn(node, checkLVal(init)); + return parseForIn(node, toAssignable(init)); } return parseFor(node, init); @@ -432,7 +433,7 @@ var clause = startNode(); next(); expect(tt.parenL); - clause.param = parseIdent(); + clause.param = toAssignable(parseExprAtom()); expect(tt.parenR); clause.guard = null; clause.body = parseBlock(); @@ -845,6 +846,7 @@ next(); if (token.type === tt.name) node.id = parseIdent(); else if (isStatement) node.id = dummyIdent(); + else node.id = null; node.superClass = eat(tt._extends) ? parseExpression() : null; node.body = startNode(); node.body.body = []; @@ -857,11 +859,12 @@ if (curIndent + 1 < indent) { indent = curIndent; line = curLineStart; } while (!closes(tt.braceR, indent, line)) { if (isClass && semicolon()) continue; - var prop = startNode(), isGenerator; + var prop = startNode(), isGenerator, start; if (options.ecmaVersion >= 6) { if (isClass) { prop['static'] = false; } else { + start = storeCurrentPos(); prop.method = false; prop.shorthand = false; } @@ -901,7 +904,19 @@ prop.value = parseMethod(isGenerator); } else { prop.kind = "init"; - prop.value = options.ecmaVersion >= 6 ? prop.key : dummyIdent(); + if (options.ecmaVersion >= 6) { + if (eat(tt.eq)) { + var assign = startNodeAt(start); + assign.operator = "="; + assign.left = prop.key; + assign.right = parseMaybeAssign(); + prop.value = finishNode(assign, "AssignmentExpression"); + } else { + prop.value = prop.key; + } + } else { + prop.value = dummyIdent(); + } prop.shorthand = true; } @@ -1044,7 +1059,14 @@ node['default'] = eat(tt._default); node.specifiers = node.source = null; if (node['default']) { - node.declaration = parseExpression(); + var expr = parseMaybeAssign(); + if (expr.id) { + switch (expr.type) { + case "FunctionExpression": expr.type = "FunctionDeclaration"; break; + case "ClassExpression": expr.type = "ClassDeclaration"; break; + } + } + node.declaration = expr; semicolon(); } else if (token.type.keyword) { node.declaration = parseStatement(); diff --git a/test/tests-harmony.js b/test/tests-harmony.js index 596e5f7ab0..1b45b380cd 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -1712,74 +1712,7 @@ test("([a, , b]) => 42", { locations: true }); -test("([a.a]) => 42", { - type: "Program", - body: [{ - type: "ExpressionStatement", - expression: { - type: "ArrowFunctionExpression", - id: null, - params: [{ - type: "ArrayPattern", - elements: [{ - type: "MemberExpression", - computed: false, - object: { - type: "Identifier", - name: "a", - loc: { - start: {line: 1, column: 2}, - end: {line: 1, column: 3} - } - }, - property: { - type: "Identifier", - name: "a", - loc: { - start: {line: 1, column: 4}, - end: {line: 1, column: 5} - } - }, - loc: { - start: {line: 1, column: 2}, - end: {line: 1, column: 5} - } - }], - loc: { - start: {line: 1, column: 1}, - end: {line: 1, column: 6} - } - }], - body: { - type: "Literal", - value: 42, - raw: "42", - loc: { - start: {line: 1, column: 11}, - end: {line: 1, column: 13} - } - }, - generator: false, - expression: true, - loc: { - start: {line: 1, column: 0}, - end: {line: 1, column: 13} - } - }, - loc: { - start: {line: 1, column: 0}, - end: {line: 1, column: 13} - } - }], - loc: { - start: {line: 1, column: 0}, - end: {line: 1, column: 13} - } -}, { - ecmaVersion: 6, - ranges: true, - locations: true -}); +testFail("([a.a]) => 42", "Assigning to rvalue (1:2)", {ecmaVersion: 6}); test("(x=1) => x * x", { type: "Program", @@ -1806,6 +1739,10 @@ test("(x=1) => x * x", { start: {line: 1, column: 3}, end: {line: 1, column: 4} } + }, + loc: { + start: {line: 1, column: 1}, + end: {line: 1, column: 4} } }], body: { @@ -2073,6 +2010,10 @@ test("(eval = 10) => 42", { start: {line: 1, column: 8}, end: {line: 1, column: 10} } + }, + loc: { + start: {line: 1, column: 1}, + end: {line: 1, column: 10} } }], body: { @@ -2140,6 +2081,10 @@ test("(eval, a = 10) => 42", { start: {line: 1, column: 11}, end: {line: 1, column: 13} } + }, + loc: { + start: {line: 1, column: 7}, + end: {line: 1, column: 13} } } ], @@ -4881,6 +4826,110 @@ test("export default 42", { locations: true }); +test("export default function () {}", { + type: "Program", + range: [0, 29], + body: [{ + type: "ExportDeclaration", + range: [0, 29], + declaration: { + type: "FunctionExpression", + range: [15, 29], + id: null, + generator: false, + expression: false, + params: [], + body: { + type: "BlockStatement", + range: [27, 29], + body: [] + } + }, + default: true, + specifiers: null, + source: null + }] +}, {ecmaVersion: 6, ranges: true}); + +test("export default function f() {}", { + type: "Program", + range: [0, 30], + body: [{ + type: "ExportDeclaration", + range: [0, 30], + declaration: { + type: "FunctionDeclaration", + range: [15, 30], + id: { + type: "Identifier", + range: [24, 25], + name: "f" + }, + generator: false, + expression: false, + params: [], + body: { + type: "BlockStatement", + range: [28, 30], + body: [] + } + }, + default: true, + specifiers: null, + source: null + }] +}, {ecmaVersion: 6, ranges: true}); + +test("export default class {}", { + type: "Program", + range: [0, 23], + body: [{ + type: "ExportDeclaration", + range: [0, 23], + declaration: { + type: "ClassExpression", + range: [15, 23], + id: null, + superClass: null, + body: { + type: "ClassBody", + range: [21, 23], + body: [] + } + }, + default: true, + specifiers: null, + source: null + }] +}, {ecmaVersion: 6, ranges: true}); + +test("export default class A {}", { + type: "Program", + range: [0, 25], + body: [{ + type: "ExportDeclaration", + range: [0, 25], + declaration: { + type: "ClassDeclaration", + range: [15, 25], + id: { + type: "Identifier", + range: [21, 22], + name: "A" + }, + superClass: null, + body: { + type: "ClassBody", + range: [23, 25], + body: [] + } + }, + default: true, + specifiers: null, + source: null + }] +}, {ecmaVersion: 6, ranges: true}); + testFail("export *", "Unexpected token (1:8)", {ecmaVersion: 6}); test("export * from \"crypto\"", { @@ -9589,6 +9638,10 @@ test("function f([x] = [1]) {}", { start: {line: 1, column: 17}, end: {line: 1, column: 20} } + }, + loc: { + start: {line: 1, column: 11}, + end: {line: 1, column: 20} } }], body: { @@ -9698,6 +9751,10 @@ test("function f({x} = {x: 10}) {}", { start: {line: 1, column: 17}, end: {line: 1, column: 24} } + }, + loc: { + start: {line: 1, column: 11}, + end: {line: 1, column: 24} } }], body: { @@ -9813,6 +9870,10 @@ test("f = function({x} = {x: 10}) {}", { start: {line: 1, column: 19}, end: {line: 1, column: 26} } + }, + loc: { + start: {line: 1, column: 13}, + end: {line: 1, column: 26} } }], body: { @@ -9939,6 +10000,10 @@ test("({f: function({x} = {x: 10}) {}})", { start: {line: 1, column: 20}, end: {line: 1, column: 27} } + }, + loc: { + start: {line: 1, column: 14}, + end: {line: 1, column: 27} } }], body: { @@ -10074,6 +10139,10 @@ test("({f({x} = {x: 10}) {}})", { start: {line: 1, column: 10}, end: {line: 1, column: 17} } + }, + loc: { + start: {line: 1, column: 4}, + end: {line: 1, column: 17} } }], body: { @@ -10213,6 +10282,10 @@ test("(class {f({x} = {x: 10}) {}})", { start: {line: 1, column: 16}, end: {line: 1, column: 23} } + }, + loc: { + start: {line: 1, column: 10}, + end: {line: 1, column: 23} } }], body: { @@ -10339,6 +10412,10 @@ test("(({x} = {x: 10}) => {})", { start: {line: 1, column: 8}, end: {line: 1, column: 15} } + }, + loc: { + start: {line: 1, column: 2}, + end: {line: 1, column: 15} } }], body: { @@ -10407,6 +10484,10 @@ test("x = function(y = 1) {}", { start: {line: 1, column: 17}, end: {line: 1, column: 18} } + }, + loc: { + start: {line: 1, column: 13}, + end: {line: 1, column: 18} } }], body: { @@ -10474,6 +10555,10 @@ test("function f(a = 1) {}", { start: {line: 1, column: 15}, end: {line: 1, column: 16} } + }, + loc: { + start: {line: 1, column: 11}, + end: {line: 1, column: 16} } }], body: { @@ -10549,6 +10634,10 @@ test("x = { f: function(a=1) {} }", { start: {line: 1, column: 20}, end: {line: 1, column: 21} } + }, + loc: { + start: {line: 1, column: 18}, + end: {line: 1, column: 21} } }], body: { @@ -10648,6 +10737,10 @@ test("x = { f(a=1) {} }", { start: {line: 1, column: 10}, end: {line: 1, column: 11} } + }, + loc: { + start: {line: 1, column: 8}, + end: {line: 1, column: 11} } }], body: { @@ -14379,7 +14472,7 @@ test("var {propName: localVar = defaultValue} = obj", { }, value: { type: "AssignmentPattern", - range: [5, 38], + range: [15, 38], operator: "=", left: { type: "Identifier", @@ -14406,8 +14499,7 @@ test("var {propName: localVar = defaultValue} = obj", { }, { ecmaVersion: 6, ranges: true, - locations: true, - loose: false + locations: true }); test("var {propName = defaultValue} = obj", { @@ -14462,8 +14554,7 @@ test("var {propName = defaultValue} = obj", { }, { ecmaVersion: 6, ranges: true, - locations: true, - loose: false + locations: true }); test("var [localVar = defaultValue] = obj", { @@ -14480,7 +14571,7 @@ test("var [localVar = defaultValue] = obj", { range: [4, 29], elements: [{ type: "AssignmentPattern", - range: [16, 28], + range: [5, 28], operator: "=", left: { type: "Identifier", @@ -14505,8 +14596,7 @@ test("var [localVar = defaultValue] = obj", { }, { ecmaVersion: 6, ranges: true, - locations: true, - loose: false + locations: true }); test("({x = 0} = obj)", { @@ -14536,7 +14626,7 @@ test("({x = 0} = obj)", { kind: "init", value: { type: "AssignmentPattern", - range: [6, 7], + range: [2, 7], operator: "=", left: { type: "Identifier", @@ -14560,8 +14650,7 @@ test("({x = 0} = obj)", { }] }, { ecmaVersion: 6, - ranges: true, - loose: false + ranges: true }); test("({x = 0}) => x", { @@ -14593,7 +14682,7 @@ test("({x = 0}) => x", { kind: "init", value: { type: "AssignmentPattern", - range: [6, 7], + range: [2, 7], operator: "=", left: { type: "Identifier", @@ -14617,8 +14706,7 @@ test("({x = 0}) => x", { }] }, { ecmaVersion: 6, - ranges: true, - loose: false + ranges: true }); test("[a, {b: {c = 1}}] = arr", { @@ -14671,7 +14759,7 @@ test("[a, {b: {c = 1}}] = arr", { kind: "init", value: { type: "AssignmentPattern", - range: [13, 14], + range: [9, 14], operator: "=", left: { type: "Identifier", @@ -14700,8 +14788,7 @@ test("[a, {b: {c = 1}}] = arr", { }] }, { ecmaVersion: 6, - ranges: true, - loose: false + ranges: true }); test("for ({x = 0} in arr);", { @@ -14727,7 +14814,7 @@ test("for ({x = 0} in arr);", { kind: "init", value: { type: "AssignmentPattern", - range: [10, 11], + range: [6, 11], operator: "=", left: { type: "Identifier", @@ -14754,8 +14841,7 @@ test("for ({x = 0} in arr);", { }] }, { ecmaVersion: 6, - ranges: true, - loose: false + ranges: true }); testFail("obj = {x = 0}", "Unexpected token (1:9)", {ecmaVersion: 6}); @@ -14807,14 +14893,12 @@ test("try {} catch ({message}) {}", { body: [] } }, - guardedHandlers: [], finalizer: null }] }, { ecmaVersion: 6, ranges: true, - locations: true, - loose: false + locations: true }); // https://github.com/marijnh/acorn/issues/192