diff --git a/boa_parser/src/parser/expression/left_hand_side/member.rs b/boa_parser/src/parser/expression/left_hand_side/member.rs index f0ad8bf4ad..e42c34fe72 100644 --- a/boa_parser/src/parser/expression/left_hand_side/member.rs +++ b/boa_parser/src/parser/expression/left_hand_side/member.rs @@ -82,7 +82,7 @@ where TokenKind::Keyword((Keyword::New, false)) => { cursor.advance(interner); - if cursor.next_if(Punctuator::Dot, interner)?.is_some() { + let lhs_new_target = if cursor.next_if(Punctuator::Dot, interner)?.is_some() { let token = cursor.next(interner).or_abrupt()?; match token.kind() { TokenKind::IdentifierName((Sym::TARGET, ContainsEscapeSequence(true))) => { @@ -92,7 +92,7 @@ where )); } TokenKind::IdentifierName((Sym::TARGET, ContainsEscapeSequence(false))) => { - return Ok(ast::Expression::NewTarget) + ast::Expression::NewTarget } _ => { return Err(Error::general( @@ -101,19 +101,22 @@ where )); } } - } + } else { + let lhs_inner = self.parse(cursor, interner)?; + let args = match cursor.peek(0, interner)? { + Some(next) + if next.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) => + { + Arguments::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)? + } + _ => Box::new([]), + }; + let call_node = Call::new(lhs_inner, args); - let lhs = self.parse(cursor, interner)?; - let args = match cursor.peek(0, interner)? { - Some(next) if next.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) => { - Arguments::new(self.allow_yield, self.allow_await) - .parse(cursor, interner)? - } - _ => Box::new([]), + ast::Expression::from(New::from(call_node)) }; - let call_node = Call::new(lhs, args); - - ast::Expression::from(New::from(call_node)) + lhs_new_target } TokenKind::Keyword((Keyword::Super, _)) => { cursor.advance(interner); diff --git a/boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs b/boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs index 4bf8afbcde..68b5d97521 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs @@ -1,9 +1,14 @@ use crate::parser::tests::check_script_parser; use boa_ast::{ - expression::literal::Literal, + declaration::{LexicalDeclaration, Variable, VariableList}, + expression::{ + access::{PropertyAccess, SimplePropertyAccess}, + literal::Literal, + Call, Identifier, + }, function::{Class, ClassElement, FormalParameterList, Function}, property::{MethodDefinition, PropertyName}, - Declaration, StatementList, + Declaration, Expression, Statement, StatementList, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; @@ -90,3 +95,76 @@ fn check_async_field() { interner, ); } + +#[test] +fn check_new_target_with_property_access() { + let interner = &mut Interner::default(); + + let new_target = Expression::PropertyAccess( + SimplePropertyAccess::new( + Expression::NewTarget, + interner.get_or_intern_static("name", utf16!("name")), + ) + .into(), + ); + + let console = Expression::Call(Call::new( + PropertyAccess::Simple(SimplePropertyAccess::new( + Identifier::from(interner.get_or_intern_static("console", utf16!("console"))).into(), + interner.get_or_intern_static("log", utf16!("log")), + )) + .into(), + [new_target].into(), + )); + + let constructor = Function::new( + Some(interner.get_or_intern_static("A", utf16!("A")).into()), + FormalParameterList::default(), + StatementList::new([Statement::Expression(console).into()], false), + ); + + let class = Class::new( + Some(interner.get("A").unwrap().into()), + None, + Some(constructor), + Box::default(), + true, + ); + + let instantiation = Expression::New( + Call::new( + Identifier::from(interner.get("A").unwrap()).into(), + Box::default(), + ) + .into(), + ); + + let const_decl = LexicalDeclaration::Const( + VariableList::new( + [Variable::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(instantiation), + )] + .into(), + ) + .unwrap(), + ); + + let script = [ + StatementListItem::Declaration(class.into()), + StatementListItem::Declaration(const_decl.into()), + ]; + + check_script_parser( + r#" + class A { + constructor() { + console.log(new.target.name); + } + } + const a = new A(); + "#, + script, + interner, + ); +}