in lib/Parser/JSParserImpl.cpp [3325:3469]
Optional<ESTree::Node *> JSParserImpl::parseMemberSelect(
SMLoc startLoc,
SMLoc objectLoc,
ESTree::NodePtr expr,
bool seenOptionalChain) {
assert(
checkN(TokenKind::l_square, TokenKind::period, TokenKind::questiondot));
SMLoc puncLoc = tok_->getStartLoc();
bool optional = checkAndEat(TokenKind::questiondot);
if (checkAndEat(TokenKind::l_square)) {
// Parsing another Expression directly without going through
// PrimaryExpression. This can overflow, so check.
CHECK_RECURSION;
auto propExpr = parseExpression();
if (!propExpr)
return None;
SMLoc endLoc = tok_->getEndLoc();
if (!eat(
TokenKind::r_square,
JSLexer::AllowDiv,
"at end of member expression '[...'",
"location iof '['",
puncLoc))
return None;
if (optional || seenOptionalChain) {
return setLocation(
startLoc,
endLoc,
puncLoc,
new (context_) ESTree::OptionalMemberExpressionNode(
expr, propExpr.getValue(), true, optional));
}
return setLocation(
startLoc,
endLoc,
puncLoc,
new (context_)
ESTree::MemberExpressionNode(expr, propExpr.getValue(), true));
} else if (
checkAndEat(TokenKind::period) ||
(optional &&
!(check(TokenKind::l_paren) ||
(context_.getParseFlow() && check(TokenKind::less))))) {
if (!check(TokenKind::identifier, TokenKind::private_identifier) &&
!tok_->isResWord()) {
// Just use the pattern here, even though we know it will fail.
if (!need(
TokenKind::identifier,
"after '.' or '?.' in member expression",
"start of member expression",
objectLoc))
return None;
}
ESTree::Node *id = nullptr;
if (check(TokenKind::private_identifier)) {
auto optId = parsePrivateName();
if (!optId)
return None;
id = *optId;
} else {
id = setLocation(
tok_,
tok_,
new (context_) ESTree::IdentifierNode(
tok_->getResWordOrIdentifier(), nullptr, false));
advance(JSLexer::AllowDiv);
}
if (optional || seenOptionalChain) {
return setLocation(
startLoc,
id,
puncLoc,
new (context_)
ESTree::OptionalMemberExpressionNode(expr, id, false, optional));
}
return setLocation(
startLoc,
id,
puncLoc,
new (context_) ESTree::MemberExpressionNode(expr, id, false));
} else {
assert(
optional &&
(check(TokenKind::l_paren) ||
(context_.getParseFlow() && check(TokenKind::less))) &&
"must be ?.() at this point");
// ?. Arguments :
// ?. ( ArgumentList )
// ^
auto debugLoc = tok_->getStartLoc();
ESTree::NodePtr typeArgs = nullptr;
#if HERMES_PARSE_FLOW
if (context_.getParseFlow() && check(TokenKind::less)) {
auto optTypeArgs = parseTypeArgsFlow();
if (!optTypeArgs) {
return None;
}
typeArgs = *optTypeArgs;
if (!need(
TokenKind::l_paren,
"after type arguments in optional call",
"start of optional call",
objectLoc))
return None;
}
#endif
#if HERMES_PARSE_TS
if (context_.getParseTS() && check(TokenKind::less)) {
auto optTypeArgs = parseTSTypeArguments();
if (!optTypeArgs) {
return None;
}
typeArgs = *optTypeArgs;
if (!need(
TokenKind::l_paren,
"after type arguments in optional call",
"start of optional call",
objectLoc))
return None;
}
#endif
ESTree::NodeList argList;
SMLoc endLoc;
if (!parseArguments(argList, endLoc))
return None;
return setLocation(
startLoc,
endLoc,
debugLoc,
new (context_) ESTree::OptionalCallExpressionNode(
expr, typeArgs, std::move(argList), true));
}
llvm_unreachable("Invalid token in parseMemberSelect");
}