Optional JSParserImpl::parseImportSpecifier()

in lib/Parser/JSParserImpl.cpp [6157:6339]


Optional<ESTree::ImportSpecifierNode *> JSParserImpl::parseImportSpecifier(
    SMLoc importLoc) {
  // ImportSpecifier:
  //   ImportedBinding
  //   IdentifierName as ImportedBinding
  SMLoc startLoc = tok_->getStartLoc();

  UniqueString *kind = valueIdent_;
  ESTree::IdentifierNode *imported = nullptr;
  ESTree::IdentifierNode *local = nullptr;
  TokenKind localKind;

#if HERMES_PARSE_FLOW
  if (context_.getParseFlow() && checkAndEat(TokenKind::rw_typeof)) {
    kind = typeofIdent_;
  }
#endif

  // This isn't wrapped in #if HERMES_PARSE_FLOW, as it is entangled
  // in the rest of the import specifier parsing code and doesn't actually
  // depend on JSParserImpl-flow specific code at all.
  if (HERMES_PARSE_FLOW && context_.getParseFlow() && check(typeIdent_) &&
      kind == valueIdent_) {
    // Consume 'type', but make no assumptions about what it means yet.
    SMRange typeRange = advance();
    if (check(TokenKind::r_brace, TokenKind::comma)) {
      // 'type'
      imported = setLocation(
          typeRange,
          typeRange,
          new (context_) ESTree::IdentifierNode(typeIdent_, nullptr, false));
      local = imported;
      localKind = TokenKind::identifier;
    } else if (check(asIdent_)) {
      SMRange asRange = advance();
      if (check(TokenKind::r_brace, TokenKind::comma)) {
        // 'type' 'as'
        kind = typeIdent_;
        imported = setLocation(
            asRange,
            asRange,
            new (context_) ESTree::IdentifierNode(asIdent_, nullptr, false));
        local = imported;
        localKind = TokenKind::identifier;
        advance();
      } else if (checkAndEat(asIdent_)) {
        // 'type' 'as' 'as' Identifier
        //                  ^
        if (!check(TokenKind::identifier) && !tok_->isResWord()) {
          errorExpected(
              TokenKind::identifier,
              "in import specifier",
              "specifiers start",
              importLoc);
          return None;
        }
        kind = typeIdent_;
        imported = setLocation(
            asRange,
            asRange,
            new (context_) ESTree::IdentifierNode(asIdent_, nullptr, false));
        local = setLocation(
            tok_,
            tok_,
            new (context_) ESTree::IdentifierNode(
                tok_->getResWordOrIdentifier(), nullptr, false));
        localKind = TokenKind::identifier;
        advance();
      } else {
        // 'type' 'as' Identifier
        //             ^
        if (!check(TokenKind::identifier) && !tok_->isResWord()) {
          errorExpected(
              TokenKind::identifier,
              "in import specifier",
              "specifiers start",
              importLoc);
          return None;
        }
        imported = setLocation(
            typeRange,
            typeRange,
            new (context_) ESTree::IdentifierNode(typeIdent_, nullptr, false));
        local = setLocation(
            tok_,
            tok_,
            new (context_) ESTree::IdentifierNode(
                tok_->getResWordOrIdentifier(), nullptr, false));
        localKind = TokenKind::identifier;
        advance();
      }
    } else {
      // 'type' Identifier
      //        ^
      kind = typeIdent_;
      if (!check(TokenKind::identifier) && !tok_->isResWord()) {
        errorExpected(
            TokenKind::identifier,
            "in import specifier",
            "specifiers start",
            importLoc);
        return None;
      }
      imported = setLocation(
          tok_,
          tok_,
          new (context_) ESTree::IdentifierNode(
              tok_->getResWordOrIdentifier(), nullptr, false));
      local = imported;
      localKind = tok_->getKind();
      advance();
      if (checkAndEat(asIdent_)) {
        // type Identifier 'as' Identifier
        //                      ^
        if (!check(TokenKind::identifier) && !tok_->isResWord()) {
          errorExpected(
              TokenKind::identifier,
              "in import specifier",
              "specifiers start",
              importLoc);
          return None;
        }
        local = setLocation(
            tok_,
            tok_,
            new (context_) ESTree::IdentifierNode(
                tok_->getResWordOrIdentifier(), nullptr, false));
        localKind = tok_->getKind();
        advance();
      }
    }
  } else {
    // Not attempting to parse a type identifier.
    if (!check(TokenKind::identifier) && !tok_->isResWord()) {
      errorExpected(
          TokenKind::identifier,
          "in import specifier",
          "specifiers start",
          importLoc);
      return None;
    }
    imported = setLocation(
        tok_,
        tok_,
        new (context_) ESTree::IdentifierNode(
            tok_->getResWordOrIdentifier(), nullptr, false));
    local = imported;
    localKind = tok_->getKind();
    advance();

    if (checkAndEat(asIdent_)) {
      if (!check(TokenKind::identifier) && !tok_->isResWord()) {
        errorExpected(
            TokenKind::identifier,
            "in import specifier",
            "specifiers start",
            importLoc);
        return None;
      }
      local = setLocation(
          tok_,
          tok_,
          new (context_) ESTree::IdentifierNode(
              tok_->getResWordOrIdentifier(), nullptr, false));
      localKind = tok_->getKind();
      advance();
    }
  }

  // Only the local name must be parsed as a binding identifier.
  // We need to check for 'as' before knowing what the local name is.
  // Thus, we need to validate the binding identifier for the local name
  // after the fact.
  if (!validateBindingIdentifier(
          Param{}, local->getSourceRange(), local->_name, localKind)) {
    error(local->getSourceRange(), "Invalid local name for import");
  }

  return setLocation(
      startLoc,
      getPrevTokenEndLoc(),
      new (context_) ESTree::ImportSpecifierNode(imported, local, kind));
}