//===--- ParsePattern.cpp - Swift Language Parser for Patterns ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Pattern Parsing and AST Building
//
//===----------------------------------------------------------------------===//

#include "swift/Parse/Parser.h"

#include "swift/AST/ASTWalker.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/GenericParamList.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/Module.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeRepr.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/StringExtras.h"
#include "swift/Parse/IDEInspectionCallbacks.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/SaveAndRestore.h"

using namespace swift;

static ParserStatus
parseDefaultArgument(Parser &P, Parser::DefaultArgumentInfo *defaultArgs,
                     unsigned argIndex, Expr *&init,
                     DefaultArgumentInitializer *&initContext,
                     Parser::ParameterContextKind paramContext) {
  assert(P.Tok.is(tok::equal) ||
       (P.Tok.isBinaryOperator() && P.Tok.getText() == "=="));
  SourceLoc equalLoc = P.consumeToken();

  // Enter a fresh default-argument context with a meaningless parent.
  // We'll change the parent to the function later after we've created
  // that declaration.
  initContext = DefaultArgumentInitializer::create(P.CurDeclContext, argIndex);
  Parser::ParseFunctionBody initScope(P, initContext);

  ParserResult<Expr> initR = P.parseExpr(diag::expected_init_value);

  Diag<> diagID = { DiagID() };
  switch (paramContext) {
  case Parser::ParameterContextKind::Function:
  case Parser::ParameterContextKind::Operator:
  case Parser::ParameterContextKind::Initializer:
  case Parser::ParameterContextKind::EnumElement:
  case Parser::ParameterContextKind::Subscript:
  case Parser::ParameterContextKind::Macro:
    break;
  case Parser::ParameterContextKind::Closure:
    diagID = diag::no_default_arg_closure;
    break;
  case Parser::ParameterContextKind::Curried:
    diagID = diag::no_default_arg_curried;
    break;
  }
  
  assert((diagID.ID != DiagID()) == !defaultArgs &&
         "Default arguments specified for an unexpected parameter list kind");
  
  if (diagID.ID != DiagID()) {
    auto inFlight = P.diagnose(equalLoc, diagID);
    if (initR.isNonNull())
      inFlight.fixItRemove(SourceRange(equalLoc, initR.get()->getEndLoc()));
    return ParserStatus();
  }
  
  defaultArgs->HasDefaultArgument = true;

  if (initR.hasCodeCompletion()) {
    init = initR.get();
    return makeParserCodeCompletionStatus();
  }

  if (initR.isNull())
    return makeParserError();

  init = initR.get();
  return ParserStatus();
}

/// Determine whether we are at the start of a parameter name when
/// parsing a parameter.
bool Parser::startsParameterName(bool isClosure) {
  // To have a parameter name here, we need a name.
  if (!Tok.canBeArgumentLabel())
    return false;

  // If the next token is ':', this is a name.
  const auto &nextTok = peekToken();
  if (nextTok.is(tok::colon))
    return true;



  // If the next token can be an argument label, we might have a name.
  if (nextTok.canBeArgumentLabel()) {
    // If the first name wasn't a contextual keyword, we're done.
    if (!Tok.isContextualKeyword("isolated") &&
        !Tok.isContextualKeyword("some") && !Tok.isContextualKeyword("any") &&
        !Tok.isContextualKeyword("each") &&
        !Tok.isContextualKeyword("__shared") &&
        !Tok.isContextualKeyword("__owned") &&
        !Tok.isContextualKeyword("borrowing") &&
        (!Context.LangOpts.hasFeature(Feature::SendingArgsAndResults) ||
         !Tok.isContextualKeyword("sending")) &&
        !Tok.isContextualKeyword("consuming") && !Tok.is(tok::kw_repeat))
      return true;

    // Parameter specifiers can be an argument label, but they're also
    // contextual keywords, so look ahead one more token (two total) and see
    // if we have a ':' that would
    // indicate that this is an argument label.
    return lookahead<bool>(2, [&](CancellableBacktrackingScope &) {
      if (Tok.is(tok::colon))
        return true; // isolated :

      return Tok.canBeArgumentLabel() && nextTok.is(tok::colon);
    });
  }

  if (isOptionalToken(nextTok)
      || isImplicitlyUnwrappedOptionalToken(nextTok))
    return false;

  // The identifier could be a name or it could be a type. In a closure, we
  // assume it's a name, because the type can be inferred. Elsewhere, we
  // assume it's a type.
  return isClosure;
}

SourceLoc Parser::tryCompleteFunctionParamTypeBeginning() {
  if (!L->isCodeCompletion())
    return SourceLoc();

  // Skip over any starting parameter specifiers.
  {
    CancellableBacktrackingScope backtrack(*this);
    ParsedTypeAttributeList attrs(ParseTypeReason::Unspecified);
    attrs.parse(*this);
    if (!Tok.is(tok::code_complete))
      return SourceLoc();

    backtrack.cancelBacktrack();
  }

  if (CodeCompletionCallbacks)
    CodeCompletionCallbacks->completeTypePossibleFunctionParamBeginning();

  return consumeToken(tok::code_complete);
}

ParserStatus
Parser::parseParameterClause(SourceLoc &leftParenLoc,
                             SmallVectorImpl<ParsedParameter> &params,
                             SourceLoc &rightParenLoc,
                             DefaultArgumentInfo *defaultArgs,
                             ParameterContextKind paramContext) {
  assert(params.empty() && leftParenLoc.isInvalid() &&
         rightParenLoc.isInvalid() && "Must start with empty state");

  // Consume the starting '(';
  leftParenLoc = consumeToken(tok::l_paren);

  // Trivial case: empty parameter list.
  if (Tok.is(tok::r_paren)) {
    rightParenLoc = consumeToken(tok::r_paren);

    // Per SE-0155, enum elements may not have empty parameter lists.
    if (paramContext == ParameterContextKind::EnumElement) {
      decltype(diag::enum_element_empty_arglist) diagnostic;
      if (Context.isSwiftVersionAtLeast(5)) {
        diagnostic = diag::enum_element_empty_arglist;
      } else {
        diagnostic = diag::enum_element_empty_arglist_swift4;
      }

      diagnose(leftParenLoc, diagnostic)
        .highlight({leftParenLoc, rightParenLoc});
      diagnose(leftParenLoc, diag::enum_element_empty_arglist_delete)
        .fixItRemoveChars(leftParenLoc,
                          Lexer::getLocForEndOfToken(SourceMgr, rightParenLoc));
      diagnose(leftParenLoc, diag::enum_element_empty_arglist_add_void)
        .fixItInsertAfter(leftParenLoc, "Void");
    }
    return ParserStatus();
  }

  // Parse the parameter list.
  bool isClosure = paramContext == ParameterContextKind::Closure;
  return parseList(tok::r_paren, leftParenLoc, rightParenLoc,
                      /*AllowSepAfterLast=*/true,
                      diag::expected_rparen_parameter,
                      [&]() -> ParserStatus {
    ParsedParameter param;
    ParserStatus status;
    SourceLoc StartLoc = Tok.getLoc();

    unsigned defaultArgIndex = defaultArgs ? defaultArgs->NextIndex++ : 0;

    // Attributes.
    if (paramContext != ParameterContextKind::EnumElement) {
      auto AttrStatus = parseDeclAttributeList(param.Attrs);
      if (AttrStatus.hasCodeCompletion()) {
        if (this->CodeCompletionCallbacks)
          this->CodeCompletionCallbacks->setAttrTargetDeclKind(DeclKind::Param);
        status.setHasCodeCompletionAndIsError();
      }
    }

    {
      // ('inout' | '__shared' | '__owned' | isolated)?
      bool hasSpecifier = false;
      while (isParameterSpecifier()) {
        // Placing 'inout' in front of the parameter specifiers was allowed in
        // the Swift 2-ish era and got moved to the return type in Swift 3
        // (SE-0031).
        // But new parameters that don't store there location in
        // `SpecifierLoc` were added afterwards and didn't get diagnosed.
        // We thus need to parameter specifiers that don't store their location
        // in `SpecifierLoc` here. `SpecifierLoc` parameters get diagnosed in
        // `validateParameterWithOwnership`

        // is this token the identifier of an argument label? `inout` is a
        // reserved keyword but the other modifiers are not.
        if (!Tok.is(tok::kw_inout)) {
          bool partOfArgumentLabel = lookahead<bool>(1, [&](CancellableBacktrackingScope &) {
            if (Tok.is(tok::colon))
              return true;  // isolated :

            return Tok.canBeArgumentLabel() && peekToken().is(tok::colon);
          });

          if (partOfArgumentLabel)
            break;
        }
        
        if (Tok.isContextualKeyword("isolated")) {
          diagnose(Tok, diag::parameter_specifier_as_attr_disallowed, Tok.getText())
                    .warnUntilSwiftVersion(6);
          // did we already find an 'isolated' type modifier?
          if (param.IsolatedLoc.isValid()) {
            diagnose(Tok, diag::parameter_specifier_repeated)
              .fixItRemove(Tok.getLoc());
            consumeToken();
            continue;
          }

          // consume 'isolated' as type modifier
          param.IsolatedLoc = consumeToken();
          continue;
        }

        if (Tok.isContextualKeyword("_const")) {
          diagnose(Tok, diag::parameter_specifier_as_attr_disallowed, Tok.getText())
                    .warnUntilSwiftVersion(6);
          param.CompileConstLoc = consumeToken();
          continue;
        }

        if (Context.LangOpts.hasFeature(Feature::SendingArgsAndResults) &&
            Tok.isContextualKeyword("sending")) {
          diagnose(Tok, diag::parameter_specifier_as_attr_disallowed,
                   Tok.getText())
              .warnUntilSwiftVersion(6);
          if (param.SendingLoc.isValid()) {
            diagnose(Tok, diag::parameter_specifier_repeated)
                .fixItRemove(Tok.getLoc());
            consumeToken();
            continue;
          }

          param.SendingLoc = consumeToken();
          continue;
        }

        if (!hasSpecifier) {
          // These cases are handled later when mapping to ParamDecls for
          // better fixits.
          if (Tok.is(tok::kw_inout)) {
            param.SpecifierKind = ParamDecl::Specifier::InOut;
            param.SpecifierLoc = consumeToken();
          } else if (Tok.isContextualKeyword("borrowing")) {
            param.SpecifierKind = ParamDecl::Specifier::Borrowing;
            param.SpecifierLoc = consumeToken();
          } else if (Tok.isContextualKeyword("consuming")) {
            param.SpecifierKind = ParamDecl::Specifier::Consuming;
            param.SpecifierLoc = consumeToken();
          } else if (Tok.isContextualKeyword("__shared")) {
            param.SpecifierKind = ParamDecl::Specifier::LegacyShared;
            param.SpecifierLoc = consumeToken();
          } else if (Tok.isContextualKeyword("__owned")) {
            param.SpecifierKind = ParamDecl::Specifier::LegacyOwned;
            param.SpecifierLoc = consumeToken();
          }

          if (param.SendingLoc.isValid()) {
            diagnose(Tok, diag::sending_before_parameter_specifier,
                     getNameForParamSpecifier(param.SpecifierKind));
          }

          hasSpecifier = true;
        } else {
          // Redundant specifiers are fairly common, recognize, reject, and
          // recover from this gracefully.
          diagnose(Tok, diag::parameter_specifier_repeated)
            .fixItRemove(Tok.getLoc());
          consumeToken();
        }
      }
    }
    
    // If let or var is being used as an argument label, allow it but
    // generate a warning.
    if (!isClosure &&
        (Tok.isAny(tok::kw_let, tok::kw_var) ||
         (Context.LangOpts.hasFeature(Feature::ReferenceBindings) &&
          Tok.isAny(tok::kw_inout)))) {
      diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
        .fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
    }

    auto parseParamType = [&]() -> ParserResult<TypeRepr> {
      // Currently none of the parameter type completions are relevant for
      // enum cases, so don't include them. We'll complete for a regular type
      // beginning instead.
      if (paramContext != ParameterContextKind::EnumElement) {
        if (auto CCLoc = tryCompleteFunctionParamTypeBeginning()) {
          auto *ET = ErrorTypeRepr::create(Context, CCLoc);
          return makeParserCodeCompletionResult<TypeRepr>(ET);
        }
      }
      return parseType(diag::expected_parameter_type);
    };

    if (startsParameterName(isClosure)) {
      // identifier-or-none for the first name
      param.FirstNameLoc = consumeArgumentLabel(param.FirstName,
                                                /*diagnoseDollarPrefix=*/!isClosure);

      // identifier-or-none? for the second name
      if (Tok.canBeArgumentLabel())
        param.SecondNameLoc = consumeArgumentLabel(param.SecondName,
                                                   /*diagnoseDollarPrefix=*/true);

      // Operators, closures, and enum elements cannot have API names.
      if ((paramContext == ParameterContextKind::Operator ||
           paramContext == ParameterContextKind::Closure ||
           paramContext == ParameterContextKind::EnumElement) &&
          !param.FirstName.empty() &&
          param.SecondNameLoc.isValid()) {
        enum KeywordArgumentDiagnosticContextKind {
          Operator    = 0,
          Closure     = 1,
          EnumElement = 2,
        } diagContextKind;

        switch (paramContext) {
        case ParameterContextKind::Operator:
          diagContextKind = Operator;
          break;
        case ParameterContextKind::Closure:
          diagContextKind = Closure;
          break;
        case ParameterContextKind::EnumElement:
          diagContextKind = EnumElement;
          break;
        default:
          llvm_unreachable("Unhandled parameter context kind!");
        }
        diagnose(param.FirstNameLoc, diag::parameter_operator_keyword_argument,
                 unsigned(diagContextKind))
          .fixItRemoveChars(param.FirstNameLoc, param.SecondNameLoc);
        param.FirstName = param.SecondName;
        param.FirstNameLoc = param.SecondNameLoc;
        param.SecondName = Identifier();
        param.SecondNameLoc = SourceLoc();
      }

      // (':' type)?
      if (consumeIf(tok::colon)) {

        auto type = parseParamType();
        status |= type;
        param.Type = type.getPtrOrNull();

        // If we didn't parse a type, then we already diagnosed that the type
        // was invalid.  Remember that.
        if (type.isNull() && !type.hasCodeCompletion())
          param.isInvalid = true;
      } else if (paramContext != Parser::ParameterContextKind::Closure) {
        diagnose(Tok, diag::expected_parameter_colon);
        param.isInvalid = true;
      }
    } else {
      // Otherwise, we have invalid code.  Check to see if this looks like a
      // type.  If so, diagnose it as a common error.
      bool isBareType = false;
      {
        BacktrackingScope backtrack(*this);
        isBareType = canParseType() && Tok.isAny(tok::comma, tok::r_paren,
                                                 tok::equal);
      }

      if (isBareType && paramContext == ParameterContextKind::EnumElement) {
        auto type = parseParamType();
        status |= type;
        param.Type = type.getPtrOrNull();
        param.FirstName = Identifier();
        param.FirstNameLoc = SourceLoc();
        param.SecondName = Identifier();
        param.SecondNameLoc = SourceLoc();
      } else if (isBareType && !Tok.is(tok::code_complete)) {
        // Otherwise, if this is a bare type, then the user forgot to name the
        // parameter, e.g. "func foo(Int) {}"
        // Don't enter this case if the element could only be parsed as a bare
        // type because a code completion token is positioned here. In this case
        // the user is about to type the parameter label and we shouldn't
        // suggest types.
        SourceLoc typeStartLoc = Tok.getLoc();
        auto type = parseParamType();
        status |= type;
        param.Type = type.getPtrOrNull();

        // If this is a closure declaration, what is going
        // on is most likely argument destructuring, we are going
        // to diagnose that after all of the parameters are parsed.
        if (param.Type) {
          // Mark current parameter type as invalid so it is possible
          // to diagnose it as destructuring of the closure parameter list.
          param.isPotentiallyDestructured = true;
          if (!isClosure) {
            // Unnamed parameters must be written as "_: Type".
            diagnose(typeStartLoc, diag::parameter_unnamed)
                .fixItInsert(typeStartLoc, "_: ");
          } else {
            // Unnamed parameters were accidentally possibly accepted after
            // SE-110 depending on the kind of declaration.  We now need to
            // warn about the misuse of this syntax and offer to
            // fix it.
            // An exception to this rule is when the type is declared with type sugar
            // Reference: https://github.com/apple/swift/issues/54133
            if (isa<OptionalTypeRepr>(param.Type)
                || isa<ImplicitlyUnwrappedOptionalTypeRepr>(param.Type)) {
                diagnose(typeStartLoc, diag::parameter_unnamed)
                    .fixItInsert(typeStartLoc, "_: ");
            } else {
                diagnose(typeStartLoc, diag::parameter_unnamed)
                    .warnUntilSwiftVersion(6)
                    .fixItInsert(typeStartLoc, "_: ");
            }
          }
        }
      } else {
        // Otherwise, we're not sure what is going on, but this doesn't smell
        // like a parameter.
        diagnose(Tok, diag::expected_parameter_name);
        param.isInvalid = true;
        param.FirstNameLoc = Tok.getLoc();
        TokReceiver->registerTokenKindChange(param.FirstNameLoc,
                                             tok::identifier);
        status.setIsParseError();
      }
    }

    // If this parameter had an ellipsis, check it has a TypeRepr.
    if (Tok.isEllipsis()) {
      if (param.Type == nullptr && !param.isInvalid) {
        diagnose(Tok, diag::untyped_pattern_ellipsis);
        consumeToken();
      }
    }

    // ('=' expr) or ('==' expr)?
    bool isEqualBinaryOperator =
        Tok.isBinaryOperator() && Tok.getText() == "==";
    if (Tok.is(tok::equal) || isEqualBinaryOperator) {
      SourceLoc EqualLoc = Tok.getLoc();

      if (isEqualBinaryOperator) {
        diagnose(Tok, diag::expected_assignment_instead_of_comparison_operator)
            .fixItReplace(EqualLoc, "=");
      }

      status |= parseDefaultArgument(*this, defaultArgs, defaultArgIndex,
                                     param.DefaultArg,
                                     param.DefaultArgInitContext, paramContext);
    }

    // If we haven't made progress, don't add the parameter.
    if (Tok.getLoc() == StartLoc) {
      // If we took a default argument index for this parameter, but didn't add
      // one, then give it back.
      if (defaultArgs) --defaultArgs->NextIndex;
      return status;
    }

    params.push_back(param);
    return status;
  });
}

static TypeRepr *
validateParameterWithOwnership(Parser &parser,
                               Parser::ParsedParameter &paramInfo,
                               ParamSpecifier specifier,
                               bool parsingEnumElt) {
  auto type = paramInfo.Type;
  auto loc = paramInfo.SpecifierLoc;
  // If we're validating an enum element, 'inout' is not allowed
  // at all - Sema will catch this for us.  In all other contexts, we
  // assume the user put 'inout' in the wrong place and offer a fixit.
  if (parsingEnumElt) {
    return new (parser.Context) OwnershipTypeRepr(type, specifier, loc);
  }
  
  if (isa<SpecifierTypeRepr>(type)) {
    parser.diagnose(loc, diag::parameter_specifier_repeated).fixItRemove(loc);
  } else {
    auto specifierName = ParamDecl::getSpecifierSpelling(specifier);
    llvm::SmallString<128> replacement(specifierName);
    replacement += " ";
    parser
        .diagnose(loc, diag::parameter_specifier_as_attr_disallowed,
                  specifierName)
        .fixItRemove(loc)
        .fixItInsert(type->getStartLoc(), replacement);
    type = new (parser.Context) OwnershipTypeRepr(type, specifier, loc);
  }

  return type;
}

/// Map parsed parameters to a ParameterList.
static ParameterList *
mapParsedParameters(Parser &parser,
                    SourceLoc leftParenLoc,
                    MutableArrayRef<Parser::ParsedParameter> params,
                    SourceLoc rightParenLoc,
                    SmallVectorImpl<Identifier> *argNames,
                    Parser::ParameterContextKind paramContext) {
  auto &ctx = parser.Context;

  // Local function to create a pattern for a single parameter.
  auto createParam = [&](Parser::ParsedParameter &paramInfo, Identifier argName,
                         SourceLoc argNameLoc, Identifier paramName,
                         SourceLoc paramNameLoc) -> ParamDecl * {
    auto param = ParamDecl::createParsed(
        ctx, paramInfo.SpecifierLoc, argNameLoc, argName, paramNameLoc,
        paramName, paramInfo.DefaultArg, paramInfo.DefaultArgInitContext,
        parser.CurDeclContext);
    param->attachParsedAttrs(paramInfo.Attrs);

    bool parsingEnumElt
      = (paramContext == Parser::ParameterContextKind::EnumElement);
    // If we're not parsing an enum case, lack of a SourceLoc for both
    // names indicates the parameter is synthetic.
    if (!parsingEnumElt && argNameLoc.isInvalid() && paramNameLoc.isInvalid())
      param->setImplicit();
    
    // If we diagnosed this parameter as a parse error, propagate to the decl.
    if (paramInfo.isInvalid)
      param->setInvalid();

    // If we need to diagnose this parameter as a destructuring, propagate that
    // to the decl.
    // FIXME: This is a terrible way to catch this.
    if (paramInfo.isPotentiallyDestructured)
      param->setDestructured(true);

    // If a type was provided, create the type for the parameter.
    if (auto type = paramInfo.Type) {
      // If 'inout' was specified, turn the type into an in-out type.
      if (paramInfo.SpecifierKind != ParamDecl::Specifier::Default) {
        type = validateParameterWithOwnership(parser, paramInfo,
                                              paramInfo.SpecifierKind,
                                              parsingEnumElt);
      }

      if (paramInfo.IsolatedLoc.isValid()) {
        type = new (parser.Context) IsolatedTypeRepr(
            type, paramInfo.IsolatedLoc);
        param->setIsolated();
      }

      if (paramInfo.CompileConstLoc.isValid()) {
        type = new (parser.Context) CompileTimeLiteralTypeRepr(
            type, paramInfo.CompileConstLoc);
        param->setCompileTimeLiteral();
      }

      if (paramInfo.Attrs.hasAttribute<ConstValAttr>()) {
        type = new (parser.Context) ConstValueTypeRepr(
            type, paramInfo.Attrs.getAttribute<ConstValAttr>()->AtLoc);
        param->setConstValue();
      }

      if (paramInfo.SendingLoc.isValid()) {
        type = new (parser.Context) SendingTypeRepr(type, paramInfo.SendingLoc);
        param->setSending();
      }

      param->setTypeRepr(type);

    } else if (paramInfo.SpecifierLoc.isValid()) {
      llvm::SmallString<16> specifier;
      {
        llvm::raw_svector_ostream ss(specifier);
        
        ss << '\'' << ParamDecl::getSpecifierSpelling(paramInfo.SpecifierKind)
           << '\'';
      }
      parser.diagnose(paramInfo.SpecifierLoc, diag::specifier_must_have_type,
                      specifier);
      paramInfo.SpecifierLoc = SourceLoc();
      paramInfo.SpecifierKind = ParamDecl::Specifier::Default;
    }

    return param;
  };

  // Collect the elements of the tuple patterns for argument and body
  // parameters.
  SmallVector<ParamDecl*, 4> elements;

  for (auto &param : params) {
    // Whether the provided name is API by default depends on the parameter
    // context.
    bool isKeywordArgumentByDefault;
    switch (paramContext) {
    case Parser::ParameterContextKind::Closure:
    case Parser::ParameterContextKind::Subscript:
    case Parser::ParameterContextKind::Operator:
      isKeywordArgumentByDefault = false;
      break;
    case Parser::ParameterContextKind::EnumElement:
    case Parser::ParameterContextKind::Curried:
    case Parser::ParameterContextKind::Initializer:
    case Parser::ParameterContextKind::Function:
    case Parser::ParameterContextKind::Macro:
      isKeywordArgumentByDefault = true;
      break;
    }

    // Create the pattern.
    ParamDecl *result = nullptr;
    Identifier argName;
    Identifier paramName;
    if (param.SecondNameLoc.isValid()) {
      argName = param.FirstName;
      paramName = param.SecondName;

      // Both names were provided, so pass them in directly.
      result = createParam(param, argName, param.FirstNameLoc,
                           paramName, param.SecondNameLoc);

      // If the first and second names are equivalent and non-empty, and we
      // would have an argument label by default, complain.
      if (isKeywordArgumentByDefault && param.FirstName == param.SecondName
          && !param.FirstName.empty()) {
        parser.diagnose(param.FirstNameLoc,
                        diag::parameter_extraneous_double_up,
                        param.FirstName)
          .fixItRemoveChars(param.FirstNameLoc, param.SecondNameLoc);
      }
    } else {
      if (isKeywordArgumentByDefault)
        argName = param.FirstName;
      paramName = param.FirstName;

      result = createParam(param, argName, SourceLoc(),
                           param.FirstName, param.FirstNameLoc);
    }

    assert ((!param.DefaultArg ||
             paramContext == Parser::ParameterContextKind::Function ||
             paramContext == Parser::ParameterContextKind::Operator ||
             paramContext == Parser::ParameterContextKind::Initializer ||
             paramContext == Parser::ParameterContextKind::EnumElement ||
             paramContext == Parser::ParameterContextKind::Subscript ||
             paramContext == Parser::ParameterContextKind::Macro) &&
            "Default arguments are only permitted on the first param clause");

    elements.push_back(result);

    if (argNames)
      argNames->push_back(argName);
  }

  return ParameterList::create(ctx, leftParenLoc, elements, rightParenLoc);
}

/// Parse a single parameter-clause.
ParserResult<ParameterList>
Parser::parseSingleParameterClause(ParameterContextKind paramContext,
                                   SmallVectorImpl<Identifier> *namePieces,
                                   DefaultArgumentInfo *defaultArgs) {
  if (!Tok.is(tok::l_paren)) {
    // If we don't have the leading '(', complain.
    Diag<> diagID;
    switch (paramContext) {
    case ParameterContextKind::Function:
    case ParameterContextKind::Operator:
      diagID = diag::func_decl_without_paren;
      break;
    case ParameterContextKind::Subscript:
      diagID = diag::expected_lparen_subscript;
      break;
    case ParameterContextKind::Initializer:
      diagID = diag::expected_lparen_initializer;
      break;
    case ParameterContextKind::Macro:
      diagID = diag::expected_lparen_macro;
      break;
    case ParameterContextKind::EnumElement:
    case ParameterContextKind::Closure:
    case ParameterContextKind::Curried:
      llvm_unreachable("should never be here");
    }

    {
      auto diag = diagnose(Tok, diagID);
      if (Tok.isAny(tok::l_brace, tok::arrow, tok::kw_throws, tok::kw_rethrows))
        diag.fixItInsertAfter(PreviousLoc, "()");
    }

    // Create an empty parameter list to recover.
    return makeParserErrorResult(
        ParameterList::createEmpty(Context, PreviousLoc, PreviousLoc));
  }

  ParserStatus status;
  SmallVector<ParsedParameter, 4> params;
  SourceLoc leftParenLoc, rightParenLoc;
  
  // Parse the parameter clause.
  status |= parseParameterClause(leftParenLoc, params, rightParenLoc,
                                 defaultArgs, paramContext);

  // Turn the parameter clause into argument and body patterns.
  auto paramList = mapParsedParameters(*this, leftParenLoc, params,
                                       rightParenLoc, namePieces, paramContext);

  return makeParserResult(status, paramList);
}

/// Parse function arguments.
///
/// Emits a special diagnostic if there are multiple parameter lists,
/// but otherwise is identical to parseSingleParameterClause().
ParserStatus
Parser::parseFunctionArguments(SmallVectorImpl<Identifier> &NamePieces,
                               ParameterList *&BodyParams,
                               ParameterContextKind paramContext,
                               DefaultArgumentInfo &DefaultArgs) {
  // Parse parameter-clauses.
  ParserStatus status;

  auto FirstParameterClause
    = parseSingleParameterClause(paramContext, &NamePieces, &DefaultArgs);
  status |= FirstParameterClause;
  BodyParams = FirstParameterClause.get();

  bool MultipleParameterLists = false;
  while (Tok.is(tok::l_paren)) {
    MultipleParameterLists = true;
    auto CurriedParameterClause
      = parseSingleParameterClause(ParameterContextKind::Curried);
    status |= CurriedParameterClause;
  }

  // If the decl uses currying syntax, complain that syntax has gone away.
  if (MultipleParameterLists) {
    diagnose(BodyParams->getStartLoc(),
             diag::parameter_curry_syntax_removed);
  }

  return status;
}

/// Parse a function definition signature.
///   func-signature:
///     func-arguments ('async'|'reasync')? func-throws? func-signature-result?
///   func-signature-result:
///     '->' type
///
/// Note that this leaves retType as null if unspecified.
ParserStatus
Parser::parseFunctionSignature(DeclBaseName SimpleName,
                               DeclName &FullName,
                               ParameterList *&bodyParams,
                               DefaultArgumentInfo &defaultArgs,
                               SourceLoc &asyncLoc,
                               bool &reasync,
                               SourceLoc &throwsLoc,
                               bool &rethrows,
                               TypeRepr *&thrownType,
                               TypeRepr *&retType) {
  SmallVector<Identifier, 4> NamePieces;
  ParserStatus Status;

  ParameterContextKind paramContext =
      SimpleName.isOperator()
          ? ParameterContextKind::Operator
          : (SimpleName.isConstructor() ? ParameterContextKind::Initializer
                                        : ParameterContextKind::Function);
  Status |= parseFunctionArguments(NamePieces, bodyParams, paramContext,
                                   defaultArgs);
  FullName = DeclName(Context, SimpleName, NamePieces);

  // Check for the 'async' and 'throws' keywords.
  reasync = false;
  rethrows = false;
  thrownType = nullptr;
  Status |= parseEffectsSpecifiers(SourceLoc(),
                                   asyncLoc, &reasync,
                                   throwsLoc, &rethrows, thrownType);

  // If there's a trailing arrow, parse the rest as the result type.
  SourceLoc arrowLoc;
  if (Tok.isAny(tok::arrow, tok::colon)) {
    if (!consumeIf(tok::arrow, arrowLoc)) {
      // FixIt ':' to '->'.
      diagnose(Tok, diag::func_decl_expected_arrow)
          .fixItReplace(Tok.getLoc(), " -> ");
      arrowLoc = consumeToken(tok::colon);
    }

    // Check for effect specifiers after the arrow, but before the return type,
    // and correct it.
    parseEffectsSpecifiers(arrowLoc, asyncLoc, &reasync, throwsLoc, &rethrows,
                           thrownType);

    ParserResult<TypeRepr> ResultType =
        parseDeclResultType(diag::expected_type_function_result);
    retType = ResultType.getPtrOrNull();
    Status |= ResultType;
    if (Status.isErrorOrHasCompletion())
      return Status;

    // Check for effect specifiers after the type and correct it.
    parseEffectsSpecifiers(
        arrowLoc, asyncLoc, &reasync, throwsLoc, &rethrows, thrownType);
  } else {
    // Otherwise, we leave retType null.
    retType = nullptr;
  }

  return Status;
}

bool Parser::isThrowsEffectSpecifier(const Token &T) {
  return T.isAny(tok::kw_throws, tok::kw_rethrows) ||
    (T.isAny(tok::kw_throw, tok::kw_try) && !T.isAtStartOfLine());
}

bool Parser::isEffectsSpecifier(const Token &T) {
  // NOTE: If this returns 'true', that token must be handled in
  //       'parseEffectsSpecifiers()'.

  if (T.isContextualKeyword("async") ||
      (T.isContextualKeyword("await") && !T.isAtStartOfLine()) ||
      T.isContextualKeyword("reasync"))
    return true;

  if (isThrowsEffectSpecifier(T))
    return true;

  return false;
}

ParserStatus Parser::parseEffectsSpecifiers(SourceLoc existingArrowLoc,
                                            SourceLoc &asyncLoc,
                                            bool *reasync,
                                            SourceLoc &throwsLoc,
                                            bool *rethrows,
                                            TypeRepr *&thrownType) {
  ParserStatus status;
  while (true) {
    // 'async'
    bool isReasync = (shouldParseExperimentalConcurrency() &&
                      Tok.isContextualKeyword("reasync"));
    if (Tok.isContextualKeyword("async") ||
        isReasync) {
      if (asyncLoc.isValid()) {
        diagnose(Tok, diag::duplicate_effects_specifier, Tok.getText())
            .highlight(asyncLoc)
            .fixItRemove(Tok.getLoc());
      } else if (!reasync && isReasync) {
        // Replace 'reasync' with 'async' unless it's allowed.
        diagnose(Tok, diag::reasync_function_type)
            .fixItReplace(Tok.getLoc(), "async");
      } else if (existingArrowLoc.isValid()) {
        SourceLoc insertLoc = existingArrowLoc;
        if (throwsLoc.isValid() &&
            SourceMgr.isBeforeInBuffer(throwsLoc, insertLoc))
          insertLoc = throwsLoc;
        diagnose(Tok, diag::async_or_throws_in_wrong_position,
                 (reasync && isReasync) ? "reasync" : "async")
            .fixItRemove(Tok.getLoc())
            .fixItInsert(insertLoc,
                         (reasync && isReasync) ? "reasync " : "async ");
      } else if (throwsLoc.isValid()) {
        // 'async' cannot be after 'throws'.
        assert(existingArrowLoc.isInvalid());
        diagnose(Tok, diag::async_after_throws,
                 reasync && isReasync,
                 rethrows && *rethrows)
            .fixItRemove(Tok.getLoc())
            .fixItInsert(throwsLoc, isReasync ? "reasync " : "async ");
      }
      if (asyncLoc.isInvalid()) {
        Tok.setKind(tok::contextual_keyword);
        if (reasync)
          *reasync = isReasync;
        asyncLoc = Tok.getLoc();
      }
      consumeToken();
      continue;
    }
    // diagnose 'await'
    if (Tok.isContextualKeyword("await") && !Tok.isAtStartOfLine()) {
      diagnose(Tok, diag::await_in_function_type)
        .fixItReplace(Tok.getLoc(), "async");
      consumeToken();
      continue;
    }

    // 'throws'/'rethrows', or diagnose 'throw'/'try'.
    if (isThrowsEffectSpecifier(Tok)) {
      bool isRethrows = Tok.is(tok::kw_rethrows);

      if (throwsLoc.isValid()) {
        diagnose(Tok, diag::duplicate_effects_specifier, Tok.getText())
            .highlight(throwsLoc)
            .fixItRemove(Tok.getLoc());
      } else if (Tok.isAny(tok::kw_throw, tok::kw_try)) {
        // Replace 'throw' or 'try' with 'throws'.
        diagnose(Tok, diag::throw_in_function_type)
            .fixItReplace(Tok.getLoc(), "throws");
      } else if (!rethrows && isRethrows) {
        // Replace 'rethrows' with 'throws' unless it's allowed.
        diagnose(Tok, diag::rethrowing_function_type)
            .fixItReplace(Tok.getLoc(), "throws");
      } else if (existingArrowLoc.isValid()) {
        diagnose(Tok, diag::async_or_throws_in_wrong_position, Tok.getText())
            .fixItRemove(Tok.getLoc())
            .fixItInsert(existingArrowLoc, (Tok.getText() + " ").str());
      }

      if (throwsLoc.isInvalid()) {
        if (rethrows)
          *rethrows = isRethrows;
        throwsLoc = Tok.getLoc();
      }
      consumeToken();

      // Parse the thrown error type.
      SourceLoc lParenLoc;
      if (consumeIf(tok::l_paren, lParenLoc)) {
        ParserResult<TypeRepr> parsedThrownTy =
            parseType(diag::expected_thrown_error_type);
        thrownType = parsedThrownTy.getPtrOrNull();
        status |= parsedThrownTy;

        SourceLoc rParenLoc;
        parseMatchingToken(
            tok::r_paren, rParenLoc,
            diag::expected_rparen_after_thrown_error_type, lParenLoc);

        if (isRethrows) {
          diagnose(throwsLoc, diag::rethrows_with_thrown_error)
            .highlight(SourceRange(lParenLoc, rParenLoc));

          isRethrows = false;

          if (rethrows)
            *rethrows = false;
        }
      }

      continue;
    }

    // Code completion.
    if (Tok.is(tok::code_complete) && !Tok.isAtStartOfLine() &&
        !existingArrowLoc.isValid()) {
      if (CodeCompletionCallbacks) {
        CodeCompletionCallbacks->completeEffectsSpecifier(asyncLoc.isValid(),
                                                          throwsLoc.isValid());
      }
      consumeToken(tok::code_complete);
      status.setHasCodeCompletionAndIsError();
      continue;
    }

    break;
  }
  return status;
}

/// Parse a pattern with an optional type annotation.
///
///  typed-pattern ::= pattern (':' type)?
///
ParserResult<Pattern> Parser::parseTypedPattern() {
  auto result = parsePattern();
  
  // Now parse an optional type annotation.
  if (Tok.is(tok::colon)) {
    SourceLoc colonLoc = consumeToken(tok::colon);
    
    if (result.isNull()) {
      // Recover by creating AnyPattern.
      auto *AP = new (Context) AnyPattern(colonLoc);
      if (colonLoc.isInvalid())
        AP->setImplicit();
      result = makeParserErrorResult(AP);
    }

    ParserResult<TypeRepr> Ty = parseDeclResultType(diag::expected_type);
    if (Ty.hasCodeCompletion())
      return makeParserCodeCompletionResult<Pattern>();
    if (!Ty.isNull()) {
      // Attempt to diagnose initializer calls incorrectly written
      // as typed patterns, such as "var x: [Int]()".
      // Disable this tentative parse when in IDE inspection mode, otherwise
      // code-completion may enter the delayed-decl state twice.
      if (Tok.isFollowingLParen() &&
          !SourceMgr.hasIDEInspectionTargetBuffer()) {
        CancellableBacktrackingScope backtrack(*this);

        SmallVector<ExprListElt, 2> elts;
        auto argListResult = parseArgumentList(tok::l_paren, tok::r_paren,
                                               /*isExprBasic*/ false);
        if (!argListResult.isParseErrorOrHasCompletion()) {
          backtrack.cancelBacktrack();
          
          // Suggest replacing ':' with '='
          auto *args = argListResult.get();
          diagnose(args->getLParenLoc(), diag::initializer_as_typed_pattern)
            .highlight({Ty.get()->getStartLoc(), args->getRParenLoc()})
            .fixItReplace(colonLoc, " = ");
          result.setIsParseError();
        }
      }
    } else {
      Ty = makeParserResult(ErrorTypeRepr::create(Context, PreviousLoc));
    }
    
    result = makeParserResult(result,
                            new (Context) TypedPattern(result.get(), Ty.get()));
  }
  
  return result;
}

/// Parse a pattern.
///   pattern ::= identifier
///   pattern ::= '_'
///   pattern ::= pattern-tuple
///   pattern ::= 'var' pattern
///   pattern ::= 'let' pattern
///
ParserResult<Pattern> Parser::parsePattern() {
  auto introducer =
      InBindingPattern.getIntroducer().value_or(VarDecl::Introducer::Let);
  switch (Tok.getKind()) {
  case tok::l_paren:
    return parsePatternTuple();

  case tok::kw__: {
    // Normally, '_' is invalid in type context for patterns, but they show up
    // in interface files as the name for type members that are non-public.
    // Treat them as an implicitly synthesized NamedPattern with a nameless
    // VarDecl inside.
    if (CurDeclContext->isTypeContext() &&
        SF.Kind == SourceFileKind::Interface) {
      auto VD = new (Context) VarDecl(
        /*IsStatic*/false, introducer,
        consumeToken(tok::kw__), Identifier(), CurDeclContext);
      return makeParserResult(NamedPattern::createImplicit(Context, VD));
    }

    const auto isAsyncLet =
        InPatternWithAsyncAttribute && introducer == VarDecl::Introducer::Let;
    return makeParserResult(
        new (Context) AnyPattern(consumeToken(tok::kw__), isAsyncLet));
  }
  case tok::identifier: {
    Identifier name;
    SourceLoc loc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/true);
    if (Tok.isIdentifierOrUnderscore() && !Tok.isContextualDeclKeyword() &&
        !Tok.isAtStartOfLine())
      diagnoseConsecutiveIDs(name.str(), loc,
                             introducer == VarDecl::Introducer::Let
                             ? "constant" : "variable");

    return makeParserResult(createBindingFromPattern(loc, name, introducer));
  }
    
  case tok::code_complete:
    if (!CurDeclContext->isTypeContext()) {
      // This cannot be an overridden property, so just eat the token. We cannot
      // code complete anything here -- we expect an identifier.
      consumeToken(tok::code_complete);
    }
    return makeParserCodeCompletionStatus();
  case tok::kw_inout:
    // If we don't have the reference binding feature, break if we have
    // inout. Otherwise, go below.
    if (!Context.LangOpts.hasFeature(Feature::ReferenceBindings))
      break;
    LLVM_FALLTHROUGH;
  case tok::kw_var:
  case tok::kw_let: {
    auto newBindingState = PatternBindingState(Tok);
    SourceLoc varLoc = consumeToken();

    // 'var', 'let', 'inout' patterns shouldn't nest.
    if (InBindingPattern.getIntroducer().has_value()) {
      auto diag = diag::var_pattern_in_var;
      unsigned index = *newBindingState.getSelectIndexForIntroducer();
      if (Context.LangOpts.hasFeature(Feature::ReferenceBindings)) {
        diag = diag::var_pattern_in_var_inout;
      }
      diagnose(varLoc, diag, index);
    }

    // 'let' isn't valid inside an implicitly immutable context, but var is.
    if (newBindingState.isLet() &&
        InBindingPattern == PatternBindingState::ImplicitlyImmutable)
      diagnose(varLoc, diag::let_pattern_in_immutable_context);

    // In our recursive parse, remember that we're in a let/var/inout
    // pattern. We default to var if we don't have an immediate pattern bidning
    // state.
    llvm::SaveAndRestore<decltype(InBindingPattern)> T(
        InBindingPattern, newBindingState.getPatternBindingStateForIntroducer(
                              VarDecl::Introducer::Var));

    // Reset async attribute in parser context.
    llvm::SaveAndRestore<bool> AsyncAttr(InPatternWithAsyncAttribute, false);

    ParserResult<Pattern> subPattern = parsePattern();
    if (subPattern.hasCodeCompletion())
      return makeParserCodeCompletionResult<Pattern>();
    if (subPattern.isNull())
      return nullptr;
    return makeParserResult(new (Context) BindingPattern(
        varLoc,
        newBindingState.getIntroducer().value_or(VarDecl::Introducer::Var),
        subPattern.get()));
  }
      
  default:
    break;
  }

  // Handle the default case.
  if (Tok.isKeyword() &&
      (peekToken().is(tok::colon) || peekToken().is(tok::equal))) {
    diagnose(Tok, diag::keyword_cant_be_identifier, Tok.getText());
    diagnose(Tok, diag::backticks_to_escape)
      .fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
    SourceLoc Loc = Tok.getLoc();
    consumeToken();
    return makeParserErrorResult(new (Context) AnyPattern(Loc));
  }
  diagnose(Tok, diag::expected_pattern);
  return nullptr;
}

Pattern *Parser::createBindingFromPattern(SourceLoc loc, Identifier name,
                                          VarDecl::Introducer introducer) {
  auto var = new (Context) VarDecl(/*IsStatic*/false, introducer,
                                   loc, name, CurDeclContext);
  return new (Context) NamedPattern(var);
}

/// Parse an element of a tuple pattern.
///
///   pattern-tuple-element:
///     (identifier ':')? pattern
std::pair<ParserStatus, std::optional<TuplePatternElt>>
Parser::parsePatternTupleElement() {
  // If this element has a label, parse it.
  Identifier Label;
  SourceLoc LabelLoc;

  // If the tuple element has a label, parse it.
  if (Tok.is(tok::identifier) && peekToken().is(tok::colon)) {
    LabelLoc = consumeIdentifier(Label, /*diagnoseDollarPrefix=*/true);
    consumeToken(tok::colon);
  }

  // Parse the pattern.
  ParserResult<Pattern>  pattern = parsePattern();
  if (pattern.hasCodeCompletion())
    return std::make_pair(makeParserCodeCompletionStatus(), std::nullopt);
  if (pattern.isNull())
    return std::make_pair(makeParserError(), std::nullopt);

  auto Elt = TuplePatternElt(Label, LabelLoc, pattern.get());
  return std::make_pair(makeParserSuccess(), Elt);
}

/// Parse a tuple pattern.
///
///   pattern-tuple:
///     '(' pattern-tuple-body? ')'
///   pattern-tuple-body:
///     pattern-tuple-element (',' pattern-tuple-body)*
ParserResult<Pattern> Parser::parsePatternTuple() {
  StructureMarkerRAII ParsingPatternTuple(*this, Tok);
  SourceLoc LPLoc = consumeToken(tok::l_paren);
  SourceLoc RPLoc;

  // Parse all the elements.
  SmallVector<TuplePatternElt, 8> elts;
  ParserStatus ListStatus = parseList(
      tok::r_paren, LPLoc, RPLoc,
      /*AllowSepAfterLast=*/true, diag::expected_rparen_tuple_pattern_list,
      [&]() -> ParserStatus {
        // Parse the pattern tuple element.
        ParserStatus EltStatus;
        std::optional<TuplePatternElt> elt;
        std::tie(EltStatus, elt) = parsePatternTupleElement();
        if (EltStatus.hasCodeCompletion())
          return makeParserCodeCompletionStatus();
        if (!elt)
          return makeParserError();

        // Add this element to the list.
        elts.push_back(*elt);
        return makeParserSuccess();
      });

  return makeParserResult(
           ListStatus,
           TuplePattern::createSimple(Context, LPLoc, elts, RPLoc));
}

/// Parse an optional type annotation on a pattern.
///
///  pattern-type-annotation ::= (':' type)?
///
ParserResult<Pattern> Parser::
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result) {
  if (!Tok.is(tok::colon))
    return result;

  // Parse an optional type annotation.
  consumeToken(tok::colon);

  if (result.isNull())
    return result;

  Pattern *P = result.get();
  ParserStatus status;
  if (result.hasCodeCompletion())
    status.setHasCodeCompletionAndIsError();

  ParserResult<TypeRepr> Ty = parseType();
  if (Ty.hasCodeCompletion()) {
    result.setHasCodeCompletionAndIsError();
    return result;
  }

  TypeRepr *repr = Ty.getPtrOrNull();
  if (!repr)
    repr = ErrorTypeRepr::create(Context, PreviousLoc);

  return makeParserResult(status, new (Context) TypedPattern(P, repr));
}



/// matching-pattern ::= 'is' type
/// matching-pattern ::= matching-pattern-var
/// matching-pattern ::= expr
///
ParserResult<Pattern> Parser::parseMatchingPattern(bool isExprBasic) {
  // TODO: Since we expect a pattern in this position, we should optimistically
  // parse pattern nodes for productions shared by pattern and expression
  // grammar. For short-term ease of initial implementation, we always go
  // through the expr parser for ambiguous productions.

  // Parse productions that can only be patterns.
  if (Tok.isAny(tok::kw_var, tok::kw_let) ||
      (Context.LangOpts.hasFeature(Feature::ReferenceBindings) &&
       Tok.isAny(tok::kw_inout))) {
    assert(Tok.isAny(tok::kw_let, tok::kw_var, tok::kw_inout) && "expects var or let");
    auto newPatternBindingState = PatternBindingState(Tok);
    SourceLoc varLoc = consumeToken();

    return parseMatchingPatternAsBinding(newPatternBindingState, varLoc,
                                         isExprBasic);
  }
  
  // The `borrowing` modifier is a contextual keyword, so it's only accepted
  // directly applied to a binding name, as in `case .foo(borrowing x)`.
  if ((Tok.isContextualKeyword("_borrowing")
       || Tok.isContextualKeyword("borrowing"))
      && peekToken().isAny(tok::identifier, tok::kw_self, tok::dollarident,
                           tok::code_complete)
      && !peekToken().isAtStartOfLine()) {
    diagnose(Tok.getLoc(),
             diag::borrowing_syntax_change)
      .fixItReplace(Tok.getLoc(), "let");

    Tok.setKind(tok::contextual_keyword);
    SourceLoc borrowingLoc = consumeToken();
    
    // If we have `case borrowing x.`, `x(`, `x[`, or `x<` then this looks
    // like an attempt to include a subexpression under a `borrowing`
    // binding, which isn't yet supported.
    if (peekToken().isAny(tok::period, tok::period_prefix, tok::l_paren,
                          tok::l_square)
        || (peekToken().isAnyOperator() && peekToken().getText() == "<")) {

      // Diagnose the unsupported production.
      diagnose(Tok.getLoc(),
               diag::borrowing_subpattern_unsupported);
      
      // Recover by parsing as if it was supported.
      return parseMatchingPattern(isExprBasic);
    }
    Identifier name;
    SourceLoc nameLoc = consumeIdentifier(name,
                                          /*diagnoseDollarPrefix*/ false);
    auto namedPattern = createBindingFromPattern(nameLoc, name,
                                           VarDecl::Introducer::Borrowing);
    auto bindPattern = new (Context) BindingPattern(
      borrowingLoc, VarDecl::Introducer::Borrowing, namedPattern);
    
    return makeParserResult(bindPattern);
  }

  // matching-pattern ::= 'is' type
  if (Tok.is(tok::kw_is)) {
    SourceLoc isLoc = consumeToken(tok::kw_is);
    ParserResult<TypeRepr> castType = parseType();
    if (castType.isNull() || castType.hasCodeCompletion())
      return nullptr;
    auto *CastTE = new (Context) TypeExpr(castType.get());
    return makeParserResult(new (Context) IsPattern(
        isLoc, CastTE, nullptr, CheckedCastKind::Unresolved));
  }

  // matching-pattern ::= expr
  // Fall back to expression parsing for ambiguous forms. Name lookup will
  // disambiguate.
  ParserResult<Expr> subExpr =
    parseExprImpl(diag::expected_pattern, isExprBasic);
  ParserStatus status = subExpr;
  if (subExpr.isNull())
    return status;

  if (isa<CodeCompletionExpr>(subExpr.get()) && Tok.isFollowingLParen()) {
    // We are in the case like the following of parsing a pattern with the code
    // completion token as base and associated value matches:
    // #^COMPLETE^#(let a)
    // We will have not consumed the `(let a)` in `parseExprPostfixSuffix`
    // because usually suffixes don't influence the code completion's type and
    // the suffix might be unrelated. But the trailing `(let a)` that is left
    // prevents us from forming a valid pattern.
    // Consume and discard the `(let a)`, which just leaves us with the base
    // of the pattern.
    (void)parseExprCallSuffix(subExpr, isExprBasic);
  }

  // The most common case here is to parse something that was a lexically
  // obvious pattern, which will come back wrapped in an immediate
  // UnresolvedPatternExpr.  Transform this now to simplify later code.
  if (auto *UPE = dyn_cast<UnresolvedPatternExpr>(subExpr.get()))
    return makeParserResult(status, UPE->getSubPattern());

  auto *EP = ExprPattern::createParsed(Context, subExpr.get(), CurDeclContext);
  return makeParserResult(status, EP);
}

ParserResult<Pattern>
Parser::parseMatchingPatternAsBinding(PatternBindingState newState,
                                      SourceLoc varLoc, bool isExprBasic) {
  // 'var', 'let', 'inout' patterns shouldn't nest.
  if (InBindingPattern.getIntroducer().has_value()) {
    auto diag = diag::var_pattern_in_var;
    if (Context.LangOpts.hasFeature(Feature::ReferenceBindings))
      diag = diag::var_pattern_in_var_inout;
    diagnose(varLoc, diag,
             *newState.getSelectIndexForIntroducer());
  }

  // 'let' isn't valid inside an implicitly immutable context, but var is.
  if (newState.isLet() &&
      InBindingPattern == PatternBindingState::ImplicitlyImmutable)
    diagnose(varLoc, diag::let_pattern_in_immutable_context);

  // In our recursive parse, remember that we're in a var/let pattern.
  llvm::SaveAndRestore<decltype(InBindingPattern)> T(
      InBindingPattern,
      newState.getPatternBindingStateForIntroducer(VarDecl::Introducer::Var));

  // Reset async attribute in parser context.
  llvm::SaveAndRestore<bool> AsyncAttr(InPatternWithAsyncAttribute, false);

  ParserResult<Pattern> subPattern = parseMatchingPattern(isExprBasic);
  if (subPattern.isNull())
    return nullptr;
  auto *varP = new (Context) BindingPattern(
      varLoc, newState.getIntroducer().value_or(VarDecl::Introducer::Var),
      subPattern.get());
  return makeParserResult(ParserStatus(subPattern), varP);
}

bool Parser::isOnlyStartOfMatchingPattern() {
  if ((Tok.isContextualKeyword("_borrowing")
       || Tok.isContextualKeyword("borrowing"))
      && peekToken().isAny(tok::identifier, tok::kw_self, tok::dollarident,
                           tok::code_complete)
      && !peekToken().isAtStartOfLine()) {
    return true;
  }

  return Tok.isAny(tok::kw_var, tok::kw_let, tok::kw_is) ||
         (Context.LangOpts.hasFeature(Feature::ReferenceBindings) &&
          Tok.isAny(tok::kw_inout));
}


static bool canParsePatternTuple(Parser &P);

///   pattern ::= identifier
///   pattern ::= '_'
///   pattern ::= pattern-tuple
///   pattern ::= 'var' pattern
///   pattern ::= 'let' pattern
static bool canParsePattern(Parser &P) {
  switch (P.Tok.getKind()) {
  case tok::identifier:
  case tok::kw__:
    P.consumeToken();
    return true;
  case tok::kw_inout:
    if (!P.Context.LangOpts.hasFeature(Feature::ReferenceBindings))
      return false;
    LLVM_FALLTHROUGH;
  case tok::kw_let:
  case tok::kw_var:
    P.consumeToken();
    return canParsePattern(P);
  case tok::l_paren:
    return canParsePatternTuple(P);

  default:
    return false;
  }
}


static bool canParsePatternTuple(Parser &P) {
  if (!P.consumeIf(tok::l_paren)) return false;

  if (P.Tok.isNot(tok::r_paren)) {
    do {
      if (!canParsePattern(P)) return false;
    } while (P.consumeIf(tok::comma));
  }

  return P.consumeIf(tok::r_paren);
}

///  typed-pattern ::= pattern (':' type)?
///
bool Parser::canParseTypedPattern() {
  if (!canParsePattern(*this)) return false;
  
  if (consumeIf(tok::colon))
    return canParseType();
  return true;
}
