mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:47:34 +00:00
LibJS: Correctly handle parentheses and new Object
Parses `new Object()?.foo`, `(new Object)?.foo` and shows syntax error on `new Object?.foo`
This commit is contained in:
parent
acaf2368ad
commit
5c913d9cc4
3 changed files with 51 additions and 23 deletions
|
@ -3348,7 +3348,7 @@ ThrowCompletionOr<OptionalChain::ReferenceAndValue> OptionalChain::to_reference_
|
|||
[&](Call const& call) -> NonnullRefPtr<Expression const> {
|
||||
return CallExpression::create(source_range(),
|
||||
create_ast_node<SyntheticReferenceExpression>(source_range(), base_reference, base),
|
||||
call.arguments);
|
||||
call.arguments, InvocationStyleEnum::Parenthesized, InsideParenthesesEnum::NotInsideParentheses);
|
||||
},
|
||||
[&](ComputedReference const& ref) -> NonnullRefPtr<Expression const> {
|
||||
return create_ast_node<MemberExpression>(source_range(),
|
||||
|
@ -4965,14 +4965,14 @@ DeprecatedString SourceRange::filename() const
|
|||
return code->filename().to_deprecated_string();
|
||||
}
|
||||
|
||||
NonnullRefPtr<CallExpression> CallExpression::create(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments)
|
||||
NonnullRefPtr<CallExpression> CallExpression::create(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens)
|
||||
{
|
||||
return ASTNodeWithTailArray::create<CallExpression>(arguments.size(), move(source_range), move(callee), arguments);
|
||||
return ASTNodeWithTailArray::create<CallExpression>(arguments.size(), move(source_range), move(callee), arguments, invocation_style, inside_parens);
|
||||
}
|
||||
|
||||
NonnullRefPtr<NewExpression> NewExpression::create(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments)
|
||||
NonnullRefPtr<NewExpression> NewExpression::create(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens)
|
||||
{
|
||||
return ASTNodeWithTailArray::create<NewExpression>(arguments.size(), move(source_range), move(callee), arguments);
|
||||
return ASTNodeWithTailArray::create<NewExpression>(arguments.size(), move(source_range), move(callee), arguments, invocation_style, inside_parens);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1507,13 +1507,26 @@ struct CallExpressionArgument {
|
|||
bool is_spread;
|
||||
};
|
||||
|
||||
enum InvocationStyleEnum {
|
||||
Parenthesized,
|
||||
NotParenthesized,
|
||||
};
|
||||
|
||||
enum InsideParenthesesEnum {
|
||||
InsideParentheses,
|
||||
NotInsideParentheses,
|
||||
};
|
||||
|
||||
class CallExpression : public ASTNodeWithTailArray<CallExpression, Expression, CallExpressionArgument> {
|
||||
friend class ASTNodeWithTailArray;
|
||||
|
||||
InvocationStyleEnum m_invocation_style;
|
||||
InsideParenthesesEnum m_inside_parentheses;
|
||||
|
||||
public:
|
||||
using Argument = CallExpressionArgument;
|
||||
|
||||
static NonnullRefPtr<CallExpression> create(SourceRange, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments);
|
||||
static NonnullRefPtr<CallExpression> create(SourceRange, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens);
|
||||
|
||||
virtual Completion execute(Interpreter&) const override;
|
||||
virtual void dump(int indent) const override;
|
||||
|
@ -1523,9 +1536,15 @@ public:
|
|||
|
||||
ReadonlySpan<Argument> arguments() const { return tail_span(); }
|
||||
|
||||
bool is_parenthesized() const { return m_invocation_style == InvocationStyleEnum::Parenthesized; }
|
||||
bool is_inside_parens() const { return m_inside_parentheses == InsideParenthesesEnum::InsideParentheses; }
|
||||
void set_inside_parens() { m_inside_parentheses = InsideParenthesesEnum::InsideParentheses; }
|
||||
|
||||
protected:
|
||||
CallExpression(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments)
|
||||
CallExpression(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens = InsideParenthesesEnum::NotInsideParentheses)
|
||||
: ASTNodeWithTailArray(move(source_range), arguments)
|
||||
, m_invocation_style(invocation_style)
|
||||
, m_inside_parentheses(inside_parens)
|
||||
, m_callee(move(callee))
|
||||
{
|
||||
}
|
||||
|
@ -1550,15 +1569,15 @@ class NewExpression final : public CallExpression {
|
|||
friend class ASTNodeWithTailArray;
|
||||
|
||||
public:
|
||||
static NonnullRefPtr<NewExpression> create(SourceRange, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments);
|
||||
static NonnullRefPtr<NewExpression> create(SourceRange, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens);
|
||||
|
||||
virtual Completion execute(Interpreter&) const override;
|
||||
|
||||
virtual bool is_new_expression() const override { return true; }
|
||||
|
||||
private:
|
||||
NewExpression(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments)
|
||||
: CallExpression(move(source_range), move(callee), arguments)
|
||||
NewExpression(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens)
|
||||
: CallExpression(move(source_range), move(callee), arguments, invocation_style, inside_parens)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1419,7 +1419,10 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression()
|
|||
}
|
||||
auto expression = parse_expression(0);
|
||||
consume(TokenType::ParenClose);
|
||||
if (is<FunctionExpression>(*expression)) {
|
||||
if (is<NewExpression>(*expression)) {
|
||||
auto& new_expression = static_cast<NewExpression&>(*static_cast<NonnullRefPtr<Expression>>(expression));
|
||||
new_expression.set_inside_parens();
|
||||
} else if (is<FunctionExpression>(*expression)) {
|
||||
auto& function = static_cast<FunctionExpression const&>(*expression);
|
||||
if (function.kind() == FunctionKind::Generator && function.name() == "yield"sv)
|
||||
syntax_error("function is not allowed to be called 'yield' in this context", function.source_range().start);
|
||||
|
@ -2221,15 +2224,18 @@ Parser::ExpressionResult Parser::parse_secondary_expression(NonnullRefPtr<Expres
|
|||
return parse_assignment_expression(AssignmentOp::NullishAssignment, move(lhs), min_precedence, associativity, forbidden);
|
||||
case TokenType::QuestionMark:
|
||||
return parse_conditional_expression(move(lhs), forbidden);
|
||||
case TokenType::QuestionMarkPeriod:
|
||||
// FIXME: This should allow `(new Foo)?.bar', but as our parser strips parenthesis,
|
||||
// we can't really tell if `lhs' was parenthesized at this point.
|
||||
if (is<NewExpression>(lhs.ptr())) {
|
||||
syntax_error("'new' cannot be used with optional chaining", position());
|
||||
consume();
|
||||
return lhs;
|
||||
case TokenType::QuestionMarkPeriod: {
|
||||
auto const* lhs_expression = lhs.ptr();
|
||||
if (is<NewExpression>(lhs_expression)) {
|
||||
auto const& new_expression = static_cast<NewExpression const&>(*lhs_expression);
|
||||
if (!new_expression.is_parenthesized() && !new_expression.is_inside_parens()) {
|
||||
syntax_error("'new' cannot be used with optional chaining", position());
|
||||
consume();
|
||||
return lhs;
|
||||
}
|
||||
}
|
||||
return parse_optional_chain(move(lhs));
|
||||
}
|
||||
default:
|
||||
expected("secondary expression");
|
||||
consume();
|
||||
|
@ -2380,7 +2386,7 @@ NonnullRefPtr<Expression const> Parser::parse_call_expression(NonnullRefPtr<Expr
|
|||
if (is<SuperExpression>(*lhs))
|
||||
return create_ast_node<SuperCall>({ m_source_code, rule_start.position(), position() }, move(arguments));
|
||||
|
||||
return CallExpression::create({ m_source_code, rule_start.position(), position() }, move(lhs), arguments.span());
|
||||
return CallExpression::create({ m_source_code, rule_start.position(), position() }, move(lhs), arguments.span(), InvocationStyleEnum::Parenthesized, InsideParenthesesEnum::NotInsideParentheses);
|
||||
}
|
||||
|
||||
NonnullRefPtr<NewExpression const> Parser::parse_new_expression()
|
||||
|
@ -2394,8 +2400,10 @@ NonnullRefPtr<NewExpression const> Parser::parse_new_expression()
|
|||
|
||||
Vector<CallExpression::Argument> arguments;
|
||||
|
||||
if (match(TokenType::ParenOpen)) {
|
||||
consume(TokenType::ParenOpen);
|
||||
auto is_parenthesized = match(TokenType::ParenOpen);
|
||||
|
||||
if (is_parenthesized) {
|
||||
consume();
|
||||
while (match_expression() || match(TokenType::TripleDot)) {
|
||||
if (match(TokenType::TripleDot)) {
|
||||
consume();
|
||||
|
@ -2410,7 +2418,9 @@ NonnullRefPtr<NewExpression const> Parser::parse_new_expression()
|
|||
consume(TokenType::ParenClose);
|
||||
}
|
||||
|
||||
return NewExpression::create({ m_source_code, rule_start.position(), position() }, move(callee), move(arguments));
|
||||
InvocationStyleEnum invocation_style = is_parenthesized ? InvocationStyleEnum::Parenthesized : InvocationStyleEnum::NotParenthesized;
|
||||
|
||||
return NewExpression::create({ m_source_code, rule_start.position(), position() }, move(callee), move(arguments), invocation_style, InsideParenthesesEnum::NotInsideParentheses);
|
||||
}
|
||||
|
||||
NonnullRefPtr<YieldExpression const> Parser::parse_yield_expression()
|
||||
|
@ -4846,5 +4856,4 @@ Parser::ForbiddenTokens Parser::ForbiddenTokens::forbid(std::initializer_list<To
|
|||
|
||||
template NonnullRefPtr<FunctionExpression> Parser::parse_function_node(u16, Optional<Position> const&);
|
||||
template NonnullRefPtr<FunctionDeclaration> Parser::parse_function_node(u16, Optional<Position> const&);
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue