diff --git a/crates/oxc_parser/src/cursor.rs b/crates/oxc_parser/src/cursor.rs index eccb1f7846f8f..299190b5cc493 100644 --- a/crates/oxc_parser/src/cursor.rs +++ b/crates/oxc_parser/src/cursor.rs @@ -325,6 +325,32 @@ impl<'a> ParserImpl<'a> { answer } + #[expect(clippy::inline_always)] + #[inline(always)] // inline because this is always on a hot path + pub(crate) fn context_add(&mut self, add_flags: Context, cb: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + let ctx = self.ctx; + self.ctx = ctx.union(add_flags); + let result = cb(self); + self.ctx = ctx; + result + } + + #[expect(clippy::inline_always)] + #[inline(always)] // inline because this is always on a hot path + pub(crate) fn context_remove(&mut self, remove_flags: Context, cb: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + let ctx = self.ctx; + self.ctx = ctx.difference(remove_flags); + let result = cb(self); + self.ctx = ctx; + result + } + #[expect(clippy::inline_always)] #[inline(always)] // inline because this is always on a hot path pub(crate) fn context(&mut self, add_flags: Context, remove_flags: Context, cb: F) -> T diff --git a/crates/oxc_parser/src/js/binding.rs b/crates/oxc_parser/src/js/binding.rs index 6829fc3a1b92d..69191f6c3740e 100644 --- a/crates/oxc_parser/src/js/binding.rs +++ b/crates/oxc_parser/src/js/binding.rs @@ -10,7 +10,7 @@ impl<'a> ParserImpl<'a> { pub(super) fn parse_binding_pattern_with_initializer(&mut self) -> BindingPattern<'a> { let span = self.start_span(); let pattern = self.parse_binding_pattern(true); - self.context(Context::In, Context::empty(), |p| p.parse_initializer(span, pattern)) + self.context_add(Context::In, |p| p.parse_initializer(span, pattern)) } pub(super) fn parse_binding_pattern(&mut self, allow_question: bool) -> BindingPattern<'a> { @@ -114,8 +114,7 @@ impl<'a> ParserImpl<'a> { // Rest element does not allow `= initializer` // function foo([...x = []]) { } // ^^^^ A rest element cannot have an initializer - let argument = self - .context(Context::In, Context::empty(), |p| p.parse_initializer(init_span, pattern)); + let argument = self.context_add(Context::In, |p| p.parse_initializer(init_span, pattern)); if let BindingPatternKind::AssignmentPattern(pat) = &argument.kind { self.error(diagnostics::a_rest_element_cannot_have_an_initializer(pat.span)); } @@ -142,7 +141,7 @@ impl<'a> ParserImpl<'a> { let identifier = self.ast.binding_pattern_kind_binding_identifier(ident.span, ident.name); let left = self.ast.binding_pattern(identifier, NONE, false); - self.context(Context::In, Context::empty(), |p| p.parse_initializer(span, left)) + self.context_add(Context::In, |p| p.parse_initializer(span, left)) } else { return self.unexpected(); } diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index 3eba95c7a16fd..5461ad779a928 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -303,7 +303,7 @@ impl<'a> ParserImpl<'a> { if kind.is_identifier_or_keyword() || kind == Kind::Star || kind == Kind::LBrack { let is_ambient = modifiers.contains(ModifierKind::Declare); return if is_ambient { - self.context(Context::Ambient, Context::empty(), |p| { + self.context_add(Context::Ambient, |p| { p.parse_property_or_method_declaration(span, r#type, &modifiers, decorators) }) } else { diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 22ee252e3d773..f7e3b6bb8445a 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -437,7 +437,7 @@ impl<'a> ParserImpl<'a> { pub(crate) fn parse_array_expression(&mut self) -> Expression<'a> { let span = self.start_span(); self.expect(Kind::LBrack); - let (elements, comma_span) = self.context(Context::In, Context::empty(), |p| { + let (elements, comma_span) = self.context_add(Context::In, |p| { p.parse_delimited_list(Kind::RBrack, Kind::Comma, Self::parse_array_expression_element) }); if let Some(comma_span) = comma_span { @@ -480,7 +480,7 @@ impl<'a> ParserImpl<'a> { quasis.push(self.parse_template_element(tagged)); // TemplateHead Expression[+In, ?Yield, ?Await] - let expr = self.context(Context::In, Context::empty(), Self::parse_expr); + let expr = self.context_add(Context::In, Self::parse_expr); expressions.push(expr); self.re_lex_template_substitution_tail(); while self.fatal_error.is_none() { @@ -492,8 +492,7 @@ impl<'a> ParserImpl<'a> { Kind::TemplateMiddle => { quasis.push(self.parse_template_element(tagged)); // TemplateMiddle Expression[+In, ?Yield, ?Await] - let expr = - self.context(Context::In, Context::empty(), Self::parse_expr); + let expr = self.context_add(Context::In, Self::parse_expr); expressions.push(expr); self.re_lex_template_substitution_tail(); } @@ -840,7 +839,7 @@ impl<'a> ParserImpl<'a> { optional: bool, ) -> Expression<'a> { self.bump_any(); // advance `[` - let property = self.context(Context::In, Context::empty(), Self::parse_expr); + let property = self.context_add(Context::In, Self::parse_expr); self.expect(Kind::RBrack); self.ast.member_expression_computed(self.end_span(lhs_span), lhs, property, optional).into() } @@ -889,7 +888,7 @@ impl<'a> ParserImpl<'a> { let arguments = if self.eat(Kind::LParen) { // ArgumentList[Yield, Await] : // AssignmentExpression[+In, ?Yield, ?Await] - let (call_arguments, _) = self.context(Context::In, Context::empty(), |p| { + let (call_arguments, _) = self.context_add(Context::In, |p| { p.parse_delimited_list(Kind::RParen, Kind::Comma, Self::parse_call_argument) }); self.expect(Kind::RParen); @@ -1213,7 +1212,7 @@ impl<'a> ParserImpl<'a> { if !self.eat(Kind::Question) { return lhs; } - let consequent = self.context(Context::In, Context::empty(), |p| { + let consequent = self.context_add(Context::In, |p| { p.parse_assignment_expression_or_higher_impl( /* allow_return_type_in_arrow_function */ false, ) @@ -1399,9 +1398,8 @@ impl<'a> ParserImpl<'a> { self.error(diagnostics::await_expression(self.cur_token().span())); } self.bump_any(); - let argument = self.context(Context::Await, Context::empty(), |p| { - p.parse_simple_unary_expression(lhs_span) - }); + let argument = + self.context_add(Context::Await, |p| p.parse_simple_unary_expression(lhs_span)); self.ast.expression_await(self.end_span(span), argument) } @@ -1435,11 +1433,7 @@ impl<'a> ParserImpl<'a> { pub(crate) fn parse_decorator(&mut self) -> Decorator<'a> { let span = self.start_span(); self.bump_any(); // bump @ - let expr = self.context( - Context::Decorator, - Context::empty(), - Self::parse_lhs_expression_or_higher, - ); + let expr = self.context_add(Context::Decorator, Self::parse_lhs_expression_or_higher); self.ast.decorator(self.end_span(span), expr) } diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index 826d3cfd76307..cc5f791ed1d05 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -32,7 +32,7 @@ impl<'a> ParserImpl<'a> { let span = self.start_span(); self.expect(Kind::LCurly); - let (directives, statements) = self.context(Context::Return, Context::empty(), |p| { + let (directives, statements) = self.context_add(Context::Return, |p| { p.parse_directives_and_statements(/* is_top_level */ false) }); diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 44e7cc19a9788..5e15f293b619b 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -5,7 +5,7 @@ use rustc_hash::FxHashMap; use super::FunctionKind; use crate::{ - Context, ParserImpl, StatementContext, diagnostics, + ParserImpl, StatementContext, diagnostics, lexer::Kind, modifiers::{Modifier, ModifierFlags, ModifierKind, Modifiers}, }; @@ -281,7 +281,7 @@ impl<'a> ParserImpl<'a> { import_kind: ImportOrExportKind, ) -> Vec<'a, ImportDeclarationSpecifier<'a>> { self.expect(Kind::LCurly); - let (list, _) = self.context(Context::empty(), self.ctx, |p| { + let (list, _) = self.context_remove(self.ctx, |p| { p.parse_delimited_list(Kind::RCurly, Kind::Comma, |parser| { parser.parse_import_specifier(import_kind) }) @@ -302,7 +302,7 @@ impl<'a> ParserImpl<'a> { let span = self.start_span(); self.expect(Kind::LCurly); - let (with_entries, _) = self.context(Context::empty(), self.ctx, |p| { + let (with_entries, _) = self.context_remove(self.ctx, |p| { p.parse_delimited_list(Kind::RCurly, Kind::Comma, Self::parse_import_attribute) }); self.expect(Kind::RCurly); @@ -478,7 +478,7 @@ impl<'a> ParserImpl<'a> { ) -> Box<'a, ExportNamedDeclaration<'a>> { let export_kind = self.parse_import_or_export_kind(); self.expect(Kind::LCurly); - let (mut specifiers, _) = self.context(Context::empty(), self.ctx, |p| { + let (mut specifiers, _) = self.context_remove(self.ctx, |p| { p.parse_delimited_list(Kind::RCurly, Kind::Comma, |parser| { parser.parse_export_specifier(export_kind) }) diff --git a/crates/oxc_parser/src/js/object.rs b/crates/oxc_parser/src/js/object.rs index ab2d402940720..928196f9e1e5e 100644 --- a/crates/oxc_parser/src/js/object.rs +++ b/crates/oxc_parser/src/js/object.rs @@ -19,7 +19,7 @@ impl<'a> ParserImpl<'a> { pub(crate) fn parse_object_expression(&mut self) -> Box<'a, ObjectExpression<'a>> { let span = self.start_span(); self.expect(Kind::LCurly); - let (object_expression_properties, _) = self.context(Context::In, Context::empty(), |p| { + let (object_expression_properties, _) = self.context_add(Context::In, |p| { p.parse_delimited_list( Kind::RCurly, Kind::Comma, @@ -180,11 +180,7 @@ impl<'a> ParserImpl<'a> { pub(crate) fn parse_computed_property_name(&mut self) -> Expression<'a> { self.bump_any(); // advance `[` - let expression = self.context( - Context::In, - Context::empty(), - Self::parse_assignment_expression_or_higher, - ); + let expression = self.context_add(Context::In, Self::parse_assignment_expression_or_higher); self.expect(Kind::RBrack); expression diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index 3ec3831e10479..399d6cfa371fd 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -384,7 +384,7 @@ impl<'a> ParserImpl<'a> { let is_async = self.at(Kind::Async) && !self.cur_token().escaped(); let expr_span = self.start_span(); - let init_expression = self.context(Context::empty(), Context::In, ParserImpl::parse_expr); + let init_expression = self.context_remove(Context::In, ParserImpl::parse_expr); match self.cur_kind() { Kind::In => { @@ -418,7 +418,7 @@ impl<'a> ParserImpl<'a> { decl_kind: VariableDeclarationKind, r#await: bool, ) -> Statement<'a> { - let init_declaration = self.context(Context::empty(), Context::In, |p| { + let init_declaration = self.context_remove(Context::In, |p| { p.parse_variable_declaration( start_span, decl_kind, @@ -495,13 +495,13 @@ impl<'a> ParserImpl<'a> { let test = if matches!(self.cur_kind(), Kind::Semicolon | Kind::RParen) { None } else { - Some(self.context(Context::In, Context::empty(), ParserImpl::parse_expr)) + Some(self.context_add(Context::In, ParserImpl::parse_expr)) }; self.expect(Kind::Semicolon); let update = if self.at(Kind::RParen) { None } else { - Some(self.context(Context::In, Context::empty(), ParserImpl::parse_expr)) + Some(self.context_add(Context::In, ParserImpl::parse_expr)) }; self.expect(Kind::RParen); if r#await { @@ -575,7 +575,7 @@ impl<'a> ParserImpl<'a> { let argument = if self.eat(Kind::Semicolon) || self.can_insert_semicolon() { None } else { - let expr = self.context(Context::In, Context::empty(), ParserImpl::parse_expr); + let expr = self.context_add(Context::In, ParserImpl::parse_expr); self.asi(); Some(expr) }; diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index 7ad836cef21d2..c7c3bd63249d6 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -22,23 +22,14 @@ impl<'a> ParserImpl<'a> { && !self.cur_token().is_on_new_line() && self.eat(Kind::Extends) { - let extends_type = self.context( - Context::DisallowConditionalTypes, - Context::empty(), - Self::parse_ts_type, - ); + let extends_type = + self.context_add(Context::DisallowConditionalTypes, Self::parse_ts_type); self.expect(Kind::Question); - let true_type = self.context( - Context::empty(), - Context::DisallowConditionalTypes, - Self::parse_ts_type, - ); + let true_type = + self.context_remove(Context::DisallowConditionalTypes, Self::parse_ts_type); self.expect(Kind::Colon); - let false_type = self.context( - Context::empty(), - Context::DisallowConditionalTypes, - Self::parse_ts_type, - ); + let false_type = + self.context_remove(Context::DisallowConditionalTypes, Self::parse_ts_type); return self.ast.ts_type_conditional_type( self.end_span(span), ty, @@ -258,8 +249,7 @@ impl<'a> ParserImpl<'a> { Kind::Unique => self.parse_type_operator(TSTypeOperatorOperator::Unique), Kind::Readonly => self.parse_type_operator(TSTypeOperatorOperator::Readonly), Kind::Infer => self.parse_infer_type(), - _ => self.context( - Context::empty(), + _ => self.context_remove( Context::DisallowConditionalTypes, Self::parse_postfix_type_or_higher, ), @@ -1042,11 +1032,8 @@ impl<'a> ParserImpl<'a> { fn try_parse_constraint_of_infer_type(&mut self) -> Option> { if self.eat(Kind::Extends) { - let constraint = self.context( - Context::DisallowConditionalTypes, - Context::empty(), - Self::parse_ts_type, - ); + let constraint = + self.context_add(Context::DisallowConditionalTypes, Self::parse_ts_type); if self.ctx.has_disallow_conditional_types() || !self.at(Kind::Question) { return Some(constraint); } @@ -1067,11 +1054,7 @@ impl<'a> ParserImpl<'a> { fn parse_return_type(&mut self) -> TSType<'a> { self.bump_any(); - self.context( - Context::empty(), - Context::DisallowConditionalTypes, - Self::parse_type_or_type_predicate, - ) + self.context_remove(Context::DisallowConditionalTypes, Self::parse_type_or_type_predicate) } fn parse_type_or_type_predicate(&mut self) -> TSType<'a> {