improve keyword-related parser errors (#1941)

fixes #1937
This commit is contained in:
Alex Lam S.L
2017-05-15 23:02:55 +08:00
committed by GitHub
parent ff526be61d
commit 265008c948
4 changed files with 295 additions and 239 deletions

View File

@@ -801,17 +801,16 @@ function parse($TEXT, options) {
}; };
var statement = embed_tokens(function() { var statement = embed_tokens(function() {
var tmp;
handle_regexp(); handle_regexp();
switch (S.token.type) { switch (S.token.type) {
case "string": case "string":
if (S.in_directives) { if (S.in_directives) {
tmp = peek(); var token = peek();
if (S.token.raw.indexOf("\\") == -1 if (S.token.raw.indexOf("\\") == -1
&& (tmp.nlb && (token.nlb
|| is_token(tmp, "eof") || is_token(token, "eof")
|| is_token(tmp, "punc", ";") || is_token(token, "punc", ";")
|| is_token(tmp, "punc", "}"))) { || is_token(token, "punc", "}"))) {
S.input.add_directive(S.token.value); S.input.add_directive(S.token.value);
} else { } else {
S.in_directives = false; S.in_directives = false;
@@ -850,72 +849,97 @@ function parse($TEXT, options) {
} }
case "keyword": case "keyword":
switch (tmp = S.token.value, next(), tmp) { switch (S.token.value) {
case "break": case "break":
next();
return break_cont(AST_Break); return break_cont(AST_Break);
case "continue": case "continue":
next();
return break_cont(AST_Continue); return break_cont(AST_Continue);
case "debugger": case "debugger":
next();
semicolon(); semicolon();
return new AST_Debugger(); return new AST_Debugger();
case "do": case "do":
next();
var body = in_loop(statement);
expect_token("keyword", "while");
var condition = parenthesised();
semicolon(true);
return new AST_Do({ return new AST_Do({
body : in_loop(statement), body : body,
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp) condition : condition
}); });
case "while": case "while":
next();
return new AST_While({ return new AST_While({
condition : parenthesised(), condition : parenthesised(),
body : in_loop(statement) body : in_loop(statement)
}); });
case "for": case "for":
next();
return for_(); return for_();
case "function": case "function":
next();
return function_(AST_Defun); return function_(AST_Defun);
case "if": case "if":
next();
return if_(); return if_();
case "return": case "return":
if (S.in_function == 0 && !options.bare_returns) if (S.in_function == 0 && !options.bare_returns)
croak("'return' outside of function"); croak("'return' outside of function");
next();
var value = null;
if (is("punc", ";")) {
next();
} else if (!can_insert_semicolon()) {
value = expression(true);
semicolon();
}
return new AST_Return({ return new AST_Return({
value: ( is("punc", ";") value: value
? (next(), null)
: can_insert_semicolon()
? null
: (tmp = expression(true), semicolon(), tmp) )
}); });
case "switch": case "switch":
next();
return new AST_Switch({ return new AST_Switch({
expression : parenthesised(), expression : parenthesised(),
body : in_loop(switch_body_) body : in_loop(switch_body_)
}); });
case "throw": case "throw":
next();
if (S.token.nlb) if (S.token.nlb)
croak("Illegal newline after 'throw'"); croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
return new AST_Throw({ return new AST_Throw({
value: (tmp = expression(true), semicolon(), tmp) value: value
}); });
case "try": case "try":
next();
return try_(); return try_();
case "var": case "var":
return tmp = var_(), semicolon(), tmp; next();
var node = var_();
semicolon();
return node;
case "with": case "with":
if (S.input.has_directive("use strict")) { if (S.input.has_directive("use strict")) {
croak("Strict mode may not include a with statement"); croak("Strict mode may not include a with statement");
} }
next();
return new AST_With({ return new AST_With({
expression : parenthesised(), expression : parenthesised(),
body : statement() body : statement()

View File

@@ -0,0 +1 @@
if (0) else 1;

View File

@@ -0,0 +1 @@
return 42;

View File

@@ -469,6 +469,36 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/else.js:1,7",
"if (0) else 1;",
" ^",
"ERROR: Unexpected token: keyword (else)"
].join("\n"));
done();
});
});
it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/return.js:1,0",
"return 42;",
"^",
"ERROR: 'return' outside of function"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) { it("Should handle literal string as source map input", function(done) {
var command = [ var command = [
uglifyjscmd, uglifyjscmd,