Optional JSParserImpl::parseTSFunctionOrParenthesizedType()

in lib/Parser/JSParserImpl-ts.cpp [227:371]


Optional<ESTree::Node *> JSParserImpl::parseTSFunctionOrParenthesizedType(
    SMLoc start,
    ESTree::Node *typeParams,
    IsConstructorType isConstructorType) {
  assert(check(TokenKind::l_paren));
  // This is either
  // ( Type )
  // ^
  // or
  // ( ParamList ) => Type
  // ^
  // so we use a similar approach to arrow function parameters by keeping track
  // and reparsing in certain cases.
  advance(JSLexer::GrammarContext::Type);

  bool isFunction = typeParams != nullptr;
  bool hasRest = false;
  ESTree::Node *type = nullptr;
  ESTree::NodeList params{};

  if (allowAnonFunctionType_ &&
      checkAndEat(TokenKind::dotdotdot, JSLexer::GrammarContext::Type)) {
    isFunction = true;
    hasRest = true;
    // Must be parameters, and this must be the last one.
    auto optName = parseTSFunctionTypeParam();
    if (!optName)
      return None;
    params.push_back(*setLocation(
        start,
        getPrevTokenEndLoc(),
        new (context_) ESTree::RestElementNode(*optName)));
  } else if (check(TokenKind::l_paren)) {
    auto optType = parseTypeAnnotationTS();
    if (!optType)
      return None;
    type = *optType;
  } else if (check(TokenKind::r_paren)) {
    isFunction = true;
    // ( )
    //   ^
    // No parameters, but this must be an empty param list.
  } else {
    // Not sure yet whether this is a param or simply a type.
    auto optParam = parseTSFunctionTypeParam();
    if (!optParam)
      return None;
    if (auto *param =
            llvh::dyn_cast<ESTree::TSParameterPropertyNode>(*optParam)) {
      if (param &&
          (param->_accessibility || param->_export || param->_readonly ||
           param->_static)) {
        // Must be a param.
        isFunction = true;
      }
      params.push_back(*param);
    } else if (
        auto *ident = llvh::dyn_cast<ESTree::IdentifierNode>(*optParam)) {
      params.push_back(*ident);
      type = ident->_typeAnnotation
          ? ident->_typeAnnotation
          : reparseIdentifierAsTSTypeAnnotation(ident);
      if (ident->_typeAnnotation || ident->_optional) {
        // Must be a param.
        isFunction = true;
      }
    } else {
      type = *optParam;
      params.push_back(**optParam);
    }
  }

  // If isFunction was already forced by something previously then we
  // have no choice but to attempt to parse as a function type annotation.
  if ((isFunction || allowAnonFunctionType_) &&
      checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) {
    isFunction = true;
    while (!check(TokenKind::r_paren)) {
      bool isRest = !hasRest &&
          checkAndEat(TokenKind::dotdotdot, JSLexer::GrammarContext::Type);

      auto optParam = parseTSFunctionTypeParam();
      if (!optParam)
        return None;
      if (isRest) {
        params.push_back(*setLocation(
            start,
            getPrevTokenEndLoc(),
            new (context_) ESTree::RestElementNode(*optParam)));
        checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type);
        break;
      } else {
        params.push_back(**optParam);
      }

      if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type))
        break;
    }
  }

  if (!eat(
          TokenKind::r_paren,
          JSLexer::GrammarContext::Type,
          "at end of function type parameters",
          "start of parameters",
          start))
    return None;

  if (isFunction) {
    if (!eat(
            TokenKind::equalgreater,
            JSLexer::GrammarContext::Type,
            "in function type",
            "start of function",
            start))
      return None;
  } else if (allowAnonFunctionType_) {
    if (checkAndEat(TokenKind::equalgreater, JSLexer::GrammarContext::Type)) {
      isFunction = true;
    }
  }

  if (!isFunction) {
    type->incParens();
    return type;
  }

  auto optReturnType = parseTypeAnnotationTS();
  if (!optReturnType)
    return None;

  if (isConstructorType == IsConstructorType::Yes) {
    return setLocation(
        start,
        getPrevTokenEndLoc(),
        new (context_) ESTree::TSConstructorTypeNode(
            std::move(params), *optReturnType, typeParams));
  }

  return setLocation(
      start,
      getPrevTokenEndLoc(),
      new (context_) ESTree::TSFunctionTypeNode(
          std::move(params), *optReturnType, typeParams));
}