From 9f7cb552648829796ab4d6836d08246e95f16b9d Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sun, 4 Jan 2015 21:08:39 +1100 Subject: [PATCH 1/4] Add stray semicolons as class elements --- acorn.js | 3 ++- test/tests-harmony.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/acorn.js b/acorn.js index 4945981d59..6ba8f97d81 100644 --- a/acorn.js +++ b/acorn.js @@ -2524,6 +2524,8 @@ classBody.body = []; expect(_braceL); while (!eat(_braceR)) { + while (eat(_semi)); + if (tokType === _braceR) continue; var method = startNode(); var isGenerator = eat(_star); parsePropertyName(method); @@ -2546,7 +2548,6 @@ } method.value = parseMethod(isGenerator); classBody.body.push(finishNode(method, "MethodDefinition")); - eat(_semi); } node.body = finishNode(classBody, "ClassBody"); return finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); diff --git a/test/tests-harmony.js b/test/tests-harmony.js index eb8b8c5812..086d85216e 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -8859,6 +8859,42 @@ test("class A { foo() {} get foo() {} }",{ locations: true }); +test("class Semicolon { ; }", { + type: "Program", + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 21} + }, + body: [{ + type: "ClassDeclaration", + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 21} + }, + id: { + type: "Identifier", + loc: { + start: {line: 1, column: 6}, + end: {line: 1, column: 15} + }, + name: "Semicolon" + }, + superClass: null, + body: { + type: "ClassBody", + loc: { + start: {line: 1, column: 16}, + end: {line: 1, column: 21} + }, + body: [] + } + }] +}, { + ecmaVersion: 6, + ranges: true, + locations: true +}); + // ES6: Computed Properties test("({[x]: 10})", { From a1d2561cfaa0f5070cf3b431bd154c0ebdf946cb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Jan 2015 12:02:30 +0100 Subject: [PATCH 2/4] Restore patch 9f7cb552648829796ab4d6836d08246e95f16b9d to original shape And make loose parser handle stray class semicolons Issue #190 --- acorn.js | 3 +-- acorn_loose.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/acorn.js b/acorn.js index 6ba8f97d81..95dd23bb7b 100644 --- a/acorn.js +++ b/acorn.js @@ -2524,8 +2524,7 @@ classBody.body = []; expect(_braceL); while (!eat(_braceR)) { - while (eat(_semi)); - if (tokType === _braceR) continue; + if (eat(_semi)) continue; var method = startNode(); var isGenerator = eat(_star); parsePropertyName(method); diff --git a/acorn_loose.js b/acorn_loose.js index 03d92b7352..a6542ee7d0 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -854,6 +854,7 @@ eat(tt.braceL); if (curIndent + 1 < indent) { indent = curIndent; line = curLineStart; } while (!closes(tt.braceR, indent, line)) { + if (isClass && semicolon()) continue; var prop = startNode(), isGenerator; if (options.ecmaVersion >= 6) { if (isClass) { @@ -904,7 +905,6 @@ if (isClass) { node.body.body.push(finishNode(prop, "MethodDefinition")); - semicolon(); } else { node.properties.push(finishNode(prop, "Property")); eat(tt.comma); From 33a7c9fc24187e10299dcc0d2378dcdb93887ac7 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Tue, 13 Jan 2015 11:10:28 +0000 Subject: [PATCH 3/4] Support import and export declarations in acorn/util/walk --- util/walk.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/util/walk.js b/util/walk.js index 117f56f93c..da7ca9f7e1 100644 --- a/util/walk.js +++ b/util/walk.js @@ -285,7 +285,15 @@ c(node.object, st, "Expression"); if (node.computed) c(node.property, st, "Expression"); }; - base.Identifier = base.Literal = base.ExportDeclaration = base.ImportDeclaration = ignore; + base.ExportDeclaration = function (node, st, c) { + c(node.declaration, st); + }; + base.ImportDeclaration = function (node, st, c) { + node.specifiers.forEach(function (specifier) { + c(specifier, st); + }); + }; + base.ImportSpecifier = base.ImportBatchSpecifier = base.Identifier = base.Literal = ignore; base.TaggedTemplateExpression = function(node, st, c) { c(node.tag, st, "Expression"); From 94b5efcd3ed845cb50d3ad676cda51766d1fc465 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Jan 2015 12:55:21 +0100 Subject: [PATCH 4/4] Disallow declaration statements in block-less context Closes #202 --- acorn.js | 37 +++++++++++++++++++++---------------- test/tests-harmony.js | 5 +++++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/acorn.js b/acorn.js index 95dd23bb7b..58d0d416ab 100644 --- a/acorn.js +++ b/acorn.js @@ -1638,7 +1638,7 @@ var first = true; if (!node.body) node.body = []; while (tokType !== _eof) { - var stmt = parseStatement(true); + var stmt = parseStatement(true, true); node.body.push(stmt); if (first && isUseStrict(stmt)) setStrict(true); first = false; @@ -1657,7 +1657,7 @@ // `if (foo) /blah/.exec(foo);`, where looking at the previous token // does not help. - function parseStatement(topLevel) { + function parseStatement(declaration, topLevel) { var starttype = tokType, node = startNode(); // Most types of statements are recognized by the keyword they @@ -1669,14 +1669,19 @@ case _debugger: return parseDebuggerStatement(node); case _do: return parseDoStatement(node); case _for: return parseForStatement(node); - case _function: return parseFunctionStatement(node); - case _class: return parseClass(node, true); + case _function: + if (!declaration && options.ecmaVersion >= 6) unexpected(); + return parseFunctionStatement(node); + case _class: + if (!declaration) unexpected(); + return parseClass(node, true); case _if: return parseIfStatement(node); case _return: return parseReturnStatement(node); case _switch: return parseSwitchStatement(node); case _throw: return parseThrowStatement(node); case _try: return parseTryStatement(node); - case _var: case _let: case _const: return parseVarStatement(node, starttype.keyword); + case _let: case _const: if (!declaration) unexpected(); // NOTE: falls through to _var + case _var: return parseVarStatement(node, starttype.keyword); case _while: return parseWhileStatement(node); case _with: return parseWithStatement(node); case _braceL: return parseBlock(); // no point creating a function for this @@ -1732,7 +1737,7 @@ function parseDoStatement(node) { next(); labels.push(loopLabel); - node.body = parseStatement(); + node.body = parseStatement(false); labels.pop(); expect(_while); node.test = parseParenExpression(); @@ -1782,8 +1787,8 @@ function parseIfStatement(node) { next(); node.test = parseParenExpression(); - node.consequent = parseStatement(); - node.alternate = eat(_else) ? parseStatement() : null; + node.consequent = parseStatement(false); + node.alternate = eat(_else) ? parseStatement(false) : null; return finishNode(node, "IfStatement"); } @@ -1827,7 +1832,7 @@ expect(_colon); } else { if (!cur) unexpected(); - cur.consequent.push(parseStatement()); + cur.consequent.push(parseStatement(true)); } } if (cur) finishNode(cur, "SwitchCase"); @@ -1878,7 +1883,7 @@ next(); node.test = parseParenExpression(); labels.push(loopLabel); - node.body = parseStatement(); + node.body = parseStatement(false); labels.pop(); return finishNode(node, "WhileStatement"); } @@ -1887,7 +1892,7 @@ if (strict) raise(tokStart, "'with' in strict mode"); next(); node.object = parseParenExpression(); - node.body = parseStatement(); + node.body = parseStatement(false); return finishNode(node, "WithStatement"); } @@ -1901,7 +1906,7 @@ if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared"); var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null; labels.push({name: maybeName, kind: kind}); - node.body = parseStatement(); + node.body = parseStatement(true); labels.pop(); node.label = expr; return finishNode(node, "LabeledStatement"); @@ -1932,7 +1937,7 @@ node.body = []; expect(_braceL); while (!eat(_braceR)) { - var stmt = parseStatement(); + var stmt = parseStatement(true); node.body.push(stmt); if (first && allowStrict && isUseStrict(stmt)) { oldStrict = strict; @@ -1955,7 +1960,7 @@ expect(_semi); node.update = tokType === _parenR ? null : parseExpression(); expect(_parenR); - node.body = parseStatement(); + node.body = parseStatement(false); labels.pop(); return finishNode(node, "ForStatement"); } @@ -1969,7 +1974,7 @@ node.left = init; node.right = parseExpression(); expect(_parenR); - node.body = parseStatement(); + node.body = parseStatement(false); labels.pop(); return finishNode(node, type); } @@ -2602,7 +2607,7 @@ next(); // export var|const|let|function|class ...; if (tokType === _var || tokType === _const || tokType === _let || tokType === _function || tokType === _class) { - node.declaration = parseStatement(); + node.declaration = parseStatement(true); node['default'] = false; node.specifiers = null; node.source = null; diff --git a/test/tests-harmony.js b/test/tests-harmony.js index 086d85216e..0ea8758cf7 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -14876,3 +14876,8 @@ test("`${/\d/.exec('1')[0]}`", { }, { ecmaVersion: 6 }); + +testFail("if (1) let x = 10;", "Unexpected token (1:7)", {ecmaVersion: 6}); +testFail("for (;;) const x = 10;", "Unexpected token (1:9)", {ecmaVersion: 6}); +testFail("while (1) function foo(){}", "Unexpected token (1:10)", {ecmaVersion: 6}); +testFail("if (1) ; else class Cls {}", "Unexpected token (1:14)", {ecmaVersion: 6});