diff options
author | 2021-05-27 14:42:33 -0700 | |
---|---|---|
committer | 2021-05-27 14:42:33 -0700 | |
commit | 84ea80b813c8b3dffe7ccc112b10fb034f21ea30 (patch) | |
tree | 673cef56fb0fc61dda87be6ef25caed1a9e6c5ed | |
parent | 3bcce51fa4d740b1e6ddf465919ec987f995e402 (diff) | |
download | bun-84ea80b813c8b3dffe7ccc112b10fb034f21ea30.tar.gz bun-84ea80b813c8b3dffe7ccc112b10fb034f21ea30.tar.zst bun-84ea80b813c8b3dffe7ccc112b10fb034f21ea30.zip |
Fix parsing await inside scopes that contain functions, return the backtracking error in TypeScript
-rw-r--r-- | src/js_parser/js_parser.zig | 91 | ||||
-rw-r--r-- | src/logger.zig | 4 |
2 files changed, 56 insertions, 39 deletions
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 7faf961c8..f1855c1a7 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -479,11 +479,11 @@ pub const ImportScanner = struct { } }; -pub const SideEffects = enum { +pub const SideEffects = enum(u2) { could_have_side_effects, no_side_effects, - pub const Result = struct { + pub const Result = packed struct { side_effects: SideEffects, ok: bool = false, value: bool = false, @@ -669,7 +669,7 @@ pub const SideEffects = enum { } } - pub const Equality = struct { equal: bool = false, ok: bool = false }; + pub const Equality = packed struct { equal: bool = false, ok: bool = false }; // Returns "equal, ok". If "ok" is false, then nothing is known about the two // values. If "ok" is true, the equality or inequality of the two values is @@ -1013,7 +1013,7 @@ const FunctionKind = enum { stmt, expr }; const EightLetterMatcher = strings.ExactSizeMatcher(8); -const AsyncPrefixExpression = enum { +const AsyncPrefixExpression = enum(u4) { none, is_yield, is_async, @@ -1042,7 +1042,7 @@ const AsyncPrefixExpression = enum { } }; -const IdentifierOpts = struct { +const IdentifierOpts = packed struct { assign_target: js_ast.AssignTarget = js_ast.AssignTarget.none, is_delete_target: bool = false, was_originally_identifier: bool = false, @@ -1080,7 +1080,7 @@ fn statementCaresAboutScope(stmt: Stmt) bool { } } -const ExprIn = struct { +const ExprIn = packed struct { // This tells us if there are optional chain expressions (EDot, EIndex, or // ECall) that are chained on to this expression. Because of the way the AST // works, chaining expressions on to this expression means they are our @@ -1236,19 +1236,14 @@ const ScopeOrder = struct { loc: logger.Loc, scope: *js_ast.Scope, }; -const EnumValueType = enum { - unknown, - string, - numeric, -}; -const ParenExprOpts = struct { +const ParenExprOpts = packed struct { async_range: logger.Range = logger.Range.None, is_async: bool = false, force_arrow_fn: bool = false, }; -const AwaitOrYield = enum { +const AwaitOrYield = enum(u3) { allow_ident, allow_expr, forbid_all, @@ -1257,15 +1252,18 @@ const AwaitOrYield = enum { // This is function-specific information used during parsing. It is saved and // restored on the call stack around code that parses nested functions and // arrow expressions. -const FnOrArrowDataParse = struct { - async_range: ?logger.Range = null, +const FnOrArrowDataParse = packed struct { + async_range: logger.Range = logger.Range.None, allow_await: AwaitOrYield = AwaitOrYield.allow_ident, allow_yield: AwaitOrYield = AwaitOrYield.allow_ident, allow_super_call: bool = false, is_top_level: bool = false, is_constructor: bool = false, is_typescript_declare: bool = false, - arrow_arg_errors: ?DeferredArrowArgErrors = null, + + has_async_range: bool = false, + arrow_arg_errors: DeferredArrowArgErrors = DeferredArrowArgErrors{}, + track_arrow_arg_errors: bool = false, // In TypeScript, forward declarations of functions have no bodies allow_missing_body_for_type_script: bool = false, @@ -1281,8 +1279,8 @@ const FnOrArrowDataParse = struct { // This is function-specific information used during visiting. It is saved and // restored on the call stack around code that parses nested functions and // arrow expressions. -const FnOrArrowDataVisit = struct { - super_index_ref: ?*js_ast.Ref = null, +const FnOrArrowDataVisit = packed struct { + // super_index_ref: ?*js_ast.Ref = null, is_arrow: bool = false, is_async: bool = false, @@ -2788,7 +2786,8 @@ pub const P = struct { var scopeIndex = try p.pushScopeForParsePass(js_ast.Scope.Kind.function_args, p.lexer.loc()); var func = try p.parseFn(name, FnOrArrowDataParse{ - .async_range = asyncRange, + .async_range = asyncRange orelse logger.Range.None, + .has_async_range = asyncRange != null, .allow_await = if (is_async) AwaitOrYield.allow_expr else AwaitOrYield.allow_ident, .allow_yield = if (is_generator) AwaitOrYield.allow_expr else AwaitOrYield.allow_ident, .is_typescript_declare = opts.is_typescript_declare, @@ -2880,10 +2879,10 @@ pub const P = struct { try p.lexer.expect(T.t_open_paren); // Await and yield are not allowed in function arguments - var old_fn_or_arrow_data = opts; + var old_fn_or_arrow_data = std.mem.toBytes(p.fn_or_arrow_data_parse); + p.fn_or_arrow_data_parse.allow_await = if (opts.allow_await == .allow_expr) AwaitOrYield.forbid_all else AwaitOrYield.allow_ident; p.fn_or_arrow_data_parse.allow_yield = if (opts.allow_yield == .allow_expr) AwaitOrYield.forbid_all else AwaitOrYield.allow_ident; - // If "super()" is allowed in the body, it's allowed in the arguments p.fn_or_arrow_data_parse.allow_super_call = opts.allow_super_call; var args = List(G.Arg).init(p.allocator); @@ -3012,7 +3011,7 @@ pub const P = struct { } try p.lexer.expect(.t_close_paren); - p.fn_or_arrow_data_parse = old_fn_or_arrow_data; + p.fn_or_arrow_data_parse = std.mem.bytesToValue(@TypeOf(p.fn_or_arrow_data_parse), &old_fn_or_arrow_data); // "function foo(): any {}" if (p.options.ts and p.lexer.token == .t_colon) { @@ -3254,7 +3253,7 @@ pub const P = struct { try p.lexer.next(); } else { try p.lexer.unexpected(); - return error.SyntaxError; + return error.Backtrack; } }, } @@ -3275,7 +3274,7 @@ pub const P = struct { }, else => { try p.lexer.unexpected(); - return error.SyntaxError; + return error.Backtrack; }, } } @@ -3532,7 +3531,7 @@ pub const P = struct { else => { try p.lexer.unexpected(); - return error.SyntaxError; + return error.Backtrack; }, } break; @@ -4872,6 +4871,7 @@ pub const P = struct { try p.lexer.next(); if (p.lexer.token == .t_function and !p.lexer.has_newline_before) { try p.lexer.next(); + return try p.parseFnStmt(async_range.loc, opts, async_range); } @@ -5706,6 +5706,11 @@ pub const P = struct { try p.lexer.expect(.t_colon); try p.skipTypeScriptType(.lowest); } + + // If we end with a .t_close_paren, that's a bug. It means we aren't following the last parenthese + if (isDebug) { + std.debug.assert(p.lexer.token != .t_close_paren); + } } if (p.lexer.token == .t_equals) { @@ -6484,9 +6489,11 @@ pub const P = struct { pub const Backtracking = struct { pub fn lexerBacktracker(p: *P, func: anytype) bool { - var old_lexer = p.lexer.clone(); + var old_lexer = std.mem.toBytes(p.lexer); + const old_log_disabled = p.lexer.is_log_disabled; p.lexer.is_log_disabled = true; - defer p.lexer.is_log_disabled = old_lexer.is_log_disabled; + + defer p.lexer.is_log_disabled = old_log_disabled; var backtrack = false; func(p) catch |err| { switch (err) { @@ -6498,7 +6505,7 @@ pub const P = struct { }; if (backtrack) { - p.lexer = old_lexer; + p.lexer = std.mem.bytesToValue(@TypeOf(p.lexer), &old_lexer); } return !backtrack; @@ -6514,7 +6521,9 @@ pub const P = struct { pub fn skipTypeScriptArrowArgsWithBacktracking(p: *P) anyerror!void { try p.skipTypescriptFnArgs(); - try p.lexer.expect(.t_equals_greater_than); + p.lexer.expect(.t_equals_greater_than) catch |err| { + return error.Backtrack; + }; } pub fn skipTypeScriptTypeArgumentsWithBacktracking(p: *P) anyerror!void { @@ -6528,7 +6537,9 @@ pub const P = struct { } pub fn skipTypeScriptArrowReturnTypeWithBacktracking(p: *P) anyerror!void { - try p.lexer.expect(.t_colon); + p.lexer.expect(.t_colon) catch |err| { + return error.Backtrack; + }; try p.skipTypescriptReturnType(); // Check the token after this and backtrack if it's the wrong one if (p.lexer.token != .t_equals_greater_than) { @@ -6985,6 +6996,7 @@ pub const P = struct { var func = try p.parseFn(null, FnOrArrowDataParse{ .async_range = opts.async_range, + .has_async_range = !opts.async_range.isEmpty(), .allow_await = if (opts.is_async) AwaitOrYield.allow_expr else AwaitOrYield.allow_ident, .allow_yield = if (opts.is_generator) AwaitOrYield.allow_expr else AwaitOrYield.allow_ident, .allow_super_call = opts.class_has_extends and is_constructor, @@ -8173,8 +8185,8 @@ pub const P = struct { p.top_level_await_keyword = name_range; } - if (p.fn_or_arrow_data_parse.arrow_arg_errors) |*args| { - args.invalid_expr_await = name_range; + if (p.fn_or_arrow_data_parse.track_arrow_arg_errors) { + p.fn_or_arrow_data_parse.arrow_arg_errors.invalid_expr_await = name_range; } const value = try p.parseExpr(.prefix); @@ -8204,8 +8216,8 @@ pub const P = struct { } const value = try p.parseExpr(.prefix); - if (p.fn_or_arrow_data_parse.arrow_arg_errors) |*args| { - args.invalid_expr_yield = name_range; + if (p.fn_or_arrow_data_parse.track_arrow_arg_errors) { + p.fn_or_arrow_data_parse.arrow_arg_errors.invalid_expr_yield = name_range; } if (p.lexer.token == T.t_asterisk_asterisk) { @@ -9260,8 +9272,8 @@ pub const P = struct { pub fn visitFunc(p: *P, _func: G.Fn, open_parens_loc: logger.Loc) G.Fn { var func = _func; - const old_fn_or_arrow_data = p.fn_or_arrow_data_visit; - const old_fn_only_data = p.fn_only_data_visit; + const old_fn_or_arrow_data = std.mem.toBytes(p.fn_or_arrow_data_visit); + const old_fn_only_data = std.mem.toBytes(p.fn_only_data_visit); p.fn_or_arrow_data_visit = FnOrArrowDataVisit{ .is_async = func.flags.is_async }; p.fn_only_data_visit = FnOnlyDataVisit{ .is_this_nested = true, .arguments_ref = func.arguments_ref }; @@ -9296,8 +9308,8 @@ pub const P = struct { p.popScope(); p.popScope(); - p.fn_or_arrow_data_visit = old_fn_or_arrow_data; - p.fn_only_data_visit = old_fn_only_data; + p.fn_or_arrow_data_visit = std.mem.bytesToValue(@TypeOf(p.fn_or_arrow_data_visit), &old_fn_or_arrow_data); + p.fn_only_data_visit = std.mem.bytesToValue(@TypeOf(p.fn_only_data_visit), &old_fn_only_data); return func; } @@ -12168,6 +12180,7 @@ pub const P = struct { // Forbid "await" and "yield", but only for arrow functions var old_fn_or_arrow_data = p.fn_or_arrow_data_parse; p.fn_or_arrow_data_parse.arrow_arg_errors = arrowArgErrors; + p.fn_or_arrow_data_parse.track_arrow_arg_errors = true; // Scan over the comma-separated arguments or expressions while (p.lexer.token != .t_close_paren) { @@ -12616,7 +12629,7 @@ pub const P = struct { // // This is an error // function* foo() { (x = yield y) => {} } // -const DeferredArrowArgErrors = struct { +const DeferredArrowArgErrors = packed struct { invalid_expr_await: logger.Range = logger.Range.None, invalid_expr_yield: logger.Range = logger.Range.None, }; diff --git a/src/logger.zig b/src/logger.zig index 8d282ee13..233df0485 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -192,6 +192,10 @@ pub const Range = packed struct { len: i32 = 0, pub const None = Range{ .loc = Loc.Empty, .len = 0 }; + pub fn isEmpty(r: *const Range) bool { + return r.len == 0 and r.loc.start == Loc.Empty.start; + } + pub fn end(self: *const Range) Loc { return Loc{ .start = self.loc.start + self.len }; } |