void Parser::ParseDeclarationSpecifiers()

in clang/lib/Parse/ParseDecl.cpp [3064:4218]


void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
                                        const ParsedTemplateInfo &TemplateInfo,
                                        AccessSpecifier AS,
                                        DeclSpecContext DSContext,
                                        LateParsedAttrList *LateAttrs) {
  if (DS.getSourceRange().isInvalid()) {
    // Start the range at the current token but make the end of the range
    // invalid.  This will make the entire range invalid unless we successfully
    // consume a token.
    DS.SetRangeStart(Tok.getLocation());
    DS.SetRangeEnd(SourceLocation());
  }

  bool EnteringContext = (DSContext == DeclSpecContext::DSC_class ||
                          DSContext == DeclSpecContext::DSC_top_level);
  bool AttrsLastTime = false;
  ParsedAttributesWithRange attrs(AttrFactory);
  // We use Sema's policy to get bool macros right.
  PrintingPolicy Policy = Actions.getPrintingPolicy();
  while (true) {
    bool isInvalid = false;
    bool isStorageClass = false;
    const char *PrevSpec = nullptr;
    unsigned DiagID = 0;

    // This value needs to be set to the location of the last token if the last
    // token of the specifier is already consumed.
    SourceLocation ConsumedEnd;

    // HACK: MSVC doesn't consider _Atomic to be a keyword and its STL
    // implementation for VS2013 uses _Atomic as an identifier for one of the
    // classes in <atomic>.
    //
    // A typedef declaration containing _Atomic<...> is among the places where
    // the class is used.  If we are currently parsing such a declaration, treat
    // the token as an identifier.
    if (getLangOpts().MSVCCompat && Tok.is(tok::kw__Atomic) &&
        DS.getStorageClassSpec() == clang::DeclSpec::SCS_typedef &&
        !DS.hasTypeSpecifier() && GetLookAheadToken(1).is(tok::less))
      Tok.setKind(tok::identifier);

    SourceLocation Loc = Tok.getLocation();

    // Helper for image types in OpenCL.
    auto handleOpenCLImageKW = [&] (StringRef Ext, TypeSpecifierType ImageTypeSpec) {
      // Check if the image type is supported and otherwise turn the keyword into an identifier
      // because image types from extensions are not reserved identifiers.
      if (!StringRef(Ext).empty() && !getActions().getOpenCLOptions().isSupported(Ext, getLangOpts())) {
        Tok.getIdentifierInfo()->revertTokenIDToIdentifier();
        Tok.setKind(tok::identifier);
        return false;
      }
      isInvalid = DS.SetTypeSpecType(ImageTypeSpec, Loc, PrevSpec, DiagID, Policy);
      return true;
    };

    // Turn off usual access checking for template specializations and
    // instantiations.
    bool IsTemplateSpecOrInst =
        (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
         TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);

    switch (Tok.getKind()) {
    default:
    DoneWithDeclSpec:
      if (!AttrsLastTime)
        ProhibitAttributes(attrs);
      else {
        // Reject C++11 attributes that appertain to decl specifiers as
        // we don't support any C++11 attributes that appertain to decl
        // specifiers. This also conforms to what g++ 4.8 is doing.
        ProhibitCXX11Attributes(attrs, diag::err_attribute_not_type_attr);

        DS.takeAttributesFrom(attrs);
      }

      // If this is not a declaration specifier token, we're done reading decl
      // specifiers.  First verify that DeclSpec's are consistent.
      DS.Finish(Actions, Policy);
      return;

    case tok::l_square:
    case tok::kw_alignas:
      if (!standardAttributesAllowed() || !isCXX11AttributeSpecifier())
        goto DoneWithDeclSpec;

      ProhibitAttributes(attrs);
      // FIXME: It would be good to recover by accepting the attributes,
      //        but attempting to do that now would cause serious
      //        madness in terms of diagnostics.
      attrs.clear();
      attrs.Range = SourceRange();

      ParseCXX11Attributes(attrs);
      AttrsLastTime = true;
      continue;

    case tok::code_completion: {
      Sema::ParserCompletionContext CCC = Sema::PCC_Namespace;
      if (DS.hasTypeSpecifier()) {
        bool AllowNonIdentifiers
          = (getCurScope()->getFlags() & (Scope::ControlScope |
                                          Scope::BlockScope |
                                          Scope::TemplateParamScope |
                                          Scope::FunctionPrototypeScope |
                                          Scope::AtCatchScope)) == 0;
        bool AllowNestedNameSpecifiers
          = DSContext == DeclSpecContext::DSC_top_level ||
            (DSContext == DeclSpecContext::DSC_class && DS.isFriendSpecified());

        cutOffParsing();
        Actions.CodeCompleteDeclSpec(getCurScope(), DS,
                                     AllowNonIdentifiers,
                                     AllowNestedNameSpecifiers);
        return;
      }

      if (getCurScope()->getFnParent() || getCurScope()->getBlockParent())
        CCC = Sema::PCC_LocalDeclarationSpecifiers;
      else if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate)
        CCC = DSContext == DeclSpecContext::DSC_class ? Sema::PCC_MemberTemplate
                                                      : Sema::PCC_Template;
      else if (DSContext == DeclSpecContext::DSC_class)
        CCC = Sema::PCC_Class;
      else if (CurParsedObjCImpl)
        CCC = Sema::PCC_ObjCImplementation;

      cutOffParsing();
      Actions.CodeCompleteOrdinaryName(getCurScope(), CCC);
      return;
    }

    case tok::coloncolon: // ::foo::bar
      // C++ scope specifier.  Annotate and loop, or bail out on error.
      if (TryAnnotateCXXScopeToken(EnteringContext)) {
        if (!DS.hasTypeSpecifier())
          DS.SetTypeSpecError();
        goto DoneWithDeclSpec;
      }
      if (Tok.is(tok::coloncolon)) // ::new or ::delete
        goto DoneWithDeclSpec;
      continue;

    case tok::annot_cxxscope: {
      if (DS.hasTypeSpecifier() || DS.isTypeAltiVecVector())
        goto DoneWithDeclSpec;

      CXXScopeSpec SS;
      Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
                                                   Tok.getAnnotationRange(),
                                                   SS);

      // We are looking for a qualified typename.
      Token Next = NextToken();

      TemplateIdAnnotation *TemplateId = Next.is(tok::annot_template_id)
                                             ? takeTemplateIdAnnotation(Next)
                                             : nullptr;
      if (TemplateId && TemplateId->hasInvalidName()) {
        // We found something like 'T::U<Args> x', but U is not a template.
        // Assume it was supposed to be a type.
        DS.SetTypeSpecError();
        ConsumeAnnotationToken();
        break;
      }

      if (TemplateId && TemplateId->Kind == TNK_Type_template) {
        // We have a qualified template-id, e.g., N::A<int>

        // If this would be a valid constructor declaration with template
        // arguments, we will reject the attempt to form an invalid type-id
        // referring to the injected-class-name when we annotate the token,
        // per C++ [class.qual]p2.
        //
        // To improve diagnostics for this case, parse the declaration as a
        // constructor (and reject the extra template arguments later).
        if ((DSContext == DeclSpecContext::DSC_top_level ||
             DSContext == DeclSpecContext::DSC_class) &&
            TemplateId->Name &&
            Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS) &&
            isConstructorDeclarator(/*Unqualified=*/false)) {
          // The user meant this to be an out-of-line constructor
          // definition, but template arguments are not allowed
          // there.  Just allow this as a constructor; we'll
          // complain about it later.
          goto DoneWithDeclSpec;
        }

        DS.getTypeSpecScope() = SS;
        ConsumeAnnotationToken(); // The C++ scope.
        assert(Tok.is(tok::annot_template_id) &&
               "ParseOptionalCXXScopeSpecifier not working");
        AnnotateTemplateIdTokenAsType(SS);
        continue;
      }

      if (TemplateId && TemplateId->Kind == TNK_Concept_template &&
          GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype)) {
        DS.getTypeSpecScope() = SS;
        // This is a qualified placeholder-specifier, e.g., ::C<int> auto ...
        // Consume the scope annotation and continue to consume the template-id
        // as a placeholder-specifier.
        ConsumeAnnotationToken();
        continue;
      }

      if (Next.is(tok::annot_typename)) {
        DS.getTypeSpecScope() = SS;
        ConsumeAnnotationToken(); // The C++ scope.
        TypeResult T = getTypeAnnotation(Tok);
        isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename,
                                       Tok.getAnnotationEndLoc(),
                                       PrevSpec, DiagID, T, Policy);
        if (isInvalid)
          break;
        DS.SetRangeEnd(Tok.getAnnotationEndLoc());
        ConsumeAnnotationToken(); // The typename
      }

      if (Next.isNot(tok::identifier))
        goto DoneWithDeclSpec;

      // Check whether this is a constructor declaration. If we're in a
      // context where the identifier could be a class name, and it has the
      // shape of a constructor declaration, process it as one.
      if ((DSContext == DeclSpecContext::DSC_top_level ||
           DSContext == DeclSpecContext::DSC_class) &&
          Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(),
                                     &SS) &&
          isConstructorDeclarator(/*Unqualified*/ false))
        goto DoneWithDeclSpec;

      // C++20 [temp.spec] 13.9/6.
      // This disables the access checking rules for function template explicit
      // instantiation and explicit specialization:
      // - `return type`.
      SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);

      ParsedType TypeRep =
          Actions.getTypeName(*Next.getIdentifierInfo(), Next.getLocation(),
                              getCurScope(), &SS, false, false, nullptr,
                              /*IsCtorOrDtorName=*/false,
                              /*WantNontrivialTypeSourceInfo=*/true,
                              isClassTemplateDeductionContext(DSContext));

      if (IsTemplateSpecOrInst)
        SAC.done();

      // If the referenced identifier is not a type, then this declspec is
      // erroneous: We already checked about that it has no type specifier, and
      // C++ doesn't have implicit int.  Diagnose it as a typo w.r.t. to the
      // typename.
      if (!TypeRep) {
        if (TryAnnotateTypeConstraint())
          goto DoneWithDeclSpec;
        if (Tok.isNot(tok::annot_cxxscope) ||
            NextToken().isNot(tok::identifier))
          continue;
        // Eat the scope spec so the identifier is current.
        ConsumeAnnotationToken();
        ParsedAttributesWithRange Attrs(AttrFactory);
        if (ParseImplicitInt(DS, &SS, TemplateInfo, AS, DSContext, Attrs)) {
          if (!Attrs.empty()) {
            AttrsLastTime = true;
            attrs.takeAllFrom(Attrs);
          }
          continue;
        }
        goto DoneWithDeclSpec;
      }

      DS.getTypeSpecScope() = SS;
      ConsumeAnnotationToken(); // The C++ scope.

      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
                                     DiagID, TypeRep, Policy);
      if (isInvalid)
        break;

      DS.SetRangeEnd(Tok.getLocation());
      ConsumeToken(); // The typename.

      continue;
    }

    case tok::annot_typename: {
      // If we've previously seen a tag definition, we were almost surely
      // missing a semicolon after it.
      if (DS.hasTypeSpecifier() && DS.hasTagDefinition())
        goto DoneWithDeclSpec;

      TypeResult T = getTypeAnnotation(Tok);
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
                                     DiagID, T, Policy);
      if (isInvalid)
        break;

      DS.SetRangeEnd(Tok.getAnnotationEndLoc());
      ConsumeAnnotationToken(); // The typename

      continue;
    }

    case tok::kw___is_signed:
      // GNU libstdc++ 4.4 uses __is_signed as an identifier, but Clang
      // typically treats it as a trait. If we see __is_signed as it appears
      // in libstdc++, e.g.,
      //
      //   static const bool __is_signed;
      //
      // then treat __is_signed as an identifier rather than as a keyword.
      if (DS.getTypeSpecType() == TST_bool &&
          DS.getTypeQualifiers() == DeclSpec::TQ_const &&
          DS.getStorageClassSpec() == DeclSpec::SCS_static)
        TryKeywordIdentFallback(true);

      // We're done with the declaration-specifiers.
      goto DoneWithDeclSpec;

      // typedef-name
    case tok::kw___super:
    case tok::kw_decltype:
    case tok::identifier: {
      // This identifier can only be a typedef name if we haven't already seen
      // a type-specifier.  Without this check we misparse:
      //  typedef int X; struct Y { short X; };  as 'short int'.
      if (DS.hasTypeSpecifier())
        goto DoneWithDeclSpec;

      // If the token is an identifier named "__declspec" and Microsoft
      // extensions are not enabled, it is likely that there will be cascading
      // parse errors if this really is a __declspec attribute. Attempt to
      // recognize that scenario and recover gracefully.
      if (!getLangOpts().DeclSpecKeyword && Tok.is(tok::identifier) &&
          Tok.getIdentifierInfo()->getName().equals("__declspec")) {
        Diag(Loc, diag::err_ms_attributes_not_enabled);

        // The next token should be an open paren. If it is, eat the entire
        // attribute declaration and continue.
        if (NextToken().is(tok::l_paren)) {
          // Consume the __declspec identifier.
          ConsumeToken();

          // Eat the parens and everything between them.
          BalancedDelimiterTracker T(*this, tok::l_paren);
          if (T.consumeOpen()) {
            assert(false && "Not a left paren?");
            return;
          }
          T.skipToEnd();
          continue;
        }
      }

      // In C++, check to see if this is a scope specifier like foo::bar::, if
      // so handle it as such.  This is important for ctor parsing.
      if (getLangOpts().CPlusPlus) {
        // C++20 [temp.spec] 13.9/6.
        // This disables the access checking rules for function template
        // explicit instantiation and explicit specialization:
        // - `return type`.
        SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);

        const bool Success = TryAnnotateCXXScopeToken(EnteringContext);

        if (IsTemplateSpecOrInst)
          SAC.done();

        if (Success) {
          if (IsTemplateSpecOrInst)
            SAC.redelay();
          DS.SetTypeSpecError();
          goto DoneWithDeclSpec;
        }

        if (!Tok.is(tok::identifier))
          continue;
      }

      // Check for need to substitute AltiVec keyword tokens.
      if (TryAltiVecToken(DS, Loc, PrevSpec, DiagID, isInvalid))
        break;

      // [AltiVec] 2.2: [If the 'vector' specifier is used] The syntax does not
      //                allow the use of a typedef name as a type specifier.
      if (DS.isTypeAltiVecVector())
        goto DoneWithDeclSpec;

      if (DSContext == DeclSpecContext::DSC_objc_method_result &&
          isObjCInstancetype()) {
        ParsedType TypeRep = Actions.ActOnObjCInstanceType(Loc);
        assert(TypeRep);
        isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
                                       DiagID, TypeRep, Policy);
        if (isInvalid)
          break;

        DS.SetRangeEnd(Loc);
        ConsumeToken();
        continue;
      }

      // If we're in a context where the identifier could be a class name,
      // check whether this is a constructor declaration.
      if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class &&
          Actions.isCurrentClassName(*Tok.getIdentifierInfo(), getCurScope()) &&
          isConstructorDeclarator(/*Unqualified*/true))
        goto DoneWithDeclSpec;

      ParsedType TypeRep = Actions.getTypeName(
          *Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), nullptr,
          false, false, nullptr, false, false,
          isClassTemplateDeductionContext(DSContext));

      // If this is not a typedef name, don't parse it as part of the declspec,
      // it must be an implicit int or an error.
      if (!TypeRep) {
        if (TryAnnotateTypeConstraint())
          goto DoneWithDeclSpec;
        if (Tok.isNot(tok::identifier))
          continue;
        ParsedAttributesWithRange Attrs(AttrFactory);
        if (ParseImplicitInt(DS, nullptr, TemplateInfo, AS, DSContext, Attrs)) {
          if (!Attrs.empty()) {
            AttrsLastTime = true;
            attrs.takeAllFrom(Attrs);
          }
          continue;
        }
        goto DoneWithDeclSpec;
      }

      // Likewise, if this is a context where the identifier could be a template
      // name, check whether this is a deduction guide declaration.
      if (getLangOpts().CPlusPlus17 &&
          (DSContext == DeclSpecContext::DSC_class ||
           DSContext == DeclSpecContext::DSC_top_level) &&
          Actions.isDeductionGuideName(getCurScope(), *Tok.getIdentifierInfo(),
                                       Tok.getLocation()) &&
          isConstructorDeclarator(/*Unqualified*/ true,
                                  /*DeductionGuide*/ true))
        goto DoneWithDeclSpec;

      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
                                     DiagID, TypeRep, Policy);
      if (isInvalid)
        break;

      DS.SetRangeEnd(Tok.getLocation());
      ConsumeToken(); // The identifier

      // Objective-C supports type arguments and protocol references
      // following an Objective-C object or object pointer
      // type. Handle either one of them.
      if (Tok.is(tok::less) && getLangOpts().ObjC) {
        SourceLocation NewEndLoc;
        TypeResult NewTypeRep = parseObjCTypeArgsAndProtocolQualifiers(
                                  Loc, TypeRep, /*consumeLastToken=*/true,
                                  NewEndLoc);
        if (NewTypeRep.isUsable()) {
          DS.UpdateTypeRep(NewTypeRep.get());
          DS.SetRangeEnd(NewEndLoc);
        }
      }

      // Need to support trailing type qualifiers (e.g. "id<p> const").
      // If a type specifier follows, it will be diagnosed elsewhere.
      continue;
    }

      // type-name or placeholder-specifier
    case tok::annot_template_id: {
      TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);

      if (TemplateId->hasInvalidName()) {
        DS.SetTypeSpecError();
        break;
      }

      if (TemplateId->Kind == TNK_Concept_template) {
        // If we've already diagnosed that this type-constraint has invalid
        // arguemnts, drop it and just form 'auto' or 'decltype(auto)'.
        if (TemplateId->hasInvalidArgs())
          TemplateId = nullptr;

        if (NextToken().is(tok::identifier)) {
          Diag(Loc, diag::err_placeholder_expected_auto_or_decltype_auto)
              << FixItHint::CreateInsertion(NextToken().getLocation(), "auto");
          // Attempt to continue as if 'auto' was placed here.
          isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID,
                                         TemplateId, Policy);
          break;
        }
        if (!NextToken().isOneOf(tok::kw_auto, tok::kw_decltype))
            goto DoneWithDeclSpec;
        ConsumeAnnotationToken();
        SourceLocation AutoLoc = Tok.getLocation();
        if (TryConsumeToken(tok::kw_decltype)) {
          BalancedDelimiterTracker Tracker(*this, tok::l_paren);
          if (Tracker.consumeOpen()) {
            // Something like `void foo(Iterator decltype i)`
            Diag(Tok, diag::err_expected) << tok::l_paren;
          } else {
            if (!TryConsumeToken(tok::kw_auto)) {
              // Something like `void foo(Iterator decltype(int) i)`
              Tracker.skipToEnd();
              Diag(Tok, diag::err_placeholder_expected_auto_or_decltype_auto)
                << FixItHint::CreateReplacement(SourceRange(AutoLoc,
                                                            Tok.getLocation()),
                                                "auto");
            } else {
              Tracker.consumeClose();
            }
          }
          ConsumedEnd = Tok.getLocation();
          // Even if something went wrong above, continue as if we've seen
          // `decltype(auto)`.
          isInvalid = DS.SetTypeSpecType(TST_decltype_auto, Loc, PrevSpec,
                                         DiagID, TemplateId, Policy);
        } else {
          isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID,
                                         TemplateId, Policy);
        }
        break;
      }

      if (TemplateId->Kind != TNK_Type_template &&
          TemplateId->Kind != TNK_Undeclared_template) {
        // This template-id does not refer to a type name, so we're
        // done with the type-specifiers.
        goto DoneWithDeclSpec;
      }

      // If we're in a context where the template-id could be a
      // constructor name or specialization, check whether this is a
      // constructor declaration.
      if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class &&
          Actions.isCurrentClassName(*TemplateId->Name, getCurScope()) &&
          isConstructorDeclarator(/*Unqualified=*/true))
        goto DoneWithDeclSpec;

      // Turn the template-id annotation token into a type annotation
      // token, then try again to parse it as a type-specifier.
      CXXScopeSpec SS;
      AnnotateTemplateIdTokenAsType(SS);
      continue;
    }

    // Attributes support.
    case tok::kw___attribute:
    case tok::kw___declspec:
      ParseAttributes(PAKM_GNU | PAKM_Declspec, DS.getAttributes(), nullptr,
                      LateAttrs);
      continue;

    // Microsoft single token adornments.
    case tok::kw___forceinline: {
      isInvalid = DS.setFunctionSpecForceInline(Loc, PrevSpec, DiagID);
      IdentifierInfo *AttrName = Tok.getIdentifierInfo();
      SourceLocation AttrNameLoc = Tok.getLocation();
      DS.getAttributes().addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc,
                                nullptr, 0, ParsedAttr::AS_Keyword);
      break;
    }

    case tok::kw___unaligned:
      isInvalid = DS.SetTypeQual(DeclSpec::TQ_unaligned, Loc, PrevSpec, DiagID,
                                 getLangOpts());
      break;

    case tok::kw___sptr:
    case tok::kw___uptr:
    case tok::kw___ptr64:
    case tok::kw___ptr32:
    case tok::kw___w64:
    case tok::kw___cdecl:
    case tok::kw___stdcall:
    case tok::kw___fastcall:
    case tok::kw___thiscall:
    case tok::kw___regcall:
    case tok::kw___vectorcall:
      ParseMicrosoftTypeAttributes(DS.getAttributes());
      continue;

    // Borland single token adornments.
    case tok::kw___pascal:
      ParseBorlandTypeAttributes(DS.getAttributes());
      continue;

    // OpenCL single token adornments.
    case tok::kw___kernel:
      ParseOpenCLKernelAttributes(DS.getAttributes());
      continue;

    // Nullability type specifiers.
    case tok::kw__Nonnull:
    case tok::kw__Nullable:
    case tok::kw__Nullable_result:
    case tok::kw__Null_unspecified:
      ParseNullabilityTypeSpecifiers(DS.getAttributes());
      continue;

    // Objective-C 'kindof' types.
    case tok::kw___kindof:
      DS.getAttributes().addNew(Tok.getIdentifierInfo(), Loc, nullptr, Loc,
                                nullptr, 0, ParsedAttr::AS_Keyword);
      (void)ConsumeToken();
      continue;

    // storage-class-specifier
    case tok::kw_typedef:
      isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
                                         PrevSpec, DiagID, Policy);
      isStorageClass = true;
      break;
    case tok::kw_extern:
      if (DS.getThreadStorageClassSpec() == DeclSpec::TSCS___thread)
        Diag(Tok, diag::ext_thread_before) << "extern";
      isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_extern, Loc,
                                         PrevSpec, DiagID, Policy);
      isStorageClass = true;
      break;
    case tok::kw___private_extern__:
      isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_private_extern,
                                         Loc, PrevSpec, DiagID, Policy);
      isStorageClass = true;
      break;
    case tok::kw_static:
      if (DS.getThreadStorageClassSpec() == DeclSpec::TSCS___thread)
        Diag(Tok, diag::ext_thread_before) << "static";
      isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_static, Loc,
                                         PrevSpec, DiagID, Policy);
      isStorageClass = true;
      break;
    case tok::kw_auto:
      if (getLangOpts().CPlusPlus11) {
        if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) {
          isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
                                             PrevSpec, DiagID, Policy);
          if (!isInvalid)
            Diag(Tok, diag::ext_auto_storage_class)
              << FixItHint::CreateRemoval(DS.getStorageClassSpecLoc());
        } else
          isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec,
                                         DiagID, Policy);
      } else
        isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
                                           PrevSpec, DiagID, Policy);
      isStorageClass = true;
      break;
    case tok::kw___auto_type:
      Diag(Tok, diag::ext_auto_type);
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto_type, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_register:
      isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_register, Loc,
                                         PrevSpec, DiagID, Policy);
      isStorageClass = true;
      break;
    case tok::kw_mutable:
      isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_mutable, Loc,
                                         PrevSpec, DiagID, Policy);
      isStorageClass = true;
      break;
    case tok::kw___thread:
      isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS___thread, Loc,
                                               PrevSpec, DiagID);
      isStorageClass = true;
      break;
    case tok::kw_thread_local:
      isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS_thread_local, Loc,
                                               PrevSpec, DiagID);
      isStorageClass = true;
      break;
    case tok::kw__Thread_local:
      if (!getLangOpts().C11)
        Diag(Tok, diag::ext_c11_feature) << Tok.getName();
      isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS__Thread_local,
                                               Loc, PrevSpec, DiagID);
      isStorageClass = true;
      break;

    // function-specifier
    case tok::kw_inline:
      isInvalid = DS.setFunctionSpecInline(Loc, PrevSpec, DiagID);
      break;
    case tok::kw_virtual:
      // C++ for OpenCL does not allow virtual function qualifier, to avoid
      // function pointers restricted in OpenCL v2.0 s6.9.a.
      if (getLangOpts().OpenCLCPlusPlus &&
          !getActions().getOpenCLOptions().isAvailableOption(
              "__cl_clang_function_pointers", getLangOpts())) {
        DiagID = diag::err_openclcxx_virtual_function;
        PrevSpec = Tok.getIdentifierInfo()->getNameStart();
        isInvalid = true;
      } else {
        isInvalid = DS.setFunctionSpecVirtual(Loc, PrevSpec, DiagID);
      }
      break;
    case tok::kw_explicit: {
      SourceLocation ExplicitLoc = Loc;
      SourceLocation CloseParenLoc;
      ExplicitSpecifier ExplicitSpec(nullptr, ExplicitSpecKind::ResolvedTrue);
      ConsumedEnd = ExplicitLoc;
      ConsumeToken(); // kw_explicit
      if (Tok.is(tok::l_paren)) {
        if (getLangOpts().CPlusPlus20 || isExplicitBool() == TPResult::True) {
          Diag(Tok.getLocation(), getLangOpts().CPlusPlus20
                                      ? diag::warn_cxx17_compat_explicit_bool
                                      : diag::ext_explicit_bool);

          ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
          BalancedDelimiterTracker Tracker(*this, tok::l_paren);
          Tracker.consumeOpen();
          ExplicitExpr = ParseConstantExpression();
          ConsumedEnd = Tok.getLocation();
          if (ExplicitExpr.isUsable()) {
            CloseParenLoc = Tok.getLocation();
            Tracker.consumeClose();
            ExplicitSpec =
                Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
          } else
            Tracker.skipToEnd();
        } else {
          Diag(Tok.getLocation(), diag::warn_cxx20_compat_explicit_bool);
        }
      }
      isInvalid = DS.setFunctionSpecExplicit(ExplicitLoc, PrevSpec, DiagID,
                                             ExplicitSpec, CloseParenLoc);
      break;
    }
    case tok::kw__Noreturn:
      if (!getLangOpts().C11)
        Diag(Tok, diag::ext_c11_feature) << Tok.getName();
      isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
      break;

    // alignment-specifier
    case tok::kw__Alignas:
      if (!getLangOpts().C11)
        Diag(Tok, diag::ext_c11_feature) << Tok.getName();
      ParseAlignmentSpecifier(DS.getAttributes());
      continue;

    // friend
    case tok::kw_friend:
      if (DSContext == DeclSpecContext::DSC_class)
        isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
      else {
        PrevSpec = ""; // not actually used by the diagnostic
        DiagID = diag::err_friend_invalid_in_context;
        isInvalid = true;
      }
      break;

    // Modules
    case tok::kw___module_private__:
      isInvalid = DS.setModulePrivateSpec(Loc, PrevSpec, DiagID);
      break;

    // constexpr, consteval, constinit specifiers
    case tok::kw_constexpr:
      isInvalid = DS.SetConstexprSpec(ConstexprSpecKind::Constexpr, Loc,
                                      PrevSpec, DiagID);
      break;
    case tok::kw_consteval:
      isInvalid = DS.SetConstexprSpec(ConstexprSpecKind::Consteval, Loc,
                                      PrevSpec, DiagID);
      break;
    case tok::kw_constinit:
      isInvalid = DS.SetConstexprSpec(ConstexprSpecKind::Constinit, Loc,
                                      PrevSpec, DiagID);
      break;

    // type-specifier
    case tok::kw_short:
      isInvalid = DS.SetTypeSpecWidth(TypeSpecifierWidth::Short, Loc, PrevSpec,
                                      DiagID, Policy);
      break;
    case tok::kw_long:
      if (DS.getTypeSpecWidth() != TypeSpecifierWidth::Long)
        isInvalid = DS.SetTypeSpecWidth(TypeSpecifierWidth::Long, Loc, PrevSpec,
                                        DiagID, Policy);
      else
        isInvalid = DS.SetTypeSpecWidth(TypeSpecifierWidth::LongLong, Loc,
                                        PrevSpec, DiagID, Policy);
      break;
    case tok::kw___int64:
      isInvalid = DS.SetTypeSpecWidth(TypeSpecifierWidth::LongLong, Loc,
                                      PrevSpec, DiagID, Policy);
      break;
    case tok::kw_signed:
      isInvalid =
          DS.SetTypeSpecSign(TypeSpecifierSign::Signed, Loc, PrevSpec, DiagID);
      break;
    case tok::kw_unsigned:
      isInvalid = DS.SetTypeSpecSign(TypeSpecifierSign::Unsigned, Loc, PrevSpec,
                                     DiagID);
      break;
    case tok::kw__Complex:
      if (!getLangOpts().C99)
        Diag(Tok, diag::ext_c99_feature) << Tok.getName();
      isInvalid = DS.SetTypeSpecComplex(DeclSpec::TSC_complex, Loc, PrevSpec,
                                        DiagID);
      break;
    case tok::kw__Imaginary:
      if (!getLangOpts().C99)
        Diag(Tok, diag::ext_c99_feature) << Tok.getName();
      isInvalid = DS.SetTypeSpecComplex(DeclSpec::TSC_imaginary, Loc, PrevSpec,
                                        DiagID);
      break;
    case tok::kw_void:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_void, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_char:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_int:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw__ExtInt:
    case tok::kw__BitInt: {
      DiagnoseBitIntUse(Tok);
      ExprResult ER = ParseExtIntegerArgument();
      if (ER.isInvalid())
        continue;
      isInvalid = DS.SetBitIntType(Loc, ER.get(), PrevSpec, DiagID, Policy);
      ConsumedEnd = PrevTokLocation;
      break;
    }
    case tok::kw___int128:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_half:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw___bf16:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_BFloat16, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_float:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_double:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_double, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw__Float16:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float16, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw__Accum:
      if (!getLangOpts().FixedPoint) {
        SetupFixedPointError(getLangOpts(), PrevSpec, DiagID, isInvalid);
      } else {
        isInvalid = DS.SetTypeSpecType(DeclSpec::TST_accum, Loc, PrevSpec,
                                       DiagID, Policy);
      }
      break;
    case tok::kw__Fract:
      if (!getLangOpts().FixedPoint) {
        SetupFixedPointError(getLangOpts(), PrevSpec, DiagID, isInvalid);
      } else {
        isInvalid = DS.SetTypeSpecType(DeclSpec::TST_fract, Loc, PrevSpec,
                                       DiagID, Policy);
      }
      break;
    case tok::kw__Sat:
      if (!getLangOpts().FixedPoint) {
        SetupFixedPointError(getLangOpts(), PrevSpec, DiagID, isInvalid);
      } else {
        isInvalid = DS.SetTypeSpecSat(Loc, PrevSpec, DiagID);
      }
      break;
    case tok::kw___float128:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float128, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw___ibm128:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_ibm128, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_wchar_t:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_wchar, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_char8_t:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char8, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_char16_t:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char16, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_char32_t:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char32, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw_bool:
    case tok::kw__Bool:
      if (Tok.is(tok::kw__Bool) && !getLangOpts().C99)
        Diag(Tok, diag::ext_c99_feature) << Tok.getName();

      if (Tok.is(tok::kw_bool) &&
          DS.getTypeSpecType() != DeclSpec::TST_unspecified &&
          DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
        PrevSpec = ""; // Not used by the diagnostic.
        DiagID = diag::err_bool_redeclaration;
        // For better error recovery.
        Tok.setKind(tok::identifier);
        isInvalid = true;
      } else {
        isInvalid = DS.SetTypeSpecType(DeclSpec::TST_bool, Loc, PrevSpec,
                                       DiagID, Policy);
      }
      break;
    case tok::kw__Decimal32:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal32, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw__Decimal64:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal64, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw__Decimal128:
      isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal128, Loc, PrevSpec,
                                     DiagID, Policy);
      break;
    case tok::kw___vector:
      isInvalid = DS.SetTypeAltiVecVector(true, Loc, PrevSpec, DiagID, Policy);
      break;
    case tok::kw___pixel:
      isInvalid = DS.SetTypeAltiVecPixel(true, Loc, PrevSpec, DiagID, Policy);
      break;
    case tok::kw___bool:
      isInvalid = DS.SetTypeAltiVecBool(true, Loc, PrevSpec, DiagID, Policy);
      break;
    case tok::kw_pipe:
      if (!getLangOpts().OpenCL ||
          getLangOpts().getOpenCLCompatibleVersion() < 200) {
        // OpenCL 2.0 and later define this keyword. OpenCL 1.2 and earlier
        // should support the "pipe" word as identifier.
        Tok.getIdentifierInfo()->revertTokenIDToIdentifier();
        Tok.setKind(tok::identifier);
        goto DoneWithDeclSpec;
      } else if (!getLangOpts().OpenCLPipes) {
        DiagID = diag::err_opencl_unknown_type_specifier;
        PrevSpec = Tok.getIdentifierInfo()->getNameStart();
        isInvalid = true;
      } else
        isInvalid = DS.SetTypePipe(true, Loc, PrevSpec, DiagID, Policy);
      break;
// We only need to enumerate each image type once.
#define IMAGE_READ_WRITE_TYPE(Type, Id, Ext)
#define IMAGE_WRITE_TYPE(Type, Id, Ext)
#define IMAGE_READ_TYPE(ImgType, Id, Ext) \
    case tok::kw_##ImgType##_t: \
      if (!handleOpenCLImageKW(Ext, DeclSpec::TST_##ImgType##_t)) \
        goto DoneWithDeclSpec; \
      break;
#include "clang/Basic/OpenCLImageTypes.def"
    case tok::kw___unknown_anytype:
      isInvalid = DS.SetTypeSpecType(TST_unknown_anytype, Loc,
                                     PrevSpec, DiagID, Policy);
      break;

    // class-specifier:
    case tok::kw_class:
    case tok::kw_struct:
    case tok::kw___interface:
    case tok::kw_union: {
      tok::TokenKind Kind = Tok.getKind();
      ConsumeToken();

      // These are attributes following class specifiers.
      // To produce better diagnostic, we parse them when
      // parsing class specifier.
      ParsedAttributesWithRange Attributes(AttrFactory);
      ParseClassSpecifier(Kind, Loc, DS, TemplateInfo, AS,
                          EnteringContext, DSContext, Attributes);

      // If there are attributes following class specifier,
      // take them over and handle them here.
      if (!Attributes.empty()) {
        AttrsLastTime = true;
        attrs.takeAllFrom(Attributes);
      }
      continue;
    }

    // enum-specifier:
    case tok::kw_enum:
      ConsumeToken();
      ParseEnumSpecifier(Loc, DS, TemplateInfo, AS, DSContext);
      continue;

    // cv-qualifier:
    case tok::kw_const:
      isInvalid = DS.SetTypeQual(DeclSpec::TQ_const, Loc, PrevSpec, DiagID,
                                 getLangOpts());
      break;
    case tok::kw_volatile:
      isInvalid = DS.SetTypeQual(DeclSpec::TQ_volatile, Loc, PrevSpec, DiagID,
                                 getLangOpts());
      break;
    case tok::kw_restrict:
      isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID,
                                 getLangOpts());
      break;

    // C++ typename-specifier:
    case tok::kw_typename:
      if (TryAnnotateTypeOrScopeToken()) {
        DS.SetTypeSpecError();
        goto DoneWithDeclSpec;
      }
      if (!Tok.is(tok::kw_typename))
        continue;
      break;

    // GNU typeof support.
    case tok::kw_typeof:
      ParseTypeofSpecifier(DS);
      continue;

    case tok::annot_decltype:
      ParseDecltypeSpecifier(DS);
      continue;

    case tok::annot_pragma_pack:
      HandlePragmaPack();
      continue;

    case tok::annot_pragma_ms_pragma:
      HandlePragmaMSPragma();
      continue;

    case tok::annot_pragma_ms_vtordisp:
      HandlePragmaMSVtorDisp();
      continue;

    case tok::annot_pragma_ms_pointers_to_members:
      HandlePragmaMSPointersToMembers();
      continue;

    case tok::kw___underlying_type:
      ParseUnderlyingTypeSpecifier(DS);
      continue;

    case tok::kw__Atomic:
      // C11 6.7.2.4/4:
      //   If the _Atomic keyword is immediately followed by a left parenthesis,
      //   it is interpreted as a type specifier (with a type name), not as a
      //   type qualifier.
      if (!getLangOpts().C11)
        Diag(Tok, diag::ext_c11_feature) << Tok.getName();

      if (NextToken().is(tok::l_paren)) {
        ParseAtomicSpecifier(DS);
        continue;
      }
      isInvalid = DS.SetTypeQual(DeclSpec::TQ_atomic, Loc, PrevSpec, DiagID,
                                 getLangOpts());
      break;

    // OpenCL address space qualifiers:
    case tok::kw___generic:
      // generic address space is introduced only in OpenCL v2.0
      // see OpenCL C Spec v2.0 s6.5.5
      // OpenCL v3.0 introduces __opencl_c_generic_address_space
      // feature macro to indicate if generic address space is supported
      if (!Actions.getLangOpts().OpenCLGenericAddressSpace) {
        DiagID = diag::err_opencl_unknown_type_specifier;
        PrevSpec = Tok.getIdentifierInfo()->getNameStart();
        isInvalid = true;
        break;
      }
      LLVM_FALLTHROUGH;
    case tok::kw_private:
      // It's fine (but redundant) to check this for __generic on the
      // fallthrough path; we only form the __generic token in OpenCL mode.
      if (!getLangOpts().OpenCL)
        goto DoneWithDeclSpec;
      LLVM_FALLTHROUGH;
    case tok::kw___private:
    case tok::kw___global:
    case tok::kw___local:
    case tok::kw___constant:
    // OpenCL access qualifiers:
    case tok::kw___read_only:
    case tok::kw___write_only:
    case tok::kw___read_write:
      ParseOpenCLQualifiers(DS.getAttributes());
      break;

    case tok::less:
      // GCC ObjC supports types like "<SomeProtocol>" as a synonym for
      // "id<SomeProtocol>".  This is hopelessly old fashioned and dangerous,
      // but we support it.
      if (DS.hasTypeSpecifier() || !getLangOpts().ObjC)
        goto DoneWithDeclSpec;

      SourceLocation StartLoc = Tok.getLocation();
      SourceLocation EndLoc;
      TypeResult Type = parseObjCProtocolQualifierType(EndLoc);
      if (Type.isUsable()) {
        if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, StartLoc,
                               PrevSpec, DiagID, Type.get(),
                               Actions.getASTContext().getPrintingPolicy()))
          Diag(StartLoc, DiagID) << PrevSpec;

        DS.SetRangeEnd(EndLoc);
      } else {
        DS.SetTypeSpecError();
      }

      // Need to support trailing type qualifiers (e.g. "id<p> const").
      // If a type specifier follows, it will be diagnosed elsewhere.
      continue;
    }

    DS.SetRangeEnd(ConsumedEnd.isValid() ? ConsumedEnd : Tok.getLocation());

    // If the specifier wasn't legal, issue a diagnostic.
    if (isInvalid) {
      assert(PrevSpec && "Method did not return previous specifier!");
      assert(DiagID);

      if (DiagID == diag::ext_duplicate_declspec ||
          DiagID == diag::ext_warn_duplicate_declspec ||
          DiagID == diag::err_duplicate_declspec)
        Diag(Loc, DiagID) << PrevSpec
                          << FixItHint::CreateRemoval(
                                 SourceRange(Loc, DS.getEndLoc()));
      else if (DiagID == diag::err_opencl_unknown_type_specifier) {
        Diag(Loc, DiagID) << getLangOpts().getOpenCLVersionString() << PrevSpec
                          << isStorageClass;
      } else
        Diag(Loc, DiagID) << PrevSpec;
    }

    if (DiagID != diag::err_bool_redeclaration && ConsumedEnd.isInvalid())
      // After an error the next token can be an annotation token.
      ConsumeAnyToken();

    AttrsLastTime = false;
  }
}