bool Parser::parseNewDeclAttribute()

in lib/Parse/ParseDecl.cpp [1868:2875]


bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
                                   DeclAttrKind DK, bool isFromClangAttribute) {
  // Ok, it is a valid attribute, eat it, and then process it.
  StringRef AttrName = Tok.getText();
  SourceLoc Loc = consumeToken();

  bool DiscardAttribute = false;

  // Diagnose duplicated attributes.
  const DeclAttribute *DuplicateAttribute = nullptr;
  if (!DeclAttribute::allowMultipleAttributes(DK))
    if ((DuplicateAttribute = Attributes.getAttribute(DK))) {
      // Delay issuing the diagnostic until we parse the attribute.
      DiscardAttribute = true;
    }

  // If this is a SIL-only attribute, reject it.
  if ((DeclAttribute::getOptions(DK) & DeclAttribute::SILOnly) != 0 &&
      !isInSILMode()) {
    diagnose(Loc, diag::only_allowed_in_sil, AttrName);
    DiscardAttribute = true;
  }

  // If this attribute is only permitted when concurrency is enabled, reject it.
  if (DeclAttribute::isConcurrencyOnly(DK) &&
      !shouldParseExperimentalConcurrency()) {
    // Ignore concurrency-only attributes that come from Clang.
    if (!isFromClangAttribute) {
      diagnose(
          Loc, diag::attr_requires_concurrency, AttrName,
          DeclAttribute::isDeclModifier(DK));
    }

    DiscardAttribute = true;
  }

  // If this attribute is only permitted when distributed is enabled, reject it.
  if (DeclAttribute::isDistributedOnly(DK) &&
      !shouldParseExperimentalDistributed()) {
    diagnose(Loc, diag::attr_requires_distributed, AttrName,
             DeclAttribute::isDeclModifier(DK));
    DiscardAttribute = true;
  }

  if (Context.LangOpts.Target.isOSBinFormatCOFF()) {
    if (DK == DAK_WeakLinked) {
      diagnose(Loc, diag::attr_unsupported_on_target, AttrName,
               Context.LangOpts.Target.str());
      DiscardAttribute = true;
    }
  }

  // Filled in during parsing.  If there is a duplicate
  // diagnostic this can be used for better error presentation.
  SourceRange AttrRange;

  switch (DK) {
  case DAK_Count:
    llvm_unreachable("DAK_Count should not appear in parsing switch");

  case DAK_RawDocComment:
  case DAK_ObjCBridged:
  case DAK_RestatedObjCConformance:
  case DAK_SynthesizedProtocol:
  case DAK_ClangImporterSynthesizedType:
  case DAK_Custom:
    llvm_unreachable("virtual attributes should not be parsed "
                     "by attribute parsing code");
  case DAK_SetterAccess:
    llvm_unreachable("handled by DAK_AccessControl");

#define SIMPLE_DECL_ATTR(_, CLASS, ...) \
  case DAK_##CLASS: \
    if (!DiscardAttribute) \
      Attributes.add(new (Context) CLASS##Attr(AtLoc, Loc)); \
    break;
#include "swift/AST/Attr.def"

  case DAK_MainType:
    if (!DiscardAttribute)
      Attributes.add(new (Context) MainTypeAttr(AtLoc, Loc));
    break;

  case DAK_Effects: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));      return false;
    }
    EffectsKind kind = EffectsKind::Unspecified;
    SourceLoc customStart, customEnd;
    {
      SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList);

      if (Tok.isNot(tok::identifier)) {
        diagnose(Loc, diag::error_in_effects_attribute, "expected identifier");
        return false;
      }

      if (Tok.getText() == "readonly")
        kind = EffectsKind::ReadOnly;
      else if (Tok.getText() == "readnone")
        kind = EffectsKind::ReadNone;
      else if (Tok.getText() == "readwrite")
        kind = EffectsKind::ReadWrite;
      else if (Tok.getText() == "releasenone")
        kind = EffectsKind::ReleaseNone;
      else {
        customStart = customEnd = Tok.getLoc();
        while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::eof)) {
          consumeToken();
        }
        customEnd = Tok.getLoc();
        kind = EffectsKind::Custom;
        AttrRange = SourceRange(Loc, customEnd);
      }
      if (kind != EffectsKind::Custom) {
        AttrRange = SourceRange(Loc, Tok.getRange().getStart());
        consumeToken(tok::identifier);
      }
    }
    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    if (!DiscardAttribute) {
      if (kind == EffectsKind::Custom) {
        StringRef customStr = SourceMgr.extractText(
                          CharSourceRange(SourceMgr, customStart, customEnd));
        Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange,
                                                 customStr, customStart));
      } else {
        Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange, kind));
      }
    }
    break;
  }

  case DAK_Inline: {
    auto kind = parseSingleAttrOption<InlineKind>
                         (*this, Loc, AttrRange, AttrName, DK)
                    .when("never", InlineKind::Never)
                    .when("__always", InlineKind::Always)
                    .diagnoseWhenOmitted();
    if (!kind)
      return false;

    if (!DiscardAttribute)
      Attributes.add(new (Context) InlineAttr(AtLoc, AttrRange, *kind));

    break;
  }

  case DAK_Optimize: {
    auto optMode = parseSingleAttrOption<OptimizationMode>
                            (*this, Loc, AttrRange, AttrName, DK)
                       .when("speed", OptimizationMode::ForSpeed)
                       .when("size", OptimizationMode::ForSize)
                       .when("none", OptimizationMode::NoOptimization)
                       .diagnoseWhenOmitted();
    if (!optMode)
      return false;

    if (!DiscardAttribute)
      Attributes.add(new (Context) OptimizeAttr(AtLoc, AttrRange, *optMode));

    break;
  }

  case DAK_Exclusivity: {
    auto mode = parseSingleAttrOption<ExclusivityAttr::Mode>
                            (*this, Loc, AttrRange, AttrName, DK)
                       .when("checked", ExclusivityAttr::Mode::Checked)
                       .when("unchecked", ExclusivityAttr::Mode::Unchecked)
                       .diagnoseWhenOmitted();
    if (!mode)
      return false;

    if (!DiscardAttribute)
      Attributes.add(new (Context) ExclusivityAttr(AtLoc, AttrRange, *mode));

    break;
  }

  case DAK_ReferenceOwnership: {
    // Handle weak/unowned/unowned(unsafe).
    auto Kind = AttrName == "weak" ? ReferenceOwnership::Weak
                                   : ReferenceOwnership::Unowned;

    if (Kind == ReferenceOwnership::Unowned) {
      // Parse an optional specifier after unowned.
      Kind = parseSingleAttrOption<ReferenceOwnership>
                     (*this, Loc, AttrRange, AttrName, DK)
                .when("unsafe", ReferenceOwnership::Unmanaged)
                .when("safe", ReferenceOwnership::Unowned)
                .whenOmitted(ReferenceOwnership::Unowned)
            // Recover from errors by going back to Unowned.
            .getValueOr(ReferenceOwnership::Unowned);
    }
    else {
      AttrRange = SourceRange(Loc);
    }

    if (!DiscardAttribute)
      Attributes.add(
          new (Context) ReferenceOwnershipAttr(AttrRange, Kind));

    break;
  }

  case DAK_NonSendable: {
    auto kind = parseSingleAttrOption<NonSendableKind>
                         (*this, Loc, AttrRange, AttrName, DK)
                    .when("_assumed", NonSendableKind::Assumed)
                    .whenOmitted(NonSendableKind::Specific);
    if (!kind)
      return false;

    if (!DiscardAttribute)
      Attributes.add(new (Context) NonSendableAttr(AtLoc, AttrRange, *kind));

    break;
  }

  case DAK_AccessControl: {

    // Diagnose using access control in a local scope, which isn't meaningful.
    if (CurDeclContext->isLocalContext()) {
      diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
    }

    AccessLevel access = llvm::StringSwitch<AccessLevel>(AttrName)
      .Case("private", AccessLevel::Private)
      .Case("fileprivate", AccessLevel::FilePrivate)
      .Case("internal", AccessLevel::Internal)
      .Case("public", AccessLevel::Public)
      .Case("open", AccessLevel::Open);

    if (!consumeIf(tok::l_paren)) {
      // Normal access control attribute.
      AttrRange = Loc;
      DuplicateAttribute = Attributes.getAttribute<AccessControlAttr>();
      if (!DuplicateAttribute)
        Attributes.add(new (Context) AccessControlAttr(AtLoc, Loc, access));
      break;
    }

    // Parse the subject.
    if (Tok.isContextualKeyword("set")) {
      consumeToken();
    } else {
      diagnose(Loc, diag::attr_access_expected_set, AttrName);
      // Minimal recovery: if there's a single token and then an r_paren,
      // consume them both. If there's just an r_paren, consume that.
      if (!consumeIf(tok::r_paren)) {
        if (Tok.isNot(tok::l_paren) && peekToken().is(tok::r_paren)) {
          consumeToken();
          consumeToken(tok::r_paren);
        }
      }
      return false;
    }

    AttrRange = SourceRange(Loc, Tok.getLoc());

    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    DuplicateAttribute = Attributes.getAttribute<SetterAccessAttr>();
    if (!DuplicateAttribute) {
      Attributes.add(new (Context) SetterAccessAttr(AtLoc, AttrRange, access));
    }

    break;
  }

  case DAK_SPIAccessControl: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    SmallVector<Identifier, 4> spiGroups;

    if (!Tok.is(tok::identifier) ||
        Tok.isContextualKeyword("set")) {
      diagnose(getEndOfPreviousLoc(), diag::attr_access_expected_spi_name);
      consumeToken();
      consumeIf(tok::r_paren);
      return false;
    }

    auto text = Tok.getText();
    spiGroups.push_back(Context.getIdentifier(text));
    consumeToken();

    AttrRange = SourceRange(Loc, Tok.getLoc());

    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    Attributes.add(SPIAccessControlAttr::create(Context, AtLoc, AttrRange,
                                                spiGroups));
    break;
  }

  case DAK_CDecl:
  case DAK_SILGenName: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    if (Tok.isNot(tok::string_literal)) {
      diagnose(Loc, diag::attr_expected_string_literal, AttrName);
      return false;
    }

    Optional<StringRef> AsmName = getStringLiteralIfNotInterpolated(
        Loc, ("'" + AttrName + "'").str());

    consumeToken(tok::string_literal);

    if (AsmName.hasValue())
      AttrRange = SourceRange(Loc, Tok.getRange().getStart());
    else
      DiscardAttribute = true;

    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    // Diagnose using @_silgen_name in a local scope.  These don't
    // actually work.
    if (CurDeclContext->isLocalContext()) {
      // Emit an error, but do not discard the attribute.  This enables
      // better recovery in the parser.
      diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
    }

    if (!DiscardAttribute) {
      if (DK == DAK_SILGenName)
        Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc,
                                                AttrRange, /*Implicit=*/false));
      else if (DK == DAK_CDecl)
        Attributes.add(new (Context) CDeclAttr(AsmName.getValue(), AtLoc,
                                               AttrRange, /*Implicit=*/false));
      else
        llvm_unreachable("out of sync with switch");
    }

    break;
  }
  
  case DAK_Alignment: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }
    
    if (Tok.isNot(tok::integer_literal)) {
      diagnose(Loc, diag::alignment_must_be_positive_integer);
      return false;
    }
    
    StringRef alignmentText = Tok.getText();
    unsigned alignmentValue;
    if (alignmentText.getAsInteger(0, alignmentValue)) {
      diagnose(Loc, diag::alignment_must_be_positive_integer);
      return false;
    }
    
    consumeToken(tok::integer_literal);
    
    auto range = SourceRange(Loc, Tok.getRange().getStart());
    
    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    Attributes.add(new (Context) AlignmentAttr(alignmentValue, AtLoc, range,
                                               /*implicit*/ false));
    
    break;
  }
  
  case DAK_SwiftNativeObjCRuntimeBase: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    if (Tok.isNot(tok::identifier)) {
      diagnose(Loc, diag::swift_native_objc_runtime_base_must_be_identifier);
      return false;
    }
    
    Identifier name;
    consumeIdentifier(name, /*diagnoseDollarPrefix=*/false);

    auto range = SourceRange(Loc, Tok.getRange().getStart());

    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }
    
    Attributes.add(new (Context) SwiftNativeObjCRuntimeBaseAttr(name,
                                            AtLoc, range, /*implicit*/ false));
    break;
  }
  
  case DAK_Semantics: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    if (Tok.isNot(tok::string_literal)) {
      diagnose(Loc, diag::attr_expected_string_literal, AttrName);
      return false;
    }

    auto Value = getStringLiteralIfNotInterpolated(
        Loc, ("'" + AttrName + "'").str());

    consumeToken(tok::string_literal);

    if (Value.hasValue())
      AttrRange = SourceRange(Loc, Tok.getRange().getStart());
    else
      DiscardAttribute = true;

    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    if (!DiscardAttribute)
      Attributes.add(new (Context) SemanticsAttr(Value.getValue(), AtLoc,
                                                 AttrRange,
                                                 /*Implicit=*/false));
    break;
  }
  case DAK_OriginallyDefinedIn: {
    auto LeftLoc = Tok.getLoc();
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }
    SourceLoc RightLoc;
    enum class NextSegmentKind: uint8_t {
      ModuleName = 0,
      PlatformVersion,
    };
    NextSegmentKind NK = NextSegmentKind::ModuleName;
    StringRef OriginalModuleName;
    llvm::SmallVector<std::pair<PlatformKind, llvm::VersionTuple>, 4>
      PlatformAndVersions;

    StringRef AttrName = "@_originallyDefinedIn";
    bool SuppressLaterDiags = false;
    if (parseList(tok::r_paren, LeftLoc, RightLoc, false,
                  diag::originally_defined_in_missing_rparen,
                  SyntaxKind::AvailabilitySpecList, [&]() -> ParserStatus {
      SWIFT_DEFER {
        if (NK != NextSegmentKind::PlatformVersion) {
          NK = (NextSegmentKind)((uint8_t)NK + (uint8_t)1);
        }
      };
      switch (NK) {
      // Parse 'module: "original_module_name"'.
      case NextSegmentKind::ModuleName: {
        SyntaxParsingContext argumentContext(SyntaxContext,
                                             SyntaxKind::AvailabilityLabeledArgument);
        // Parse 'module' ':'.
        if (!Tok.is(tok::identifier) || Tok.getText() != "module" ||
            !peekToken().is(tok::colon)) {
          diagnose(Tok, diag::originally_defined_in_need_original_module_name);
          SuppressLaterDiags = true;
          return makeParserError();
        }
        consumeToken(tok::identifier);
        consumeToken(tok::colon);
        // Parse the next string literal as the original module name.
        auto ModuleNameLoc = Tok.getLoc();
        if (Tok.is(tok::string_literal)) {
          auto NameOp = getStringLiteralIfNotInterpolated(Tok.getLoc(),
                                                          "original module name");
          if (NameOp.hasValue())
            OriginalModuleName = *NameOp;
          consumeToken();
        }
        if (OriginalModuleName.empty()) {
          diagnose(ModuleNameLoc,
                   diag::originally_defined_in_need_nonempty_module_name);
          SuppressLaterDiags = true;
          return makeParserError();
        }
        return makeParserSuccess();
      }
      // Parse 'OSX 13.13'.
      case NextSegmentKind::PlatformVersion: {
        SyntaxParsingContext argumentContext(SyntaxContext,
                                             SyntaxKind::AvailabilityVersionRestriction);
        if ((Tok.is(tok::identifier) || Tok.is(tok::oper_binary_spaced)) &&
            (peekToken().isAny(tok::integer_literal, tok::floating_literal) ||
             peekAvailabilityMacroName())) {

          PlatformKind Platform;

          if (peekAvailabilityMacroName()) {
            // Handle availability macros first.
            //
            // The logic to search for macros and platform name could
            // likely be handled by parseAvailabilitySpecList
            // if we don't rely on parseList here.
            SmallVector<AvailabilitySpec *, 4> Specs;
            ParserStatus MacroStatus = parseAvailabilityMacro(Specs);
            if (MacroStatus.isError())
              return MacroStatus;

            for (auto *Spec : Specs) {
              if (auto *PlatformVersionSpec =
                   dyn_cast<PlatformVersionConstraintAvailabilitySpec>(Spec)) {
                auto Platform = PlatformVersionSpec->getPlatform();
                auto Version = PlatformVersionSpec->getVersion();
                if (Version.getSubminor().hasValue() ||
                    Version.getBuild().hasValue()) {
                  diagnose(Tok.getLoc(), diag::originally_defined_in_major_minor_only);
                }
                PlatformAndVersions.emplace_back(Platform, Version);

              } else if (auto *PlatformAgnostic =
                  dyn_cast<PlatformAgnosticVersionConstraintAvailabilitySpec>(Spec)) {
                diagnose(PlatformAgnostic->getPlatformAgnosticNameLoc(),
                         PlatformAgnostic->isLanguageVersionSpecific() ?
                           diag::originally_defined_in_swift_version :
                           diag::originally_defined_in_package_description);

              } else if (auto *OtherPlatform =
                         dyn_cast<OtherPlatformAvailabilitySpec>(Spec)) {
                diagnose(OtherPlatform->getStarLoc(),
                         diag::originally_defined_in_missing_platform_name);

              } else {
                llvm_unreachable("Unexpected AvailabilitySpec kind.");
              }
            }

            return makeParserSuccess();
          }

          // Parse platform name.
          auto Plat = platformFromString(Tok.getText());
          if (!Plat.hasValue()) {
            diagnose(Tok.getLoc(),
                     diag::originally_defined_in_unrecognized_platform);
            SuppressLaterDiags = true;
            return makeParserError();
          } else {
            consumeToken();
            Platform = *Plat;
          }
          // Parse version number
          llvm::VersionTuple VerTuple;
          SourceRange VersionRange;
          if (parseVersionTuple(VerTuple, VersionRange,
              Diagnostic(diag::attr_availability_expected_version, AttrName))) {
            SuppressLaterDiags = true;
            return makeParserError();
          } else {
            if (VerTuple.getSubminor().hasValue() ||
                VerTuple.getBuild().hasValue()) {
              diagnose(Tok.getLoc(), diag::originally_defined_in_major_minor_only);
            }
            // * as platform name isn't supported.
            if (Platform == PlatformKind::none) {
              diagnose(AtLoc, diag::originally_defined_in_missing_platform_name);
            } else {
              PlatformAndVersions.emplace_back(Platform, VerTuple);
            }
            return makeParserSuccess();
          }
        }
        diagnose(AtLoc, diag::originally_defined_in_need_platform_version);
        SuppressLaterDiags = true;
        return makeParserError();
      }
      }
      llvm_unreachable("invalid next segment kind");
    }).isErrorOrHasCompletion() || SuppressLaterDiags) {
      return false;
    }
    if (OriginalModuleName.empty()) {
      diagnose(AtLoc, diag::originally_defined_in_need_nonempty_module_name);
      return false;
    }
    if (PlatformAndVersions.empty()) {
      diagnose(AtLoc, diag::originally_defined_in_need_platform_version);
      return false;
    }

    assert(!OriginalModuleName.empty());
    assert(!PlatformAndVersions.empty());
    assert(NK == NextSegmentKind::PlatformVersion);
    AttrRange = SourceRange(Loc, Tok.getLoc());
    for (auto &Item: PlatformAndVersions) {
      Attributes.add(new (Context) OriginallyDefinedInAttr(AtLoc, AttrRange,
                                                           OriginalModuleName,
                                                           Item.first,
                                                           Item.second,
                                                           /*IsImplicit*/false));
    }
    break;
  }
  case DAK_Available: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }
    if (!parseAvailability(false, AttrName, DiscardAttribute, AttrRange, AtLoc,
                           Loc,
                           [&](AvailableAttr *attr) { Attributes.add(attr); }))
      return false;
    break;
  }
  case DAK_PrivateImport: {
    // Parse the leading '('.
    if (Tok.isNot(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }
    SourceLoc LParenLoc = consumeToken(tok::l_paren);
    Optional<StringRef> filename;
    {
      SyntaxParsingContext ContentContext(
          SyntaxContext, SyntaxKind::NamedAttributeStringArgument);

      // Parse 'sourceFile'.
      if (Tok.getText() != "sourceFile") {
        diagnose(LParenLoc, diag::attr_private_import_expected_sourcefile);
        return false;
      }
      auto ForLoc = consumeToken();

      // Parse ':'.
      if (Tok.getKind() != tok::colon) {
        diagnose(ForLoc, diag::attr_private_import_expected_colon);
        return false;
      }
      auto ColonLoc = consumeToken(tok::colon);

      // Parse '"'function-name'"'
      if (Tok.isNot(tok::string_literal)) {
        diagnose(ColonLoc, diag::attr_private_import_expected_sourcefile_name);
        return false;
      }
      filename = getStringLiteralIfNotInterpolated(Loc, "_private");
      if (!filename.hasValue()) {
        diagnose(ColonLoc, diag::attr_private_import_expected_sourcefile_name);
        return false;
      }
      consumeToken(tok::string_literal);
    }
    // Parse the matching ')'.
    SourceLoc RParenLoc;
    bool Invalid = parseMatchingToken(tok::r_paren, RParenLoc,
                                      diag::attr_private_import_expected_rparen,
                                      LParenLoc);
    if (Invalid)
      return false;
    auto *attr = PrivateImportAttr::create(Context, AtLoc, Loc, LParenLoc,
                                           *filename, RParenLoc);
    Attributes.add(attr);

    break;
  }
  case DAK_ObjC: {
    // Unnamed @objc attribute.
    if (Tok.isNot(tok::l_paren)) {
      auto attr = ObjCAttr::createUnnamed(Context, AtLoc, Loc);
      Attributes.add(attr);
      break;
    }

    // Parse the leading '('.
    SourceLoc LParenLoc = consumeToken(tok::l_paren);

    // Parse the names, with trailing colons (if there are present) and populate
    // the inout parameters
    SmallVector<Identifier, 4> Names;
    SmallVector<SourceLoc, 4> NameLocs;
    bool NullarySelector = true;
    parseObjCSelector(Names, NameLocs, NullarySelector);

    // Parse the matching ')'.
    SourceLoc RParenLoc;
    bool Invalid = parseMatchingToken(tok::r_paren, RParenLoc,
                                      diag::attr_objc_expected_rparen,
                                      LParenLoc);

    ObjCAttr *attr;
    if (Names.empty()) {
      // When there are no names, recover as if there were no parentheses.
      if (!Invalid)
        diagnose(LParenLoc, diag::attr_objc_empty_name);
      attr = ObjCAttr::createUnnamed(Context, AtLoc, Loc);
    } else if (NullarySelector) {
      // When we didn't see a colon, this is a nullary name.
      assert(Names.size() == 1 && "Forgot to set sawColon?");
      attr = ObjCAttr::createNullary(Context, AtLoc, Loc, LParenLoc,
                                     NameLocs.front(), Names.front(),
                                     RParenLoc);
    } else {
      // When we did see a colon, this is a selector.
      attr = ObjCAttr::createSelector(Context, AtLoc, Loc, LParenLoc,
                                      NameLocs, Names, RParenLoc);
    }
    Attributes.add(attr);
    break;
  }
  case DAK_ObjCRuntimeName: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    if (Tok.isNot(tok::identifier)) {
      diagnose(Loc, diag::objc_runtime_name_must_be_identifier);
      return false;
    }

    auto name = Tok.getText();

    consumeToken(tok::identifier);

    auto range = SourceRange(Loc, Tok.getRange().getStart());

    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    Attributes.add(new (Context) ObjCRuntimeNameAttr(name, AtLoc, range,
                                                     /*implicit*/ false));
    break;
  }


  case DAK_DynamicReplacement: {
    // Parse the leading '('.
    if (Tok.isNot(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    SourceLoc LParenLoc = consumeToken(tok::l_paren);
    DeclNameRef replacedFunction;
    {
      SyntaxParsingContext ContentContext(
          SyntaxContext, SyntaxKind::NamedAttributeStringArgument);

      // Parse 'for'.
      if (Tok.getText() != "for") {
        diagnose(Loc, diag::attr_dynamic_replacement_expected_for);
        return false;
      }
      auto ForLoc = consumeToken();

      // Parse ':'.
      if (Tok.getText() != ":") {
        diagnose(ForLoc, diag::attr_dynamic_replacement_expected_colon);
        return false;
      }
      consumeToken(tok::colon);
      {
        SyntaxParsingContext ContentContext(SyntaxContext,
                                            SyntaxKind::DeclName);

        DeclNameLoc loc;
        replacedFunction = parseDeclNameRef(loc,
            diag::attr_dynamic_replacement_expected_function,
            DeclNameFlag::AllowZeroArgCompoundNames |
            DeclNameFlag::AllowKeywordsUsingSpecialNames |
            DeclNameFlag::AllowOperators);
      }
    }

    // Parse the matching ')'.
    SourceLoc RParenLoc;
    bool Invalid = parseMatchingToken(
        tok::r_paren, RParenLoc, diag::attr_dynamic_replacement_expected_rparen,
        LParenLoc);
    if (Invalid) {
      return false;
    }


    DynamicReplacementAttr *attr = DynamicReplacementAttr::create(
        Context, AtLoc, Loc, LParenLoc, replacedFunction, RParenLoc);
    Attributes.add(attr);
    break;
  }

  case DAK_TypeEraser: {
    // Parse leading '('
    if (Tok.isNot(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    SourceLoc LParenLoc = consumeToken(tok::l_paren);
    ParserResult<TypeRepr> ErasedType;
    bool invalid = false;
    {
      // Parse type-eraser type
      SyntaxParsingContext ContentContext(SyntaxContext, SyntaxKind::Type);
      ErasedType = parseType(diag::attr_type_eraser_expected_type_name);
      invalid = ErasedType.hasCodeCompletion() || ErasedType.isNull();
    }

    // Parse matching ')'
    SourceLoc RParenLoc;
    invalid |= parseMatchingToken(tok::r_paren, RParenLoc,
                                  diag::attr_type_eraser_expected_rparen,
                                  LParenLoc);
    if (invalid)
      return false;

    auto *TE = new (Context) TypeExpr(ErasedType.get());
    Attributes.add(TypeEraserAttr::create(Context, AtLoc, {Loc, RParenLoc}, TE));
    break;
  }

  case DAK_Specialize: {
    if (Tok.isNot(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }
    SpecializeAttr *Attr;
    if (!parseSpecializeAttribute(tok::r_paren, AtLoc, Loc, Attr, nullptr))
      return false;

    Attributes.add(Attr);
    break;
    }

  case DAK_Implements: {
    ParserResult<ImplementsAttr> Attr = parseImplementsAttribute(AtLoc, Loc);
    if (Attr.isNonNull()) {
      Attributes.add(Attr.get());
    }
    break;
  }

  case DAK_Differentiable: {
    auto Attr = parseDifferentiableAttribute(AtLoc, Loc);
    if (Attr.isNonNull())
      Attributes.add(Attr.get());
    break;
  }

  case DAK_Derivative: {
    // `@derivative` in a local scope is not allowed.
    if (CurDeclContext->isLocalContext())
      diagnose(Loc, diag::attr_only_at_non_local_scope, '@' + AttrName.str());

    auto Attr = parseDerivativeAttribute(AtLoc, Loc);
    if (Attr.isNonNull())
      Attributes.add(Attr.get());
    break;
  }

  case DAK_Transpose: {
    // `@transpose` in a local scope is not allowed.
    if (CurDeclContext->isLocalContext())
      diagnose(Loc, diag::attr_only_at_non_local_scope, '@' + AttrName.str());

    auto Attr = parseTransposeAttribute(AtLoc, Loc);
    if (Attr.isNonNull())
      Attributes.add(Attr.get());
    break;
  }

  case DAK_ProjectedValueProperty: {
    if (!consumeIf(tok::l_paren)) {
      diagnose(Loc, diag::attr_expected_lparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    if (Tok.isNot(tok::identifier)) {
      diagnose(Loc, diag::projection_value_property_not_identifier);
      return false;
    }

    Identifier name;
    consumeIdentifier(name, /*diagnoseDollarPrefix=*/false);

    auto range = SourceRange(Loc, Tok.getRange().getStart());

    if (!consumeIf(tok::r_paren)) {
      diagnose(Loc, diag::attr_expected_rparen, AttrName,
               DeclAttribute::isDeclModifier(DK));
      return false;
    }

    Attributes.add(new (Context) ProjectedValuePropertyAttr(
        name, AtLoc, range, /*implicit*/ false));
    break;
  }
  case DAK_TypeSequence: {
    auto range = SourceRange(Loc, Tok.getRange().getStart());
    Attributes.add(TypeSequenceAttr::create(Context, AtLoc, range));
    break;
  }

  case DAK_UnavailableFromAsync: {
    StringRef message;
    if (consumeIf(tok::l_paren)) {
      if (!Tok.is(tok::identifier)) {
        llvm_unreachable("Flag must start with an indentifier");
      }

      StringRef flag = Tok.getText();

      if (flag != "message") {
        diagnose(Tok.getLoc(), diag::attr_unknown_option, flag, AttrName);
        return true;
      }
      consumeToken();
      if (!consumeIf(tok::colon)) {
        if (!Tok.is(tok::equal)) {
          diagnose(Tok.getLoc(), diag::attr_expected_colon_after_label, flag);
          return false;
        }
        diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value)
          .fixItReplace(Tok.getLoc(), ": ");
        consumeToken();
      }
      if (!Tok.is(tok::string_literal)) {
        diagnose(Tok.getLoc(), diag::attr_expected_string_literal, AttrName);
        return false;
      }

      Optional<StringRef> value = getStringLiteralIfNotInterpolated(
          Tok.getLoc(), flag);
      if (!value)
        return false;
      Token stringTok = Tok;
      consumeToken();
      message = *value;

      if (!consumeIf(tok::r_paren))
        diagnose(stringTok.getRange().getEnd(), diag::attr_expected_rparen,
            AttrName, /*isModifiler*/false)
          .fixItInsertAfter(stringTok.getLoc(), ")");
    }

    Attributes.add(new (Context) UnavailableFromAsyncAttr(
        message, AtLoc, SourceRange(Loc, Tok.getLoc()), false));
    break;
  }
  }

  if (DuplicateAttribute) {
    diagnose(Loc, diag::duplicate_attribute, DeclAttribute::isDeclModifier(DK))
      .highlight(AttrRange);
    diagnose(DuplicateAttribute->getLocation(),
             diag::previous_attribute,
             DeclAttribute::isDeclModifier(DK))
      .highlight(DuplicateAttribute->getRange());
  }

  // If this is a decl modifier spelled with an @, emit an error and remove it
  // with a fixit.
  if (AtLoc.isValid() && DeclAttribute::isDeclModifier(DK))
    diagnose(AtLoc, diag::cskeyword_not_attribute, AttrName).fixItRemove(AtLoc);
  
  return false;
}