Optional JSParserImpl::parseForStatement()

in lib/Parser/JSParserImpl.cpp [1672:1845]


Optional<ESTree::Node *> JSParserImpl::parseForStatement(Param param) {
  assert(check(TokenKind::rw_for));
  SMLoc startLoc = advance().Start;

  bool await = false;
  SMRange awaitRng;
  if (paramAwait_ && check(awaitIdent_)) {
    awaitRng = advance();
    await = true;
  }

  SMLoc lparenLoc = tok_->getStartLoc();
  if (!eat(
          TokenKind::l_paren,
          JSLexer::AllowRegExp,
          "after 'for'",
          "location of 'for'",
          startLoc))
    return None;

  ESTree::VariableDeclarationNode *decl = nullptr;
  ESTree::NodePtr expr1 = nullptr;

  if (checkN(TokenKind::rw_var, TokenKind::rw_const, letIdent_)) {
    // Productions valid here:
    //   for ( var/let/const VariableDeclarationList
    //   for [await] ( var/let/const VariableDeclaration
    SMLoc varStartLoc = tok_->getStartLoc();
    auto *declIdent = tok_->getResWordOrIdentifier();
    advance();

    ESTree::NodeList declList;
    if (!parseVariableDeclarationList(Param{}, declList, varStartLoc))
      return None;

    auto endLoc = declList.back().getEndLoc();
    decl = setLocation(
        varStartLoc,
        endLoc,
        new (context_)
            ESTree::VariableDeclarationNode(declIdent, std::move(declList)));
  } else {
    // Productions valid here:
    //   for [await] ( Expression_opt
    //   for [await] ( LeftHandSideExpression

    if (!check(TokenKind::semi)) {
      auto optExpr1 = parseExpression(Param{});
      if (!optExpr1)
        return None;
      expr1 = optExpr1.getValue();
    }
  }

  if (checkN(TokenKind::rw_in, ofIdent_)) {
    // Productions valid here:
    //   for [await] ( var/let/const VariableDeclaration[In] in/of
    //   for [await] ( LeftHandSideExpression in/of

    if (decl && decl->_declarations.size() > 1) {
      error(
          decl->getSourceRange(),
          "Only one binding must be declared in a for-in/for-of loop");
      return None;
    }

    // Check for destructuring pattern on the left and reparse it.
    if (expr1 &&
        (isa<ESTree::ArrayExpressionNode>(expr1) ||
         isa<ESTree::ObjectExpressionNode>(expr1))) {
      auto optExpr1 = reparseAssignmentPattern(expr1, false);
      if (!optExpr1)
        return None;
      expr1 = *optExpr1;
    }

    // Remember whether we are parsing for-in or for-of.
    bool const forInLoop = check(TokenKind::rw_in);
    advance();

    if (forInLoop && await)
      error(awaitRng, "unexpected 'await' in for..in loop");

    auto optRightExpr =
        forInLoop ? parseExpression() : parseAssignmentExpression(ParamIn);

    if (!eat(
            TokenKind::r_paren,
            JSLexer::AllowRegExp,
            "after 'for(... in/of ...'",
            "location of '('",
            lparenLoc))
      return None;

    auto optBody = parseStatement(param.get(ParamReturn));
    if (!optBody || !optRightExpr)
      return None;

    ESTree::Node *node;
    if (forInLoop) {
      node = new (context_) ESTree::ForInStatementNode(
          decl ? decl : expr1, optRightExpr.getValue(), optBody.getValue());
    } else {
      node = new (context_) ESTree::ForOfStatementNode(
          decl ? decl : expr1,
          optRightExpr.getValue(),
          optBody.getValue(),
          await);
    }
    return setLocation(startLoc, optBody.getValue(), node);
  } else if (checkAndEat(TokenKind::semi)) {
    // Productions valid here:
    //   for ( var/let/const VariableDeclarationList[In] ; Expressionopt ;
    //   Expressionopt )
    //       Statement
    //   for ( Expression[In]opt ; Expressionopt ; Expressionopt ) Statement

    if (await)
      error(awaitRng, "unexpected 'await' in for loop without 'of'");

    if (decl)
      ensureDestructuringInitialized(decl);

    ESTree::NodePtr test = nullptr;
    if (!check(TokenKind::semi)) {
      auto optTest = parseExpression();
      if (!optTest)
        return None;
      test = optTest.getValue();
    }

    if (!eat(
            TokenKind::semi,
            JSLexer::AllowRegExp,
            "after 'for( ... ; ...'",
            "location of '('",
            lparenLoc))
      return None;

    ESTree::NodePtr update = nullptr;
    if (!check(TokenKind::r_paren)) {
      auto optUpdate = parseExpression();
      if (!optUpdate)
        return None;
      update = optUpdate.getValue();
    }

    if (!eat(
            TokenKind::r_paren,
            JSLexer::AllowRegExp,
            "after 'for( ... ; ... ; ...'",
            "location of '('",
            lparenLoc))
      return None;

    auto optBody = parseStatement(param.get(ParamReturn));
    if (!optBody)
      return None;

    return setLocation(
        startLoc,
        optBody.getValue(),
        new (context_) ESTree::ForStatementNode(
            decl ? decl : expr1, test, update, optBody.getValue()));
  } else {
    errorExpected(
        TokenKind::semi,
        TokenKind::rw_in,
        "inside 'for'",
        "location of the 'for'",
        startLoc);
    return None;
  }
}