aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/lib/Parse
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2024-03-13 13:58:24 +0300
committerthegeorg <thegeorg@yandex-team.com>2024-03-13 14:11:53 +0300
commit11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch)
treefabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/lib/Parse
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/lib/Parse')
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseAST.cpp214
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseCXXInlineMethods.cpp1386
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseDecl.cpp7823
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseDeclCXX.cpp4850
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseExpr.cpp3782
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseExprCXX.cpp4099
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseHLSL.cpp200
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseInit.cpp610
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseObjc.cpp3756
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseOpenMP.cpp4720
-rw-r--r--contrib/libs/clang16/lib/Parse/ParsePragma.cpp4037
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseStmt.cpp2741
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseStmtAsm.cpp963
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseTemplate.cpp1894
-rw-r--r--contrib/libs/clang16/lib/Parse/ParseTentative.cpp2253
-rw-r--r--contrib/libs/clang16/lib/Parse/Parser.cpp2735
-rw-r--r--contrib/libs/clang16/lib/Parse/ya.make51
17 files changed, 46114 insertions, 0 deletions
diff --git a/contrib/libs/clang16/lib/Parse/ParseAST.cpp b/contrib/libs/clang16/lib/Parse/ParseAST.cpp
new file mode 100644
index 0000000000..f442b62138
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseAST.cpp
@@ -0,0 +1,214 @@
+//===--- ParseAST.cpp - Provide the clang::ParseAST method ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the clang::ParseAST method.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Parse/ParseAST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ExternalASTSource.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaConsumer.h"
+#include "clang/Sema/TemplateInstCallback.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/TimeProfiler.h"
+#include <cstdio>
+#include <memory>
+
+using namespace clang;
+
+namespace {
+
+/// Resets LLVM's pretty stack state so that stack traces are printed correctly
+/// when there are nested CrashRecoveryContexts and the inner one recovers from
+/// a crash.
+class ResetStackCleanup
+ : public llvm::CrashRecoveryContextCleanupBase<ResetStackCleanup,
+ const void> {
+public:
+ ResetStackCleanup(llvm::CrashRecoveryContext *Context, const void *Top)
+ : llvm::CrashRecoveryContextCleanupBase<ResetStackCleanup, const void>(
+ Context, Top) {}
+ void recoverResources() override {
+ llvm::RestorePrettyStackState(resource);
+ }
+};
+
+/// If a crash happens while the parser is active, an entry is printed for it.
+class PrettyStackTraceParserEntry : public llvm::PrettyStackTraceEntry {
+ const Parser &P;
+public:
+ PrettyStackTraceParserEntry(const Parser &p) : P(p) {}
+ void print(raw_ostream &OS) const override;
+};
+
+/// If a crash happens while the parser is active, print out a line indicating
+/// what the current token is.
+void PrettyStackTraceParserEntry::print(raw_ostream &OS) const {
+ const Token &Tok = P.getCurToken();
+ if (Tok.is(tok::eof)) {
+ OS << "<eof> parser at end of file\n";
+ return;
+ }
+
+ if (Tok.getLocation().isInvalid()) {
+ OS << "<unknown> parser at unknown location\n";
+ return;
+ }
+
+ const Preprocessor &PP = P.getPreprocessor();
+ Tok.getLocation().print(OS, PP.getSourceManager());
+ if (Tok.isAnnotation()) {
+ OS << ": at annotation token\n";
+ } else {
+ // Do the equivalent of PP.getSpelling(Tok) except for the parts that would
+ // allocate memory.
+ bool Invalid = false;
+ const SourceManager &SM = P.getPreprocessor().getSourceManager();
+ unsigned Length = Tok.getLength();
+ const char *Spelling = SM.getCharacterData(Tok.getLocation(), &Invalid);
+ if (Invalid) {
+ OS << ": unknown current parser token\n";
+ return;
+ }
+ OS << ": current parser token '" << StringRef(Spelling, Length) << "'\n";
+ }
+}
+
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// Public interface to the file
+//===----------------------------------------------------------------------===//
+
+/// ParseAST - Parse the entire file specified, notifying the ASTConsumer as
+/// the file is parsed. This inserts the parsed decls into the translation unit
+/// held by Ctx.
+///
+void clang::ParseAST(Preprocessor &PP, ASTConsumer *Consumer,
+ ASTContext &Ctx, bool PrintStats,
+ TranslationUnitKind TUKind,
+ CodeCompleteConsumer *CompletionConsumer,
+ bool SkipFunctionBodies) {
+
+ std::unique_ptr<Sema> S(
+ new Sema(PP, Ctx, *Consumer, TUKind, CompletionConsumer));
+
+ // Recover resources if we crash before exiting this method.
+ llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(S.get());
+
+ ParseAST(*S.get(), PrintStats, SkipFunctionBodies);
+}
+
+void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
+ // Collect global stats on Decls/Stmts (until we have a module streamer).
+ if (PrintStats) {
+ Decl::EnableStatistics();
+ Stmt::EnableStatistics();
+ }
+
+ // Also turn on collection of stats inside of the Sema object.
+ bool OldCollectStats = PrintStats;
+ std::swap(OldCollectStats, S.CollectStats);
+
+ // Initialize the template instantiation observer chain.
+ // FIXME: See note on "finalize" below.
+ initialize(S.TemplateInstCallbacks, S);
+
+ ASTConsumer *Consumer = &S.getASTConsumer();
+
+ std::unique_ptr<Parser> ParseOP(
+ new Parser(S.getPreprocessor(), S, SkipFunctionBodies));
+ Parser &P = *ParseOP.get();
+
+ llvm::CrashRecoveryContextCleanupRegistrar<const void, ResetStackCleanup>
+ CleanupPrettyStack(llvm::SavePrettyStackState());
+ PrettyStackTraceParserEntry CrashInfo(P);
+
+ // Recover resources if we crash before exiting this method.
+ llvm::CrashRecoveryContextCleanupRegistrar<Parser>
+ CleanupParser(ParseOP.get());
+
+ S.getPreprocessor().EnterMainSourceFile();
+ ExternalASTSource *External = S.getASTContext().getExternalSource();
+ if (External)
+ External->StartTranslationUnit(Consumer);
+
+ // If a PCH through header is specified that does not have an include in
+ // the source, or a PCH is being created with #pragma hdrstop with nothing
+ // after the pragma, there won't be any tokens or a Lexer.
+ bool HaveLexer = S.getPreprocessor().getCurrentLexer();
+
+ if (HaveLexer) {
+ llvm::TimeTraceScope TimeScope("Frontend");
+ P.Initialize();
+ Parser::DeclGroupPtrTy ADecl;
+ Sema::ModuleImportState ImportState;
+ EnterExpressionEvaluationContext PotentiallyEvaluated(
+ S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
+
+ for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;
+ AtEOF = P.ParseTopLevelDecl(ADecl, ImportState)) {
+ // If we got a null return and something *was* parsed, ignore it. This
+ // is due to a top-level semicolon, an action override, or a parse error
+ // skipping something.
+ if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
+ return;
+ }
+ }
+
+ // Process any TopLevelDecls generated by #pragma weak.
+ for (Decl *D : S.WeakTopLevelDecls())
+ Consumer->HandleTopLevelDecl(DeclGroupRef(D));
+
+ // For C++20 modules, the codegen for module initializers needs to be altered
+ // and to be able to use a name based on the module name.
+
+ // At this point, we should know if we are building a non-header C++20 module.
+ if (S.getLangOpts().CPlusPlusModules) {
+ // If we are building the module from source, then the top level module
+ // will be here.
+ Module *CodegenModule = S.getCurrentModule();
+ bool Interface = true;
+ if (CodegenModule)
+ // We only use module initializers for importable module (including
+ // partition implementation units).
+ Interface = S.currentModuleIsInterface();
+ else if (S.getLangOpts().isCompilingModuleInterface())
+ // If we are building the module from a PCM file, then the module can be
+ // found here.
+ CodegenModule = S.getPreprocessor().getCurrentModule();
+
+ if (Interface && CodegenModule)
+ S.getASTContext().setModuleForCodeGen(CodegenModule);
+ }
+ Consumer->HandleTranslationUnit(S.getASTContext());
+
+ // Finalize the template instantiation observer chain.
+ // FIXME: This (and init.) should be done in the Sema class, but because
+ // Sema does not have a reliable "Finalize" function (it has a
+ // destructor, but it is not guaranteed to be called ("-disable-free")).
+ // So, do the initialization above and do the finalization here:
+ finalize(S.TemplateInstCallbacks, S);
+
+ std::swap(OldCollectStats, S.CollectStats);
+ if (PrintStats) {
+ llvm::errs() << "\nSTATISTICS:\n";
+ if (HaveLexer) P.getActions().PrintStats();
+ S.getASTContext().PrintStats();
+ Decl::PrintStats();
+ Stmt::PrintStats();
+ Consumer->PrintStats();
+ }
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseCXXInlineMethods.cpp b/contrib/libs/clang16/lib/Parse/ParseCXXInlineMethods.cpp
new file mode 100644
index 0000000000..3a7f5426d4
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseCXXInlineMethods.cpp
@@ -0,0 +1,1386 @@
+//===--- ParseCXXInlineMethods.cpp - C++ class inline methods parsing------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements parsing for C++ class inline methods.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Parse/Parser.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/Scope.h"
+using namespace clang;
+
+/// ParseCXXInlineMethodDef - We parsed and verified that the specified
+/// Declarator is a well formed C++ inline method definition. Now lex its body
+/// and store its tokens for parsing after the C++ class is complete.
+NamedDecl *Parser::ParseCXXInlineMethodDef(
+ AccessSpecifier AS, const ParsedAttributesView &AccessAttrs,
+ ParsingDeclarator &D, const ParsedTemplateInfo &TemplateInfo,
+ const VirtSpecifiers &VS, SourceLocation PureSpecLoc) {
+ assert(D.isFunctionDeclarator() && "This isn't a function declarator!");
+ assert(Tok.isOneOf(tok::l_brace, tok::colon, tok::kw_try, tok::equal) &&
+ "Current token not a '{', ':', '=', or 'try'!");
+
+ MultiTemplateParamsArg TemplateParams(
+ TemplateInfo.TemplateParams ? TemplateInfo.TemplateParams->data()
+ : nullptr,
+ TemplateInfo.TemplateParams ? TemplateInfo.TemplateParams->size() : 0);
+
+ NamedDecl *FnD;
+ if (D.getDeclSpec().isFriendSpecified())
+ FnD = Actions.ActOnFriendFunctionDecl(getCurScope(), D,
+ TemplateParams);
+ else {
+ FnD = Actions.ActOnCXXMemberDeclarator(getCurScope(), AS, D,
+ TemplateParams, nullptr,
+ VS, ICIS_NoInit);
+ if (FnD) {
+ Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs);
+ if (PureSpecLoc.isValid())
+ Actions.ActOnPureSpecifier(FnD, PureSpecLoc);
+ }
+ }
+
+ if (FnD)
+ HandleMemberFunctionDeclDelays(D, FnD);
+
+ D.complete(FnD);
+
+ if (TryConsumeToken(tok::equal)) {
+ if (!FnD) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ bool Delete = false;
+ SourceLocation KWLoc;
+ SourceLocation KWEndLoc = Tok.getEndLoc().getLocWithOffset(-1);
+ if (TryConsumeToken(tok::kw_delete, KWLoc)) {
+ Diag(KWLoc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_defaulted_deleted_function
+ : diag::ext_defaulted_deleted_function)
+ << 1 /* deleted */;
+ Actions.SetDeclDeleted(FnD, KWLoc);
+ Delete = true;
+ if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(FnD)) {
+ DeclAsFunction->setRangeEnd(KWEndLoc);
+ }
+ } else if (TryConsumeToken(tok::kw_default, KWLoc)) {
+ Diag(KWLoc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_defaulted_deleted_function
+ : diag::ext_defaulted_deleted_function)
+ << 0 /* defaulted */;
+ Actions.SetDeclDefaulted(FnD, KWLoc);
+ if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(FnD)) {
+ DeclAsFunction->setRangeEnd(KWEndLoc);
+ }
+ } else {
+ llvm_unreachable("function definition after = not 'delete' or 'default'");
+ }
+
+ if (Tok.is(tok::comma)) {
+ Diag(KWLoc, diag::err_default_delete_in_multiple_declaration)
+ << Delete;
+ SkipUntil(tok::semi);
+ } else if (ExpectAndConsume(tok::semi, diag::err_expected_after,
+ Delete ? "delete" : "default")) {
+ SkipUntil(tok::semi);
+ }
+
+ return FnD;
+ }
+
+ if (SkipFunctionBodies && (!FnD || Actions.canSkipFunctionBody(FnD)) &&
+ trySkippingFunctionBody()) {
+ Actions.ActOnSkippedFunctionBody(FnD);
+ return FnD;
+ }
+
+ // In delayed template parsing mode, if we are within a class template
+ // or if we are about to parse function member template then consume
+ // the tokens and store them for parsing at the end of the translation unit.
+ if (getLangOpts().DelayedTemplateParsing &&
+ D.getFunctionDefinitionKind() == FunctionDefinitionKind::Definition &&
+ !D.getDeclSpec().hasConstexprSpecifier() &&
+ !(FnD && FnD->getAsFunction() &&
+ FnD->getAsFunction()->getReturnType()->getContainedAutoType()) &&
+ ((Actions.CurContext->isDependentContext() ||
+ (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
+ TemplateInfo.Kind != ParsedTemplateInfo::ExplicitSpecialization)) &&
+ !Actions.IsInsideALocalClassWithinATemplateFunction())) {
+
+ CachedTokens Toks;
+ LexTemplateFunctionForLateParsing(Toks);
+
+ if (FnD) {
+ FunctionDecl *FD = FnD->getAsFunction();
+ Actions.CheckForFunctionRedefinition(FD);
+ Actions.MarkAsLateParsedTemplate(FD, FnD, Toks);
+ }
+
+ return FnD;
+ }
+
+ // Consume the tokens and store them for later parsing.
+
+ LexedMethod* LM = new LexedMethod(this, FnD);
+ getCurrentClass().LateParsedDeclarations.push_back(LM);
+ CachedTokens &Toks = LM->Toks;
+
+ tok::TokenKind kind = Tok.getKind();
+ // Consume everything up to (and including) the left brace of the
+ // function body.
+ if (ConsumeAndStoreFunctionPrologue(Toks)) {
+ // We didn't find the left-brace we expected after the
+ // constructor initializer.
+
+ // If we're code-completing and the completion point was in the broken
+ // initializer, we want to parse it even though that will fail.
+ if (PP.isCodeCompletionEnabled() &&
+ llvm::any_of(Toks, [](const Token &Tok) {
+ return Tok.is(tok::code_completion);
+ })) {
+ // If we gave up at the completion point, the initializer list was
+ // likely truncated, so don't eat more tokens. We'll hit some extra
+ // errors, but they should be ignored in code completion.
+ return FnD;
+ }
+
+ // We already printed an error, and it's likely impossible to recover,
+ // so don't try to parse this method later.
+ // Skip over the rest of the decl and back to somewhere that looks
+ // reasonable.
+ SkipMalformedDecl();
+ delete getCurrentClass().LateParsedDeclarations.back();
+ getCurrentClass().LateParsedDeclarations.pop_back();
+ return FnD;
+ } else {
+ // Consume everything up to (and including) the matching right brace.
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ }
+
+ // If we're in a function-try-block, we need to store all the catch blocks.
+ if (kind == tok::kw_try) {
+ while (Tok.is(tok::kw_catch)) {
+ ConsumeAndStoreUntil(tok::l_brace, Toks, /*StopAtSemi=*/false);
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ }
+ }
+
+ if (FnD) {
+ FunctionDecl *FD = FnD->getAsFunction();
+ // Track that this function will eventually have a body; Sema needs
+ // to know this.
+ Actions.CheckForFunctionRedefinition(FD);
+ FD->setWillHaveBody(true);
+ } else {
+ // If semantic analysis could not build a function declaration,
+ // just throw away the late-parsed declaration.
+ delete getCurrentClass().LateParsedDeclarations.back();
+ getCurrentClass().LateParsedDeclarations.pop_back();
+ }
+
+ return FnD;
+}
+
+/// ParseCXXNonStaticMemberInitializer - We parsed and verified that the
+/// specified Declarator is a well formed C++ non-static data member
+/// declaration. Now lex its initializer and store its tokens for parsing
+/// after the class is complete.
+void Parser::ParseCXXNonStaticMemberInitializer(Decl *VarD) {
+ assert(Tok.isOneOf(tok::l_brace, tok::equal) &&
+ "Current token not a '{' or '='!");
+
+ LateParsedMemberInitializer *MI =
+ new LateParsedMemberInitializer(this, VarD);
+ getCurrentClass().LateParsedDeclarations.push_back(MI);
+ CachedTokens &Toks = MI->Toks;
+
+ tok::TokenKind kind = Tok.getKind();
+ if (kind == tok::equal) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ }
+
+ if (kind == tok::l_brace) {
+ // Begin by storing the '{' token.
+ Toks.push_back(Tok);
+ ConsumeBrace();
+
+ // Consume everything up to (and including) the matching right brace.
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/true);
+ } else {
+ // Consume everything up to (but excluding) the comma or semicolon.
+ ConsumeAndStoreInitializer(Toks, CIK_DefaultInitializer);
+ }
+
+ // Store an artificial EOF token to ensure that we don't run off the end of
+ // the initializer when we come to parse it.
+ Token Eof;
+ Eof.startToken();
+ Eof.setKind(tok::eof);
+ Eof.setLocation(Tok.getLocation());
+ Eof.setEofData(VarD);
+ Toks.push_back(Eof);
+}
+
+Parser::LateParsedDeclaration::~LateParsedDeclaration() {}
+void Parser::LateParsedDeclaration::ParseLexedMethodDeclarations() {}
+void Parser::LateParsedDeclaration::ParseLexedMemberInitializers() {}
+void Parser::LateParsedDeclaration::ParseLexedMethodDefs() {}
+void Parser::LateParsedDeclaration::ParseLexedAttributes() {}
+void Parser::LateParsedDeclaration::ParseLexedPragmas() {}
+
+Parser::LateParsedClass::LateParsedClass(Parser *P, ParsingClass *C)
+ : Self(P), Class(C) {}
+
+Parser::LateParsedClass::~LateParsedClass() {
+ Self->DeallocateParsedClasses(Class);
+}
+
+void Parser::LateParsedClass::ParseLexedMethodDeclarations() {
+ Self->ParseLexedMethodDeclarations(*Class);
+}
+
+void Parser::LateParsedClass::ParseLexedMemberInitializers() {
+ Self->ParseLexedMemberInitializers(*Class);
+}
+
+void Parser::LateParsedClass::ParseLexedMethodDefs() {
+ Self->ParseLexedMethodDefs(*Class);
+}
+
+void Parser::LateParsedClass::ParseLexedAttributes() {
+ Self->ParseLexedAttributes(*Class);
+}
+
+void Parser::LateParsedClass::ParseLexedPragmas() {
+ Self->ParseLexedPragmas(*Class);
+}
+
+void Parser::LateParsedMethodDeclaration::ParseLexedMethodDeclarations() {
+ Self->ParseLexedMethodDeclaration(*this);
+}
+
+void Parser::LexedMethod::ParseLexedMethodDefs() {
+ Self->ParseLexedMethodDef(*this);
+}
+
+void Parser::LateParsedMemberInitializer::ParseLexedMemberInitializers() {
+ Self->ParseLexedMemberInitializer(*this);
+}
+
+void Parser::LateParsedAttribute::ParseLexedAttributes() {
+ Self->ParseLexedAttribute(*this, true, false);
+}
+
+void Parser::LateParsedPragma::ParseLexedPragmas() {
+ Self->ParseLexedPragma(*this);
+}
+
+/// Utility to re-enter a possibly-templated scope while parsing its
+/// late-parsed components.
+struct Parser::ReenterTemplateScopeRAII {
+ Parser &P;
+ MultiParseScope Scopes;
+ TemplateParameterDepthRAII CurTemplateDepthTracker;
+
+ ReenterTemplateScopeRAII(Parser &P, Decl *MaybeTemplated, bool Enter = true)
+ : P(P), Scopes(P), CurTemplateDepthTracker(P.TemplateParameterDepth) {
+ if (Enter) {
+ CurTemplateDepthTracker.addDepth(
+ P.ReenterTemplateScopes(Scopes, MaybeTemplated));
+ }
+ }
+};
+
+/// Utility to re-enter a class scope while parsing its late-parsed components.
+struct Parser::ReenterClassScopeRAII : ReenterTemplateScopeRAII {
+ ParsingClass &Class;
+
+ ReenterClassScopeRAII(Parser &P, ParsingClass &Class)
+ : ReenterTemplateScopeRAII(P, Class.TagOrTemplate,
+ /*Enter=*/!Class.TopLevelClass),
+ Class(Class) {
+ // If this is the top-level class, we're still within its scope.
+ if (Class.TopLevelClass)
+ return;
+
+ // Re-enter the class scope itself.
+ Scopes.Enter(Scope::ClassScope|Scope::DeclScope);
+ P.Actions.ActOnStartDelayedMemberDeclarations(P.getCurScope(),
+ Class.TagOrTemplate);
+ }
+ ~ReenterClassScopeRAII() {
+ if (Class.TopLevelClass)
+ return;
+
+ P.Actions.ActOnFinishDelayedMemberDeclarations(P.getCurScope(),
+ Class.TagOrTemplate);
+ }
+};
+
+/// ParseLexedMethodDeclarations - We finished parsing the member
+/// specification of a top (non-nested) C++ class. Now go over the
+/// stack of method declarations with some parts for which parsing was
+/// delayed (such as default arguments) and parse them.
+void Parser::ParseLexedMethodDeclarations(ParsingClass &Class) {
+ ReenterClassScopeRAII InClassScope(*this, Class);
+
+ for (LateParsedDeclaration *LateD : Class.LateParsedDeclarations)
+ LateD->ParseLexedMethodDeclarations();
+}
+
+void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
+ // If this is a member template, introduce the template parameter scope.
+ ReenterTemplateScopeRAII InFunctionTemplateScope(*this, LM.Method);
+
+ // Start the delayed C++ method declaration
+ Actions.ActOnStartDelayedCXXMethodDeclaration(getCurScope(), LM.Method);
+
+ // Introduce the parameters into scope and parse their default
+ // arguments.
+ InFunctionTemplateScope.Scopes.Enter(Scope::FunctionPrototypeScope |
+ Scope::FunctionDeclarationScope |
+ Scope::DeclScope);
+ for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) {
+ auto Param = cast<ParmVarDecl>(LM.DefaultArgs[I].Param);
+ // Introduce the parameter into scope.
+ bool HasUnparsed = Param->hasUnparsedDefaultArg();
+ Actions.ActOnDelayedCXXMethodParameter(getCurScope(), Param);
+ std::unique_ptr<CachedTokens> Toks = std::move(LM.DefaultArgs[I].Toks);
+ if (Toks) {
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ // Mark the end of the default argument so that we know when to stop when
+ // we parse it later on.
+ Token LastDefaultArgToken = Toks->back();
+ Token DefArgEnd;
+ DefArgEnd.startToken();
+ DefArgEnd.setKind(tok::eof);
+ DefArgEnd.setLocation(LastDefaultArgToken.getEndLoc());
+ DefArgEnd.setEofData(Param);
+ Toks->push_back(DefArgEnd);
+
+ // Parse the default argument from its saved token stream.
+ Toks->push_back(Tok); // So that the current token doesn't get lost
+ PP.EnterTokenStream(*Toks, true, /*IsReinject*/ true);
+
+ // Consume the previously-pushed token.
+ ConsumeAnyToken();
+
+ // Consume the '='.
+ assert(Tok.is(tok::equal) && "Default argument not starting with '='");
+ SourceLocation EqualLoc = ConsumeToken();
+
+ // The argument isn't actually potentially evaluated unless it is
+ // used.
+ EnterExpressionEvaluationContext Eval(
+ Actions,
+ Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed, Param);
+
+ ExprResult DefArgResult;
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+ DefArgResult = ParseBraceInitializer();
+ } else
+ DefArgResult = ParseAssignmentExpression();
+ DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult);
+ if (DefArgResult.isInvalid()) {
+ Actions.ActOnParamDefaultArgumentError(Param, EqualLoc);
+ } else {
+ if (Tok.isNot(tok::eof) || Tok.getEofData() != Param) {
+ // The last two tokens are the terminator and the saved value of
+ // Tok; the last token in the default argument is the one before
+ // those.
+ assert(Toks->size() >= 3 && "expected a token in default arg");
+ Diag(Tok.getLocation(), diag::err_default_arg_unparsed)
+ << SourceRange(Tok.getLocation(),
+ (*Toks)[Toks->size() - 3].getLocation());
+ }
+ Actions.ActOnParamDefaultArgument(Param, EqualLoc,
+ DefArgResult.get());
+ }
+
+ // There could be leftover tokens (e.g. because of an error).
+ // Skip through until we reach the 'end of default argument' token.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ if (Tok.is(tok::eof) && Tok.getEofData() == Param)
+ ConsumeAnyToken();
+ } else if (HasUnparsed) {
+ assert(Param->hasInheritedDefaultArg());
+ const FunctionDecl *Old;
+ if (const auto *FunTmpl = dyn_cast<FunctionTemplateDecl>(LM.Method))
+ Old =
+ cast<FunctionDecl>(FunTmpl->getTemplatedDecl())->getPreviousDecl();
+ else
+ Old = cast<FunctionDecl>(LM.Method)->getPreviousDecl();
+ if (Old) {
+ ParmVarDecl *OldParam = const_cast<ParmVarDecl*>(Old->getParamDecl(I));
+ assert(!OldParam->hasUnparsedDefaultArg());
+ if (OldParam->hasUninstantiatedDefaultArg())
+ Param->setUninstantiatedDefaultArg(
+ OldParam->getUninstantiatedDefaultArg());
+ else
+ Param->setDefaultArg(OldParam->getInit());
+ }
+ }
+ }
+
+ // Parse a delayed exception-specification, if there is one.
+ if (CachedTokens *Toks = LM.ExceptionSpecTokens) {
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ // Add the 'stop' token.
+ Token LastExceptionSpecToken = Toks->back();
+ Token ExceptionSpecEnd;
+ ExceptionSpecEnd.startToken();
+ ExceptionSpecEnd.setKind(tok::eof);
+ ExceptionSpecEnd.setLocation(LastExceptionSpecToken.getEndLoc());
+ ExceptionSpecEnd.setEofData(LM.Method);
+ Toks->push_back(ExceptionSpecEnd);
+
+ // Parse the default argument from its saved token stream.
+ Toks->push_back(Tok); // So that the current token doesn't get lost
+ PP.EnterTokenStream(*Toks, true, /*IsReinject*/true);
+
+ // Consume the previously-pushed token.
+ ConsumeAnyToken();
+
+ // C++11 [expr.prim.general]p3:
+ // If a declaration declares a member function or member function
+ // template of a class X, the expression this is a prvalue of type
+ // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
+ // and the end of the function-definition, member-declarator, or
+ // declarator.
+ CXXMethodDecl *Method;
+ if (FunctionTemplateDecl *FunTmpl
+ = dyn_cast<FunctionTemplateDecl>(LM.Method))
+ Method = dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl());
+ else
+ Method = dyn_cast<CXXMethodDecl>(LM.Method);
+
+ Sema::CXXThisScopeRAII ThisScope(
+ Actions, Method ? Method->getParent() : nullptr,
+ Method ? Method->getMethodQualifiers() : Qualifiers{},
+ Method && getLangOpts().CPlusPlus11);
+
+ // Parse the exception-specification.
+ SourceRange SpecificationRange;
+ SmallVector<ParsedType, 4> DynamicExceptions;
+ SmallVector<SourceRange, 4> DynamicExceptionRanges;
+ ExprResult NoexceptExpr;
+ CachedTokens *ExceptionSpecTokens;
+
+ ExceptionSpecificationType EST
+ = tryParseExceptionSpecification(/*Delayed=*/false, SpecificationRange,
+ DynamicExceptions,
+ DynamicExceptionRanges, NoexceptExpr,
+ ExceptionSpecTokens);
+
+ if (Tok.isNot(tok::eof) || Tok.getEofData() != LM.Method)
+ Diag(Tok.getLocation(), diag::err_except_spec_unparsed);
+
+ // Attach the exception-specification to the method.
+ Actions.actOnDelayedExceptionSpecification(LM.Method, EST,
+ SpecificationRange,
+ DynamicExceptions,
+ DynamicExceptionRanges,
+ NoexceptExpr.isUsable()?
+ NoexceptExpr.get() : nullptr);
+
+ // There could be leftover tokens (e.g. because of an error).
+ // Skip through until we reach the original token position.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ // Clean up the remaining EOF token.
+ if (Tok.is(tok::eof) && Tok.getEofData() == LM.Method)
+ ConsumeAnyToken();
+
+ delete Toks;
+ LM.ExceptionSpecTokens = nullptr;
+ }
+
+ InFunctionTemplateScope.Scopes.Exit();
+
+ // Finish the delayed C++ method declaration.
+ Actions.ActOnFinishDelayedCXXMethodDeclaration(getCurScope(), LM.Method);
+}
+
+/// ParseLexedMethodDefs - We finished parsing the member specification of a top
+/// (non-nested) C++ class. Now go over the stack of lexed methods that were
+/// collected during its parsing and parse them all.
+void Parser::ParseLexedMethodDefs(ParsingClass &Class) {
+ ReenterClassScopeRAII InClassScope(*this, Class);
+
+ for (LateParsedDeclaration *D : Class.LateParsedDeclarations)
+ D->ParseLexedMethodDefs();
+}
+
+void Parser::ParseLexedMethodDef(LexedMethod &LM) {
+ // If this is a member template, introduce the template parameter scope.
+ ReenterTemplateScopeRAII InFunctionTemplateScope(*this, LM.D);
+
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ assert(!LM.Toks.empty() && "Empty body!");
+ Token LastBodyToken = LM.Toks.back();
+ Token BodyEnd;
+ BodyEnd.startToken();
+ BodyEnd.setKind(tok::eof);
+ BodyEnd.setLocation(LastBodyToken.getEndLoc());
+ BodyEnd.setEofData(LM.D);
+ LM.Toks.push_back(BodyEnd);
+ // Append the current token at the end of the new token stream so that it
+ // doesn't get lost.
+ LM.Toks.push_back(Tok);
+ PP.EnterTokenStream(LM.Toks, true, /*IsReinject*/true);
+
+ // Consume the previously pushed token.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+ assert(Tok.isOneOf(tok::l_brace, tok::colon, tok::kw_try)
+ && "Inline method not starting with '{', ':' or 'try'");
+
+ // Parse the method body. Function body parsing code is similar enough
+ // to be re-used for method bodies as well.
+ ParseScope FnScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+ Actions.ActOnStartOfFunctionDef(getCurScope(), LM.D);
+
+ if (Tok.is(tok::kw_try)) {
+ ParseFunctionTryBlock(LM.D, FnScope);
+
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ if (Tok.is(tok::eof) && Tok.getEofData() == LM.D)
+ ConsumeAnyToken();
+ return;
+ }
+ if (Tok.is(tok::colon)) {
+ ParseConstructorInitializer(LM.D);
+
+ // Error recovery.
+ if (!Tok.is(tok::l_brace)) {
+ FnScope.Exit();
+ Actions.ActOnFinishFunctionBody(LM.D, nullptr);
+
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ if (Tok.is(tok::eof) && Tok.getEofData() == LM.D)
+ ConsumeAnyToken();
+ return;
+ }
+ } else
+ Actions.ActOnDefaultCtorInitializers(LM.D);
+
+ assert((Actions.getDiagnostics().hasErrorOccurred() ||
+ !isa<FunctionTemplateDecl>(LM.D) ||
+ cast<FunctionTemplateDecl>(LM.D)->getTemplateParameters()->getDepth()
+ < TemplateParameterDepth) &&
+ "TemplateParameterDepth should be greater than the depth of "
+ "current template being instantiated!");
+
+ ParseFunctionStatementBody(LM.D, FnScope);
+
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ if (Tok.is(tok::eof) && Tok.getEofData() == LM.D)
+ ConsumeAnyToken();
+
+ if (auto *FD = dyn_cast_or_null<FunctionDecl>(LM.D))
+ if (isa<CXXMethodDecl>(FD) ||
+ FD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend))
+ Actions.ActOnFinishInlineFunctionDef(FD);
+}
+
+/// ParseLexedMemberInitializers - We finished parsing the member specification
+/// of a top (non-nested) C++ class. Now go over the stack of lexed data member
+/// initializers that were collected during its parsing and parse them all.
+void Parser::ParseLexedMemberInitializers(ParsingClass &Class) {
+ ReenterClassScopeRAII InClassScope(*this, Class);
+
+ if (!Class.LateParsedDeclarations.empty()) {
+ // C++11 [expr.prim.general]p4:
+ // Otherwise, if a member-declarator declares a non-static data member
+ // (9.2) of a class X, the expression this is a prvalue of type "pointer
+ // to X" within the optional brace-or-equal-initializer. It shall not
+ // appear elsewhere in the member-declarator.
+ // FIXME: This should be done in ParseLexedMemberInitializer, not here.
+ Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate,
+ Qualifiers());
+
+ for (LateParsedDeclaration *D : Class.LateParsedDeclarations)
+ D->ParseLexedMemberInitializers();
+ }
+
+ Actions.ActOnFinishDelayedMemberInitializers(Class.TagOrTemplate);
+}
+
+void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) {
+ if (!MI.Field || MI.Field->isInvalidDecl())
+ return;
+
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ // Append the current token at the end of the new token stream so that it
+ // doesn't get lost.
+ MI.Toks.push_back(Tok);
+ PP.EnterTokenStream(MI.Toks, true, /*IsReinject*/true);
+
+ // Consume the previously pushed token.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+
+ SourceLocation EqualLoc;
+
+ Actions.ActOnStartCXXInClassMemberInitializer();
+
+ // The initializer isn't actually potentially evaluated unless it is
+ // used.
+ EnterExpressionEvaluationContext Eval(
+ Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed);
+
+ ExprResult Init = ParseCXXMemberInitializer(MI.Field, /*IsFunction=*/false,
+ EqualLoc);
+
+ Actions.ActOnFinishCXXInClassMemberInitializer(MI.Field, EqualLoc,
+ Init.get());
+
+ // The next token should be our artificial terminating EOF token.
+ if (Tok.isNot(tok::eof)) {
+ if (!Init.isInvalid()) {
+ SourceLocation EndLoc = PP.getLocForEndOfToken(PrevTokLocation);
+ if (!EndLoc.isValid())
+ EndLoc = Tok.getLocation();
+ // No fixit; we can't recover as if there were a semicolon here.
+ Diag(EndLoc, diag::err_expected_semi_decl_list);
+ }
+
+ // Consume tokens until we hit the artificial EOF.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+ }
+ // Make sure this is *our* artificial EOF token.
+ if (Tok.getEofData() == MI.Field)
+ ConsumeAnyToken();
+}
+
+/// Wrapper class which calls ParseLexedAttribute, after setting up the
+/// scope appropriately.
+void Parser::ParseLexedAttributes(ParsingClass &Class) {
+ ReenterClassScopeRAII InClassScope(*this, Class);
+
+ for (LateParsedDeclaration *LateD : Class.LateParsedDeclarations)
+ LateD->ParseLexedAttributes();
+}
+
+/// Parse all attributes in LAs, and attach them to Decl D.
+void Parser::ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,
+ bool EnterScope, bool OnDefinition) {
+ assert(LAs.parseSoon() &&
+ "Attribute list should be marked for immediate parsing.");
+ for (unsigned i = 0, ni = LAs.size(); i < ni; ++i) {
+ if (D)
+ LAs[i]->addDecl(D);
+ ParseLexedAttribute(*LAs[i], EnterScope, OnDefinition);
+ delete LAs[i];
+ }
+ LAs.clear();
+}
+
+/// Finish parsing an attribute for which parsing was delayed.
+/// This will be called at the end of parsing a class declaration
+/// for each LateParsedAttribute. We consume the saved tokens and
+/// create an attribute with the arguments filled in. We add this
+/// to the Attribute list for the decl.
+void Parser::ParseLexedAttribute(LateParsedAttribute &LA,
+ bool EnterScope, bool OnDefinition) {
+ // Create a fake EOF so that attribute parsing won't go off the end of the
+ // attribute.
+ Token AttrEnd;
+ AttrEnd.startToken();
+ AttrEnd.setKind(tok::eof);
+ AttrEnd.setLocation(Tok.getLocation());
+ AttrEnd.setEofData(LA.Toks.data());
+ LA.Toks.push_back(AttrEnd);
+
+ // Append the current token at the end of the new token stream so that it
+ // doesn't get lost.
+ LA.Toks.push_back(Tok);
+ PP.EnterTokenStream(LA.Toks, true, /*IsReinject=*/true);
+ // Consume the previously pushed token.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+
+ ParsedAttributes Attrs(AttrFactory);
+
+ if (LA.Decls.size() > 0) {
+ Decl *D = LA.Decls[0];
+ NamedDecl *ND = dyn_cast<NamedDecl>(D);
+ RecordDecl *RD = dyn_cast_or_null<RecordDecl>(D->getDeclContext());
+
+ // Allow 'this' within late-parsed attributes.
+ Sema::CXXThisScopeRAII ThisScope(Actions, RD, Qualifiers(),
+ ND && ND->isCXXInstanceMember());
+
+ if (LA.Decls.size() == 1) {
+ // If the Decl is templatized, add template parameters to scope.
+ ReenterTemplateScopeRAII InDeclScope(*this, D, EnterScope);
+
+ // If the Decl is on a function, add function parameters to the scope.
+ bool HasFunScope = EnterScope && D->isFunctionOrFunctionTemplate();
+ if (HasFunScope) {
+ InDeclScope.Scopes.Enter(Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+ Actions.ActOnReenterFunctionContext(Actions.CurScope, D);
+ }
+
+ ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr,
+ nullptr, SourceLocation(), ParsedAttr::AS_GNU,
+ nullptr);
+
+ if (HasFunScope)
+ Actions.ActOnExitFunctionContext();
+ } else {
+ // If there are multiple decls, then the decl cannot be within the
+ // function scope.
+ ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr,
+ nullptr, SourceLocation(), ParsedAttr::AS_GNU,
+ nullptr);
+ }
+ } else {
+ Diag(Tok, diag::warn_attribute_no_decl) << LA.AttrName.getName();
+ }
+
+ if (OnDefinition && !Attrs.empty() && !Attrs.begin()->isCXX11Attribute() &&
+ Attrs.begin()->isKnownToGCC())
+ Diag(Tok, diag::warn_attribute_on_function_definition)
+ << &LA.AttrName;
+
+ for (unsigned i = 0, ni = LA.Decls.size(); i < ni; ++i)
+ Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.Decls[i], Attrs);
+
+ // Due to a parsing error, we either went over the cached tokens or
+ // there are still cached tokens left, so we skip the leftover tokens.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData())
+ ConsumeAnyToken();
+}
+
+void Parser::ParseLexedPragmas(ParsingClass &Class) {
+ ReenterClassScopeRAII InClassScope(*this, Class);
+
+ for (LateParsedDeclaration *D : Class.LateParsedDeclarations)
+ D->ParseLexedPragmas();
+}
+
+void Parser::ParseLexedPragma(LateParsedPragma &LP) {
+ PP.EnterToken(Tok, /*IsReinject=*/true);
+ PP.EnterTokenStream(LP.toks(), /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/true);
+
+ // Consume the previously pushed token.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+ assert(Tok.isAnnotation() && "Expected annotation token.");
+ switch (Tok.getKind()) {
+ case tok::annot_attr_openmp:
+ case tok::annot_pragma_openmp: {
+ AccessSpecifier AS = LP.getAccessSpecifier();
+ ParsedAttributes Attrs(AttrFactory);
+ (void)ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, Attrs);
+ break;
+ }
+ default:
+ llvm_unreachable("Unexpected token.");
+ }
+}
+
+/// ConsumeAndStoreUntil - Consume and store the token at the passed token
+/// container until the token 'T' is reached (which gets
+/// consumed/stored too, if ConsumeFinalToken).
+/// If StopAtSemi is true, then we will stop early at a ';' character.
+/// Returns true if token 'T1' or 'T2' was found.
+/// NOTE: This is a specialized version of Parser::SkipUntil.
+bool Parser::ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2,
+ CachedTokens &Toks,
+ bool StopAtSemi, bool ConsumeFinalToken) {
+ // We always want this function to consume at least one token if the first
+ // token isn't T and if not at EOF.
+ bool isFirstTokenConsumed = true;
+ while (true) {
+ // If we found one of the tokens, stop and return true.
+ if (Tok.is(T1) || Tok.is(T2)) {
+ if (ConsumeFinalToken) {
+ Toks.push_back(Tok);
+ ConsumeAnyToken();
+ }
+ return true;
+ }
+
+ switch (Tok.getKind()) {
+ case tok::eof:
+ case tok::annot_module_begin:
+ case tok::annot_module_end:
+ case tok::annot_module_include:
+ // Ran out of tokens.
+ return false;
+
+ case tok::l_paren:
+ // Recursively consume properly-nested parens.
+ Toks.push_back(Tok);
+ ConsumeParen();
+ ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false);
+ break;
+ case tok::l_square:
+ // Recursively consume properly-nested square brackets.
+ Toks.push_back(Tok);
+ ConsumeBracket();
+ ConsumeAndStoreUntil(tok::r_square, Toks, /*StopAtSemi=*/false);
+ break;
+ case tok::l_brace:
+ // Recursively consume properly-nested braces.
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ break;
+
+ // Okay, we found a ']' or '}' or ')', which we think should be balanced.
+ // Since the user wasn't looking for this token (if they were, it would
+ // already be handled), this isn't balanced. If there is a LHS token at a
+ // higher level, we will assume that this matches the unbalanced token
+ // and return it. Otherwise, this is a spurious RHS token, which we skip.
+ case tok::r_paren:
+ if (ParenCount && !isFirstTokenConsumed)
+ return false; // Matches something.
+ Toks.push_back(Tok);
+ ConsumeParen();
+ break;
+ case tok::r_square:
+ if (BracketCount && !isFirstTokenConsumed)
+ return false; // Matches something.
+ Toks.push_back(Tok);
+ ConsumeBracket();
+ break;
+ case tok::r_brace:
+ if (BraceCount && !isFirstTokenConsumed)
+ return false; // Matches something.
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ break;
+
+ case tok::semi:
+ if (StopAtSemi)
+ return false;
+ [[fallthrough]];
+ default:
+ // consume this token.
+ Toks.push_back(Tok);
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok*/true);
+ break;
+ }
+ isFirstTokenConsumed = false;
+ }
+}
+
+/// Consume tokens and store them in the passed token container until
+/// we've passed the try keyword and constructor initializers and have consumed
+/// the opening brace of the function body. The opening brace will be consumed
+/// if and only if there was no error.
+///
+/// \return True on error.
+bool Parser::ConsumeAndStoreFunctionPrologue(CachedTokens &Toks) {
+ if (Tok.is(tok::kw_try)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ }
+
+ if (Tok.isNot(tok::colon)) {
+ // Easy case, just a function body.
+
+ // Grab any remaining garbage to be diagnosed later. We stop when we reach a
+ // brace: an opening one is the function body, while a closing one probably
+ // means we've reached the end of the class.
+ ConsumeAndStoreUntil(tok::l_brace, tok::r_brace, Toks,
+ /*StopAtSemi=*/true,
+ /*ConsumeFinalToken=*/false);
+ if (Tok.isNot(tok::l_brace))
+ return Diag(Tok.getLocation(), diag::err_expected) << tok::l_brace;
+
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ return false;
+ }
+
+ Toks.push_back(Tok);
+ ConsumeToken();
+
+ // We can't reliably skip over a mem-initializer-id, because it could be
+ // a template-id involving not-yet-declared names. Given:
+ //
+ // S ( ) : a < b < c > ( e )
+ //
+ // 'e' might be an initializer or part of a template argument, depending
+ // on whether 'b' is a template.
+
+ // Track whether we might be inside a template argument. We can give
+ // significantly better diagnostics if we know that we're not.
+ bool MightBeTemplateArgument = false;
+
+ while (true) {
+ // Skip over the mem-initializer-id, if possible.
+ if (Tok.is(tok::kw_decltype)) {
+ Toks.push_back(Tok);
+ SourceLocation OpenLoc = ConsumeToken();
+ if (Tok.isNot(tok::l_paren))
+ return Diag(Tok.getLocation(), diag::err_expected_lparen_after)
+ << "decltype";
+ Toks.push_back(Tok);
+ ConsumeParen();
+ if (!ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/true)) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren;
+ Diag(OpenLoc, diag::note_matching) << tok::l_paren;
+ return true;
+ }
+ }
+ do {
+ // Walk over a component of a nested-name-specifier.
+ if (Tok.is(tok::coloncolon)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+
+ if (Tok.is(tok::kw_template)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ }
+ }
+
+ if (Tok.is(tok::identifier)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ } else {
+ break;
+ }
+ } while (Tok.is(tok::coloncolon));
+
+ if (Tok.is(tok::code_completion)) {
+ Toks.push_back(Tok);
+ ConsumeCodeCompletionToken();
+ if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_decltype)) {
+ // Could be the start of another member initializer (the ',' has not
+ // been written yet)
+ continue;
+ }
+ }
+
+ if (Tok.is(tok::comma)) {
+ // The initialization is missing, we'll diagnose it later.
+ Toks.push_back(Tok);
+ ConsumeToken();
+ continue;
+ }
+ if (Tok.is(tok::less))
+ MightBeTemplateArgument = true;
+
+ if (MightBeTemplateArgument) {
+ // We may be inside a template argument list. Grab up to the start of the
+ // next parenthesized initializer or braced-init-list. This *might* be the
+ // initializer, or it might be a subexpression in the template argument
+ // list.
+ // FIXME: Count angle brackets, and clear MightBeTemplateArgument
+ // if all angles are closed.
+ if (!ConsumeAndStoreUntil(tok::l_paren, tok::l_brace, Toks,
+ /*StopAtSemi=*/true,
+ /*ConsumeFinalToken=*/false)) {
+ // We're not just missing the initializer, we're also missing the
+ // function body!
+ return Diag(Tok.getLocation(), diag::err_expected) << tok::l_brace;
+ }
+ } else if (Tok.isNot(tok::l_paren) && Tok.isNot(tok::l_brace)) {
+ // We found something weird in a mem-initializer-id.
+ if (getLangOpts().CPlusPlus11)
+ return Diag(Tok.getLocation(), diag::err_expected_either)
+ << tok::l_paren << tok::l_brace;
+ else
+ return Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
+ }
+
+ tok::TokenKind kind = Tok.getKind();
+ Toks.push_back(Tok);
+ bool IsLParen = (kind == tok::l_paren);
+ SourceLocation OpenLoc = Tok.getLocation();
+
+ if (IsLParen) {
+ ConsumeParen();
+ } else {
+ assert(kind == tok::l_brace && "Must be left paren or brace here.");
+ ConsumeBrace();
+ // In C++03, this has to be the start of the function body, which
+ // means the initializer is malformed; we'll diagnose it later.
+ if (!getLangOpts().CPlusPlus11)
+ return false;
+
+ const Token &PreviousToken = Toks[Toks.size() - 2];
+ if (!MightBeTemplateArgument &&
+ !PreviousToken.isOneOf(tok::identifier, tok::greater,
+ tok::greatergreater)) {
+ // If the opening brace is not preceded by one of these tokens, we are
+ // missing the mem-initializer-id. In order to recover better, we need
+ // to use heuristics to determine if this '{' is most likely the
+ // beginning of a brace-init-list or the function body.
+ // Check the token after the corresponding '}'.
+ TentativeParsingAction PA(*this);
+ if (SkipUntil(tok::r_brace) &&
+ !Tok.isOneOf(tok::comma, tok::ellipsis, tok::l_brace)) {
+ // Consider there was a malformed initializer and this is the start
+ // of the function body. We'll diagnose it later.
+ PA.Revert();
+ return false;
+ }
+ PA.Revert();
+ }
+ }
+
+ // Grab the initializer (or the subexpression of the template argument).
+ // FIXME: If we support lambdas here, we'll need to set StopAtSemi to false
+ // if we might be inside the braces of a lambda-expression.
+ tok::TokenKind CloseKind = IsLParen ? tok::r_paren : tok::r_brace;
+ if (!ConsumeAndStoreUntil(CloseKind, Toks, /*StopAtSemi=*/true)) {
+ Diag(Tok, diag::err_expected) << CloseKind;
+ Diag(OpenLoc, diag::note_matching) << kind;
+ return true;
+ }
+
+ // Grab pack ellipsis, if present.
+ if (Tok.is(tok::ellipsis)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ }
+
+ // If we know we just consumed a mem-initializer, we must have ',' or '{'
+ // next.
+ if (Tok.is(tok::comma)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ } else if (Tok.is(tok::l_brace)) {
+ // This is the function body if the ')' or '}' is immediately followed by
+ // a '{'. That cannot happen within a template argument, apart from the
+ // case where a template argument contains a compound literal:
+ //
+ // S ( ) : a < b < c > ( d ) { }
+ // // End of declaration, or still inside the template argument?
+ //
+ // ... and the case where the template argument contains a lambda:
+ //
+ // S ( ) : a < 0 && b < c > ( d ) + [ ] ( ) { return 0; }
+ // ( ) > ( ) { }
+ //
+ // FIXME: Disambiguate these cases. Note that the latter case is probably
+ // going to be made ill-formed by core issue 1607.
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ return false;
+ } else if (!MightBeTemplateArgument) {
+ return Diag(Tok.getLocation(), diag::err_expected_either) << tok::l_brace
+ << tok::comma;
+ }
+ }
+}
+
+/// Consume and store tokens from the '?' to the ':' in a conditional
+/// expression.
+bool Parser::ConsumeAndStoreConditional(CachedTokens &Toks) {
+ // Consume '?'.
+ assert(Tok.is(tok::question));
+ Toks.push_back(Tok);
+ ConsumeToken();
+
+ while (Tok.isNot(tok::colon)) {
+ if (!ConsumeAndStoreUntil(tok::question, tok::colon, Toks,
+ /*StopAtSemi=*/true,
+ /*ConsumeFinalToken=*/false))
+ return false;
+
+ // If we found a nested conditional, consume it.
+ if (Tok.is(tok::question) && !ConsumeAndStoreConditional(Toks))
+ return false;
+ }
+
+ // Consume ':'.
+ Toks.push_back(Tok);
+ ConsumeToken();
+ return true;
+}
+
+/// A tentative parsing action that can also revert token annotations.
+class Parser::UnannotatedTentativeParsingAction : public TentativeParsingAction {
+public:
+ explicit UnannotatedTentativeParsingAction(Parser &Self,
+ tok::TokenKind EndKind)
+ : TentativeParsingAction(Self), Self(Self), EndKind(EndKind) {
+ // Stash away the old token stream, so we can restore it once the
+ // tentative parse is complete.
+ TentativeParsingAction Inner(Self);
+ Self.ConsumeAndStoreUntil(EndKind, Toks, true, /*ConsumeFinalToken*/false);
+ Inner.Revert();
+ }
+
+ void RevertAnnotations() {
+ Revert();
+
+ // Put back the original tokens.
+ Self.SkipUntil(EndKind, StopAtSemi | StopBeforeMatch);
+ if (Toks.size()) {
+ auto Buffer = std::make_unique<Token[]>(Toks.size());
+ std::copy(Toks.begin() + 1, Toks.end(), Buffer.get());
+ Buffer[Toks.size() - 1] = Self.Tok;
+ Self.PP.EnterTokenStream(std::move(Buffer), Toks.size(), true,
+ /*IsReinject*/ true);
+
+ Self.Tok = Toks.front();
+ }
+ }
+
+private:
+ Parser &Self;
+ CachedTokens Toks;
+ tok::TokenKind EndKind;
+};
+
+/// ConsumeAndStoreInitializer - Consume and store the token at the passed token
+/// container until the end of the current initializer expression (either a
+/// default argument or an in-class initializer for a non-static data member).
+///
+/// Returns \c true if we reached the end of something initializer-shaped,
+/// \c false if we bailed out.
+bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks,
+ CachedInitKind CIK) {
+ // We always want this function to consume at least one token if not at EOF.
+ bool IsFirstToken = true;
+
+ // Number of possible unclosed <s we've seen so far. These might be templates,
+ // and might not, but if there were none of them (or we know for sure that
+ // we're within a template), we can avoid a tentative parse.
+ unsigned AngleCount = 0;
+ unsigned KnownTemplateCount = 0;
+
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::comma:
+ // If we might be in a template, perform a tentative parse to check.
+ if (!AngleCount)
+ // Not a template argument: this is the end of the initializer.
+ return true;
+ if (KnownTemplateCount)
+ goto consume_token;
+
+ // We hit a comma inside angle brackets. This is the hard case. The
+ // rule we follow is:
+ // * For a default argument, if the tokens after the comma form a
+ // syntactically-valid parameter-declaration-clause, in which each
+ // parameter has an initializer, then this comma ends the default
+ // argument.
+ // * For a default initializer, if the tokens after the comma form a
+ // syntactically-valid init-declarator-list, then this comma ends
+ // the default initializer.
+ {
+ UnannotatedTentativeParsingAction PA(*this,
+ CIK == CIK_DefaultInitializer
+ ? tok::semi : tok::r_paren);
+ Sema::TentativeAnalysisScope Scope(Actions);
+
+ TPResult Result = TPResult::Error;
+ ConsumeToken();
+ switch (CIK) {
+ case CIK_DefaultInitializer:
+ Result = TryParseInitDeclaratorList();
+ // If we parsed a complete, ambiguous init-declarator-list, this
+ // is only syntactically-valid if it's followed by a semicolon.
+ if (Result == TPResult::Ambiguous && Tok.isNot(tok::semi))
+ Result = TPResult::False;
+ break;
+
+ case CIK_DefaultArgument:
+ bool InvalidAsDeclaration = false;
+ Result = TryParseParameterDeclarationClause(
+ &InvalidAsDeclaration, /*VersusTemplateArg=*/true);
+ // If this is an expression or a declaration with a missing
+ // 'typename', assume it's not a declaration.
+ if (Result == TPResult::Ambiguous && InvalidAsDeclaration)
+ Result = TPResult::False;
+ break;
+ }
+
+ // Put the token stream back and undo any annotations we performed
+ // after the comma. They may reflect a different parse than the one
+ // we will actually perform at the end of the class.
+ PA.RevertAnnotations();
+
+ // If what follows could be a declaration, it is a declaration.
+ if (Result != TPResult::False && Result != TPResult::Error)
+ return true;
+ }
+
+ // Keep going. We know we're inside a template argument list now.
+ ++KnownTemplateCount;
+ goto consume_token;
+
+ case tok::eof:
+ case tok::annot_module_begin:
+ case tok::annot_module_end:
+ case tok::annot_module_include:
+ // Ran out of tokens.
+ return false;
+
+ case tok::less:
+ // FIXME: A '<' can only start a template-id if it's preceded by an
+ // identifier, an operator-function-id, or a literal-operator-id.
+ ++AngleCount;
+ goto consume_token;
+
+ case tok::question:
+ // In 'a ? b : c', 'b' can contain an unparenthesized comma. If it does,
+ // that is *never* the end of the initializer. Skip to the ':'.
+ if (!ConsumeAndStoreConditional(Toks))
+ return false;
+ break;
+
+ case tok::greatergreatergreater:
+ if (!getLangOpts().CPlusPlus11)
+ goto consume_token;
+ if (AngleCount) --AngleCount;
+ if (KnownTemplateCount) --KnownTemplateCount;
+ [[fallthrough]];
+ case tok::greatergreater:
+ if (!getLangOpts().CPlusPlus11)
+ goto consume_token;
+ if (AngleCount) --AngleCount;
+ if (KnownTemplateCount) --KnownTemplateCount;
+ [[fallthrough]];
+ case tok::greater:
+ if (AngleCount) --AngleCount;
+ if (KnownTemplateCount) --KnownTemplateCount;
+ goto consume_token;
+
+ case tok::kw_template:
+ // 'template' identifier '<' is known to start a template argument list,
+ // and can be used to disambiguate the parse.
+ // FIXME: Support all forms of 'template' unqualified-id '<'.
+ Toks.push_back(Tok);
+ ConsumeToken();
+ if (Tok.is(tok::identifier)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ if (Tok.is(tok::less)) {
+ ++AngleCount;
+ ++KnownTemplateCount;
+ Toks.push_back(Tok);
+ ConsumeToken();
+ }
+ }
+ break;
+
+ case tok::kw_operator:
+ // If 'operator' precedes other punctuation, that punctuation loses
+ // its special behavior.
+ Toks.push_back(Tok);
+ ConsumeToken();
+ switch (Tok.getKind()) {
+ case tok::comma:
+ case tok::greatergreatergreater:
+ case tok::greatergreater:
+ case tok::greater:
+ case tok::less:
+ Toks.push_back(Tok);
+ ConsumeToken();
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case tok::l_paren:
+ // Recursively consume properly-nested parens.
+ Toks.push_back(Tok);
+ ConsumeParen();
+ ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false);
+ break;
+ case tok::l_square:
+ // Recursively consume properly-nested square brackets.
+ Toks.push_back(Tok);
+ ConsumeBracket();
+ ConsumeAndStoreUntil(tok::r_square, Toks, /*StopAtSemi=*/false);
+ break;
+ case tok::l_brace:
+ // Recursively consume properly-nested braces.
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ break;
+
+ // Okay, we found a ']' or '}' or ')', which we think should be balanced.
+ // Since the user wasn't looking for this token (if they were, it would
+ // already be handled), this isn't balanced. If there is a LHS token at a
+ // higher level, we will assume that this matches the unbalanced token
+ // and return it. Otherwise, this is a spurious RHS token, which we
+ // consume and pass on to downstream code to diagnose.
+ case tok::r_paren:
+ if (CIK == CIK_DefaultArgument)
+ return true; // End of the default argument.
+ if (ParenCount && !IsFirstToken)
+ return false;
+ Toks.push_back(Tok);
+ ConsumeParen();
+ continue;
+ case tok::r_square:
+ if (BracketCount && !IsFirstToken)
+ return false;
+ Toks.push_back(Tok);
+ ConsumeBracket();
+ continue;
+ case tok::r_brace:
+ if (BraceCount && !IsFirstToken)
+ return false;
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ continue;
+
+ case tok::code_completion:
+ Toks.push_back(Tok);
+ ConsumeCodeCompletionToken();
+ break;
+
+ case tok::string_literal:
+ case tok::wide_string_literal:
+ case tok::utf8_string_literal:
+ case tok::utf16_string_literal:
+ case tok::utf32_string_literal:
+ Toks.push_back(Tok);
+ ConsumeStringToken();
+ break;
+ case tok::semi:
+ if (CIK == CIK_DefaultInitializer)
+ return true; // End of the default initializer.
+ [[fallthrough]];
+ default:
+ consume_token:
+ Toks.push_back(Tok);
+ ConsumeToken();
+ break;
+ }
+ IsFirstToken = false;
+ }
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseDecl.cpp b/contrib/libs/clang16/lib/Parse/ParseDecl.cpp
new file mode 100644
index 0000000000..e6812ac72c
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseDecl.cpp
@@ -0,0 +1,7823 @@
+//===--- ParseDecl.cpp - Declaration Parsing --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Declaration portions of the Parser interfaces.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/PrettyDeclStackTrace.h"
+#include "clang/Basic/AddressSpaces.h"
+#include "clang/Basic/AttributeCommonInfo.h"
+#include "clang/Basic/Attributes.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/ParsedTemplate.h"
+#include "clang/Sema/Scope.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSwitch.h"
+#include <optional>
+
+using namespace clang;
+
+//===----------------------------------------------------------------------===//
+// C99 6.7: Declarations.
+//===----------------------------------------------------------------------===//
+
+/// ParseTypeName
+/// type-name: [C99 6.7.6]
+/// specifier-qualifier-list abstract-declarator[opt]
+///
+/// Called type-id in C++.
+TypeResult Parser::ParseTypeName(SourceRange *Range, DeclaratorContext Context,
+ AccessSpecifier AS, Decl **OwnedType,
+ ParsedAttributes *Attrs) {
+ DeclSpecContext DSC = getDeclSpecContextFromDeclaratorContext(Context);
+ if (DSC == DeclSpecContext::DSC_normal)
+ DSC = DeclSpecContext::DSC_type_specifier;
+
+ // Parse the common declaration-specifiers piece.
+ DeclSpec DS(AttrFactory);
+ if (Attrs)
+ DS.addAttributes(*Attrs);
+ ParseSpecifierQualifierList(DS, AS, DSC);
+ if (OwnedType)
+ *OwnedType = DS.isTypeSpecOwned() ? DS.getRepAsDecl() : nullptr;
+
+ // Parse the abstract-declarator, if present.
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), Context);
+ ParseDeclarator(DeclaratorInfo);
+ if (Range)
+ *Range = DeclaratorInfo.getSourceRange();
+
+ if (DeclaratorInfo.isInvalidType())
+ return true;
+
+ return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+}
+
+/// Normalizes an attribute name by dropping prefixed and suffixed __.
+static StringRef normalizeAttrName(StringRef Name) {
+ if (Name.size() >= 4 && Name.startswith("__") && Name.endswith("__"))
+ return Name.drop_front(2).drop_back(2);
+ return Name;
+}
+
+/// isAttributeLateParsed - Return true if the attribute has arguments that
+/// require late parsing.
+static bool isAttributeLateParsed(const IdentifierInfo &II) {
+#define CLANG_ATTR_LATE_PARSED_LIST
+ return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+ .Default(false);
+#undef CLANG_ATTR_LATE_PARSED_LIST
+}
+
+/// Check if the a start and end source location expand to the same macro.
+static bool FindLocsWithCommonFileID(Preprocessor &PP, SourceLocation StartLoc,
+ SourceLocation EndLoc) {
+ if (!StartLoc.isMacroID() || !EndLoc.isMacroID())
+ return false;
+
+ SourceManager &SM = PP.getSourceManager();
+ if (SM.getFileID(StartLoc) != SM.getFileID(EndLoc))
+ return false;
+
+ bool AttrStartIsInMacro =
+ Lexer::isAtStartOfMacroExpansion(StartLoc, SM, PP.getLangOpts());
+ bool AttrEndIsInMacro =
+ Lexer::isAtEndOfMacroExpansion(EndLoc, SM, PP.getLangOpts());
+ return AttrStartIsInMacro && AttrEndIsInMacro;
+}
+
+void Parser::ParseAttributes(unsigned WhichAttrKinds, ParsedAttributes &Attrs,
+ LateParsedAttrList *LateAttrs) {
+ bool MoreToParse;
+ do {
+ // Assume there's nothing left to parse, but if any attributes are in fact
+ // parsed, loop to ensure all specified attribute combinations are parsed.
+ MoreToParse = false;
+ if (WhichAttrKinds & PAKM_CXX11)
+ MoreToParse |= MaybeParseCXX11Attributes(Attrs);
+ if (WhichAttrKinds & PAKM_GNU)
+ MoreToParse |= MaybeParseGNUAttributes(Attrs, LateAttrs);
+ if (WhichAttrKinds & PAKM_Declspec)
+ MoreToParse |= MaybeParseMicrosoftDeclSpecs(Attrs);
+ } while (MoreToParse);
+}
+
+/// ParseGNUAttributes - Parse a non-empty attributes list.
+///
+/// [GNU] attributes:
+/// attribute
+/// attributes attribute
+///
+/// [GNU] attribute:
+/// '__attribute__' '(' '(' attribute-list ')' ')'
+///
+/// [GNU] attribute-list:
+/// attrib
+/// attribute_list ',' attrib
+///
+/// [GNU] attrib:
+/// empty
+/// attrib-name
+/// attrib-name '(' identifier ')'
+/// attrib-name '(' identifier ',' nonempty-expr-list ')'
+/// attrib-name '(' argument-expression-list [C99 6.5.2] ')'
+///
+/// [GNU] attrib-name:
+/// identifier
+/// typespec
+/// typequal
+/// storageclass
+///
+/// Whether an attribute takes an 'identifier' is determined by the
+/// attrib-name. GCC's behavior here is not worth imitating:
+///
+/// * In C mode, if the attribute argument list starts with an identifier
+/// followed by a ',' or an ')', and the identifier doesn't resolve to
+/// a type, it is parsed as an identifier. If the attribute actually
+/// wanted an expression, it's out of luck (but it turns out that no
+/// attributes work that way, because C constant expressions are very
+/// limited).
+/// * In C++ mode, if the attribute argument list starts with an identifier,
+/// and the attribute *wants* an identifier, it is parsed as an identifier.
+/// At block scope, any additional tokens between the identifier and the
+/// ',' or ')' are ignored, otherwise they produce a parse error.
+///
+/// We follow the C++ model, but don't allow junk after the identifier.
+void Parser::ParseGNUAttributes(ParsedAttributes &Attrs,
+ LateParsedAttrList *LateAttrs, Declarator *D) {
+ assert(Tok.is(tok::kw___attribute) && "Not a GNU attribute list!");
+
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc = StartLoc;
+
+ while (Tok.is(tok::kw___attribute)) {
+ SourceLocation AttrTokLoc = ConsumeToken();
+ unsigned OldNumAttrs = Attrs.size();
+ unsigned OldNumLateAttrs = LateAttrs ? LateAttrs->size() : 0;
+
+ if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
+ "attribute")) {
+ SkipUntil(tok::r_paren, StopAtSemi); // skip until ) or ;
+ return;
+ }
+ if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, "(")) {
+ SkipUntil(tok::r_paren, StopAtSemi); // skip until ) or ;
+ return;
+ }
+ // Parse the attribute-list. e.g. __attribute__(( weak, alias("__f") ))
+ do {
+ // Eat preceeding commas to allow __attribute__((,,,foo))
+ while (TryConsumeToken(tok::comma))
+ ;
+
+ // Expect an identifier or declaration specifier (const, int, etc.)
+ if (Tok.isAnnotation())
+ break;
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteAttribute(AttributeCommonInfo::Syntax::AS_GNU);
+ break;
+ }
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ if (!AttrName)
+ break;
+
+ SourceLocation AttrNameLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::l_paren)) {
+ Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_GNU);
+ continue;
+ }
+
+ // Handle "parameterized" attributes
+ if (!LateAttrs || !isAttributeLateParsed(*AttrName)) {
+ ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, &EndLoc, nullptr,
+ SourceLocation(), ParsedAttr::AS_GNU, D);
+ continue;
+ }
+
+ // Handle attributes with arguments that require late parsing.
+ LateParsedAttribute *LA =
+ new LateParsedAttribute(this, *AttrName, AttrNameLoc);
+ LateAttrs->push_back(LA);
+
+ // Attributes in a class are parsed at the end of the class, along
+ // with other late-parsed declarations.
+ if (!ClassStack.empty() && !LateAttrs->parseSoon())
+ getCurrentClass().LateParsedDeclarations.push_back(LA);
+
+ // Be sure ConsumeAndStoreUntil doesn't see the start l_paren, since it
+ // recursively consumes balanced parens.
+ LA->Toks.push_back(Tok);
+ ConsumeParen();
+ // Consume everything up to and including the matching right parens.
+ ConsumeAndStoreUntil(tok::r_paren, LA->Toks, /*StopAtSemi=*/true);
+
+ Token Eof;
+ Eof.startToken();
+ Eof.setLocation(Tok.getLocation());
+ LA->Toks.push_back(Eof);
+ } while (Tok.is(tok::comma));
+
+ if (ExpectAndConsume(tok::r_paren))
+ SkipUntil(tok::r_paren, StopAtSemi);
+ SourceLocation Loc = Tok.getLocation();
+ if (ExpectAndConsume(tok::r_paren))
+ SkipUntil(tok::r_paren, StopAtSemi);
+ EndLoc = Loc;
+
+ // If this was declared in a macro, attach the macro IdentifierInfo to the
+ // parsed attribute.
+ auto &SM = PP.getSourceManager();
+ if (!SM.isWrittenInBuiltinFile(SM.getSpellingLoc(AttrTokLoc)) &&
+ FindLocsWithCommonFileID(PP, AttrTokLoc, Loc)) {
+ CharSourceRange ExpansionRange = SM.getExpansionRange(AttrTokLoc);
+ StringRef FoundName =
+ Lexer::getSourceText(ExpansionRange, SM, PP.getLangOpts());
+ IdentifierInfo *MacroII = PP.getIdentifierInfo(FoundName);
+
+ for (unsigned i = OldNumAttrs; i < Attrs.size(); ++i)
+ Attrs[i].setMacroIdentifier(MacroII, ExpansionRange.getBegin());
+
+ if (LateAttrs) {
+ for (unsigned i = OldNumLateAttrs; i < LateAttrs->size(); ++i)
+ (*LateAttrs)[i]->MacroII = MacroII;
+ }
+ }
+ }
+
+ Attrs.Range = SourceRange(StartLoc, EndLoc);
+}
+
+/// Determine whether the given attribute has an identifier argument.
+static bool attributeHasIdentifierArg(const IdentifierInfo &II) {
+#define CLANG_ATTR_IDENTIFIER_ARG_LIST
+ return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+ .Default(false);
+#undef CLANG_ATTR_IDENTIFIER_ARG_LIST
+}
+
+/// Determine whether the given attribute has a variadic identifier argument.
+static bool attributeHasVariadicIdentifierArg(const IdentifierInfo &II) {
+#define CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST
+ return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+ .Default(false);
+#undef CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST
+}
+
+/// Determine whether the given attribute treats kw_this as an identifier.
+static bool attributeTreatsKeywordThisAsIdentifier(const IdentifierInfo &II) {
+#define CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST
+ return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+ .Default(false);
+#undef CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST
+}
+
+/// Determine if an attribute accepts parameter packs.
+static bool attributeAcceptsExprPack(const IdentifierInfo &II) {
+#define CLANG_ATTR_ACCEPTS_EXPR_PACK
+ return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+ .Default(false);
+#undef CLANG_ATTR_ACCEPTS_EXPR_PACK
+}
+
+/// Determine whether the given attribute parses a type argument.
+static bool attributeIsTypeArgAttr(const IdentifierInfo &II) {
+#define CLANG_ATTR_TYPE_ARG_LIST
+ return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+ .Default(false);
+#undef CLANG_ATTR_TYPE_ARG_LIST
+}
+
+/// Determine whether the given attribute requires parsing its arguments
+/// in an unevaluated context or not.
+static bool attributeParsedArgsUnevaluated(const IdentifierInfo &II) {
+#define CLANG_ATTR_ARG_CONTEXT_LIST
+ return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+ .Default(false);
+#undef CLANG_ATTR_ARG_CONTEXT_LIST
+}
+
+IdentifierLoc *Parser::ParseIdentifierLoc() {
+ assert(Tok.is(tok::identifier) && "expected an identifier");
+ IdentifierLoc *IL = IdentifierLoc::create(Actions.Context,
+ Tok.getLocation(),
+ Tok.getIdentifierInfo());
+ ConsumeToken();
+ return IL;
+}
+
+void Parser::ParseAttributeWithTypeArg(IdentifierInfo &AttrName,
+ SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs,
+ IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc,
+ ParsedAttr::Syntax Syntax) {
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ Parens.consumeOpen();
+
+ TypeResult T;
+ if (Tok.isNot(tok::r_paren))
+ T = ParseTypeName();
+
+ if (Parens.consumeClose())
+ return;
+
+ if (T.isInvalid())
+ return;
+
+ if (T.isUsable())
+ Attrs.addNewTypeAttr(&AttrName,
+ SourceRange(AttrNameLoc, Parens.getCloseLocation()),
+ ScopeName, ScopeLoc, T.get(), Syntax);
+ else
+ Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, Parens.getCloseLocation()),
+ ScopeName, ScopeLoc, nullptr, 0, Syntax);
+}
+
+unsigned Parser::ParseAttributeArgsCommon(
+ IdentifierInfo *AttrName, SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, ParsedAttr::Syntax Syntax) {
+ // Ignore the left paren location for now.
+ ConsumeParen();
+
+ bool ChangeKWThisToIdent = attributeTreatsKeywordThisAsIdentifier(*AttrName);
+ bool AttributeIsTypeArgAttr = attributeIsTypeArgAttr(*AttrName);
+ bool AttributeHasVariadicIdentifierArg =
+ attributeHasVariadicIdentifierArg(*AttrName);
+
+ // Interpret "kw_this" as an identifier if the attributed requests it.
+ if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
+ Tok.setKind(tok::identifier);
+
+ ArgsVector ArgExprs;
+ if (Tok.is(tok::identifier)) {
+ // If this attribute wants an 'identifier' argument, make it so.
+ bool IsIdentifierArg = AttributeHasVariadicIdentifierArg ||
+ attributeHasIdentifierArg(*AttrName);
+ ParsedAttr::Kind AttrKind =
+ ParsedAttr::getParsedKind(AttrName, ScopeName, Syntax);
+
+ // If we don't know how to parse this attribute, but this is the only
+ // token in this argument, assume it's meant to be an identifier.
+ if (AttrKind == ParsedAttr::UnknownAttribute ||
+ AttrKind == ParsedAttr::IgnoredAttribute) {
+ const Token &Next = NextToken();
+ IsIdentifierArg = Next.isOneOf(tok::r_paren, tok::comma);
+ }
+
+ if (IsIdentifierArg)
+ ArgExprs.push_back(ParseIdentifierLoc());
+ }
+
+ ParsedType TheParsedType;
+ if (!ArgExprs.empty() ? Tok.is(tok::comma) : Tok.isNot(tok::r_paren)) {
+ // Eat the comma.
+ if (!ArgExprs.empty())
+ ConsumeToken();
+
+ if (AttributeIsTypeArgAttr) {
+ // FIXME: Multiple type arguments are not implemented.
+ TypeResult T = ParseTypeName();
+ if (T.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+ if (T.isUsable())
+ TheParsedType = T.get();
+ } else if (AttributeHasVariadicIdentifierArg) {
+ // Parse variadic identifier arg. This can either consume identifiers or
+ // expressions. Variadic identifier args do not support parameter packs
+ // because those are typically used for attributes with enumeration
+ // arguments, and those enumerations are not something the user could
+ // express via a pack.
+ do {
+ // Interpret "kw_this" as an identifier if the attributed requests it.
+ if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
+ Tok.setKind(tok::identifier);
+
+ ExprResult ArgExpr;
+ if (Tok.is(tok::identifier)) {
+ ArgExprs.push_back(ParseIdentifierLoc());
+ } else {
+ bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions,
+ Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
+ : Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ ExprResult ArgExpr(
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
+
+ if (ArgExpr.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+ ArgExprs.push_back(ArgExpr.get());
+ }
+ // Eat the comma, move to the next argument
+ } while (TryConsumeToken(tok::comma));
+ } else {
+ // General case. Parse all available expressions.
+ bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Uneval
+ ? Sema::ExpressionEvaluationContext::Unevaluated
+ : Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ ExprVector ParsedExprs;
+ if (ParseExpressionList(ParsedExprs, llvm::function_ref<void()>(),
+ /*FailImmediatelyOnInvalidExpr=*/true,
+ /*EarlyTypoCorrection=*/true)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+
+ // Pack expansion must currently be explicitly supported by an attribute.
+ for (size_t I = 0; I < ParsedExprs.size(); ++I) {
+ if (!isa<PackExpansionExpr>(ParsedExprs[I]))
+ continue;
+
+ if (!attributeAcceptsExprPack(*AttrName)) {
+ Diag(Tok.getLocation(),
+ diag::err_attribute_argument_parm_pack_not_supported)
+ << AttrName;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+ }
+
+ ArgExprs.insert(ArgExprs.end(), ParsedExprs.begin(), ParsedExprs.end());
+ }
+ }
+
+ SourceLocation RParen = Tok.getLocation();
+ if (!ExpectAndConsume(tok::r_paren)) {
+ SourceLocation AttrLoc = ScopeLoc.isValid() ? ScopeLoc : AttrNameLoc;
+
+ if (AttributeIsTypeArgAttr && !TheParsedType.get().isNull()) {
+ Attrs.addNewTypeAttr(AttrName, SourceRange(AttrNameLoc, RParen),
+ ScopeName, ScopeLoc, TheParsedType, Syntax);
+ } else {
+ Attrs.addNew(AttrName, SourceRange(AttrLoc, RParen), ScopeName, ScopeLoc,
+ ArgExprs.data(), ArgExprs.size(), Syntax);
+ }
+ }
+
+ if (EndLoc)
+ *EndLoc = RParen;
+
+ return static_cast<unsigned>(ArgExprs.size() + !TheParsedType.get().isNull());
+}
+
+/// Parse the arguments to a parameterized GNU attribute or
+/// a C++11 attribute in "gnu" namespace.
+void Parser::ParseGNUAttributeArgs(
+ IdentifierInfo *AttrName, SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, ParsedAttr::Syntax Syntax, Declarator *D) {
+
+ assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
+
+ ParsedAttr::Kind AttrKind =
+ ParsedAttr::getParsedKind(AttrName, ScopeName, Syntax);
+
+ if (AttrKind == ParsedAttr::AT_Availability) {
+ ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
+ ScopeLoc, Syntax);
+ return;
+ } else if (AttrKind == ParsedAttr::AT_ExternalSourceSymbol) {
+ ParseExternalSourceSymbolAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ return;
+ } else if (AttrKind == ParsedAttr::AT_ObjCBridgeRelated) {
+ ParseObjCBridgeRelatedAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ return;
+ } else if (AttrKind == ParsedAttr::AT_SwiftNewType) {
+ ParseSwiftNewTypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
+ ScopeLoc, Syntax);
+ return;
+ } else if (AttrKind == ParsedAttr::AT_TypeTagForDatatype) {
+ ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ return;
+ } else if (attributeIsTypeArgAttr(*AttrName)) {
+ ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName,
+ ScopeLoc, Syntax);
+ return;
+ }
+
+ // These may refer to the function arguments, but need to be parsed early to
+ // participate in determining whether it's a redeclaration.
+ std::optional<ParseScope> PrototypeScope;
+ if (normalizeAttrName(AttrName->getName()) == "enable_if" &&
+ D && D->isFunctionDeclarator()) {
+ DeclaratorChunk::FunctionTypeInfo FTI = D->getFunctionTypeInfo();
+ PrototypeScope.emplace(this, Scope::FunctionPrototypeScope |
+ Scope::FunctionDeclarationScope |
+ Scope::DeclScope);
+ for (unsigned i = 0; i != FTI.NumParams; ++i) {
+ ParmVarDecl *Param = cast<ParmVarDecl>(FTI.Params[i].Param);
+ Actions.ActOnReenterCXXMethodParameter(getCurScope(), Param);
+ }
+ }
+
+ ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
+ ScopeLoc, Syntax);
+}
+
+unsigned Parser::ParseClangAttributeArgs(
+ IdentifierInfo *AttrName, SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, ParsedAttr::Syntax Syntax) {
+ assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
+
+ ParsedAttr::Kind AttrKind =
+ ParsedAttr::getParsedKind(AttrName, ScopeName, Syntax);
+
+ switch (AttrKind) {
+ default:
+ return ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ case ParsedAttr::AT_ExternalSourceSymbol:
+ ParseExternalSourceSymbolAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ break;
+ case ParsedAttr::AT_Availability:
+ ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
+ ScopeLoc, Syntax);
+ break;
+ case ParsedAttr::AT_ObjCBridgeRelated:
+ ParseObjCBridgeRelatedAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ break;
+ case ParsedAttr::AT_SwiftNewType:
+ ParseSwiftNewTypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
+ ScopeLoc, Syntax);
+ break;
+ case ParsedAttr::AT_TypeTagForDatatype:
+ ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ break;
+ }
+ return !Attrs.empty() ? Attrs.begin()->getNumArgs() : 0;
+}
+
+bool Parser::ParseMicrosoftDeclSpecArgs(IdentifierInfo *AttrName,
+ SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs) {
+ unsigned ExistingAttrs = Attrs.size();
+
+ // If the attribute isn't known, we will not attempt to parse any
+ // arguments.
+ if (!hasAttribute(AttributeCommonInfo::Syntax::AS_Declspec, nullptr, AttrName,
+ getTargetInfo(), getLangOpts())) {
+ // Eat the left paren, then skip to the ending right paren.
+ ConsumeParen();
+ SkipUntil(tok::r_paren);
+ return false;
+ }
+
+ SourceLocation OpenParenLoc = Tok.getLocation();
+
+ if (AttrName->getName() == "property") {
+ // The property declspec is more complex in that it can take one or two
+ // assignment expressions as a parameter, but the lhs of the assignment
+ // must be named get or put.
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.expectAndConsume(diag::err_expected_lparen_after,
+ AttrName->getNameStart(), tok::r_paren);
+
+ enum AccessorKind {
+ AK_Invalid = -1,
+ AK_Put = 0,
+ AK_Get = 1 // indices into AccessorNames
+ };
+ IdentifierInfo *AccessorNames[] = {nullptr, nullptr};
+ bool HasInvalidAccessor = false;
+
+ // Parse the accessor specifications.
+ while (true) {
+ // Stop if this doesn't look like an accessor spec.
+ if (!Tok.is(tok::identifier)) {
+ // If the user wrote a completely empty list, use a special diagnostic.
+ if (Tok.is(tok::r_paren) && !HasInvalidAccessor &&
+ AccessorNames[AK_Put] == nullptr &&
+ AccessorNames[AK_Get] == nullptr) {
+ Diag(AttrNameLoc, diag::err_ms_property_no_getter_or_putter);
+ break;
+ }
+
+ Diag(Tok.getLocation(), diag::err_ms_property_unknown_accessor);
+ break;
+ }
+
+ AccessorKind Kind;
+ SourceLocation KindLoc = Tok.getLocation();
+ StringRef KindStr = Tok.getIdentifierInfo()->getName();
+ if (KindStr == "get") {
+ Kind = AK_Get;
+ } else if (KindStr == "put") {
+ Kind = AK_Put;
+
+ // Recover from the common mistake of using 'set' instead of 'put'.
+ } else if (KindStr == "set") {
+ Diag(KindLoc, diag::err_ms_property_has_set_accessor)
+ << FixItHint::CreateReplacement(KindLoc, "put");
+ Kind = AK_Put;
+
+ // Handle the mistake of forgetting the accessor kind by skipping
+ // this accessor.
+ } else if (NextToken().is(tok::comma) || NextToken().is(tok::r_paren)) {
+ Diag(KindLoc, diag::err_ms_property_missing_accessor_kind);
+ ConsumeToken();
+ HasInvalidAccessor = true;
+ goto next_property_accessor;
+
+ // Otherwise, complain about the unknown accessor kind.
+ } else {
+ Diag(KindLoc, diag::err_ms_property_unknown_accessor);
+ HasInvalidAccessor = true;
+ Kind = AK_Invalid;
+
+ // Try to keep parsing unless it doesn't look like an accessor spec.
+ if (!NextToken().is(tok::equal))
+ break;
+ }
+
+ // Consume the identifier.
+ ConsumeToken();
+
+ // Consume the '='.
+ if (!TryConsumeToken(tok::equal)) {
+ Diag(Tok.getLocation(), diag::err_ms_property_expected_equal)
+ << KindStr;
+ break;
+ }
+
+ // Expect the method name.
+ if (!Tok.is(tok::identifier)) {
+ Diag(Tok.getLocation(), diag::err_ms_property_expected_accessor_name);
+ break;
+ }
+
+ if (Kind == AK_Invalid) {
+ // Just drop invalid accessors.
+ } else if (AccessorNames[Kind] != nullptr) {
+ // Complain about the repeated accessor, ignore it, and keep parsing.
+ Diag(KindLoc, diag::err_ms_property_duplicate_accessor) << KindStr;
+ } else {
+ AccessorNames[Kind] = Tok.getIdentifierInfo();
+ }
+ ConsumeToken();
+
+ next_property_accessor:
+ // Keep processing accessors until we run out.
+ if (TryConsumeToken(tok::comma))
+ continue;
+
+ // If we run into the ')', stop without consuming it.
+ if (Tok.is(tok::r_paren))
+ break;
+
+ Diag(Tok.getLocation(), diag::err_ms_property_expected_comma_or_rparen);
+ break;
+ }
+
+ // Only add the property attribute if it was well-formed.
+ if (!HasInvalidAccessor)
+ Attrs.addNewPropertyAttr(AttrName, AttrNameLoc, nullptr, SourceLocation(),
+ AccessorNames[AK_Get], AccessorNames[AK_Put],
+ ParsedAttr::AS_Declspec);
+ T.skipToEnd();
+ return !HasInvalidAccessor;
+ }
+
+ unsigned NumArgs =
+ ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, nullptr, nullptr,
+ SourceLocation(), ParsedAttr::AS_Declspec);
+
+ // If this attribute's args were parsed, and it was expected to have
+ // arguments but none were provided, emit a diagnostic.
+ if (ExistingAttrs < Attrs.size() && Attrs.back().getMaxArgs() && !NumArgs) {
+ Diag(OpenParenLoc, diag::err_attribute_requires_arguments) << AttrName;
+ return false;
+ }
+ return true;
+}
+
+/// [MS] decl-specifier:
+/// __declspec ( extended-decl-modifier-seq )
+///
+/// [MS] extended-decl-modifier-seq:
+/// extended-decl-modifier[opt]
+/// extended-decl-modifier extended-decl-modifier-seq
+void Parser::ParseMicrosoftDeclSpecs(ParsedAttributes &Attrs) {
+ assert(getLangOpts().DeclSpecKeyword && "__declspec keyword is not enabled");
+ assert(Tok.is(tok::kw___declspec) && "Not a declspec!");
+
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc = StartLoc;
+
+ while (Tok.is(tok::kw___declspec)) {
+ ConsumeToken();
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "__declspec",
+ tok::r_paren))
+ return;
+
+ // An empty declspec is perfectly legal and should not warn. Additionally,
+ // you can specify multiple attributes per declspec.
+ while (Tok.isNot(tok::r_paren)) {
+ // Attribute not present.
+ if (TryConsumeToken(tok::comma))
+ continue;
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteAttribute(AttributeCommonInfo::AS_Declspec);
+ return;
+ }
+
+ // We expect either a well-known identifier or a generic string. Anything
+ // else is a malformed declspec.
+ bool IsString = Tok.getKind() == tok::string_literal;
+ if (!IsString && Tok.getKind() != tok::identifier &&
+ Tok.getKind() != tok::kw_restrict) {
+ Diag(Tok, diag::err_ms_declspec_type);
+ T.skipToEnd();
+ return;
+ }
+
+ IdentifierInfo *AttrName;
+ SourceLocation AttrNameLoc;
+ if (IsString) {
+ SmallString<8> StrBuffer;
+ bool Invalid = false;
+ StringRef Str = PP.getSpelling(Tok, StrBuffer, &Invalid);
+ if (Invalid) {
+ T.skipToEnd();
+ return;
+ }
+ AttrName = PP.getIdentifierInfo(Str);
+ AttrNameLoc = ConsumeStringToken();
+ } else {
+ AttrName = Tok.getIdentifierInfo();
+ AttrNameLoc = ConsumeToken();
+ }
+
+ bool AttrHandled = false;
+
+ // Parse attribute arguments.
+ if (Tok.is(tok::l_paren))
+ AttrHandled = ParseMicrosoftDeclSpecArgs(AttrName, AttrNameLoc, Attrs);
+ else if (AttrName->getName() == "property")
+ // The property attribute must have an argument list.
+ Diag(Tok.getLocation(), diag::err_expected_lparen_after)
+ << AttrName->getName();
+
+ if (!AttrHandled)
+ Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Declspec);
+ }
+ T.consumeClose();
+ EndLoc = T.getCloseLocation();
+ }
+
+ Attrs.Range = SourceRange(StartLoc, EndLoc);
+}
+
+void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) {
+ // Treat these like attributes
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::kw___fastcall:
+ case tok::kw___stdcall:
+ case tok::kw___thiscall:
+ case tok::kw___regcall:
+ case tok::kw___cdecl:
+ case tok::kw___vectorcall:
+ case tok::kw___ptr64:
+ case tok::kw___w64:
+ case tok::kw___ptr32:
+ case tok::kw___sptr:
+ case tok::kw___uptr: {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+ break;
+ }
+ default:
+ return;
+ }
+ }
+}
+
+void Parser::DiagnoseAndSkipExtendedMicrosoftTypeAttributes() {
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc = SkipExtendedMicrosoftTypeAttributes();
+
+ if (EndLoc.isValid()) {
+ SourceRange Range(StartLoc, EndLoc);
+ Diag(StartLoc, diag::warn_microsoft_qualifiers_ignored) << Range;
+ }
+}
+
+SourceLocation Parser::SkipExtendedMicrosoftTypeAttributes() {
+ SourceLocation EndLoc;
+
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::kw_const:
+ case tok::kw_volatile:
+ case tok::kw___fastcall:
+ case tok::kw___stdcall:
+ case tok::kw___thiscall:
+ case tok::kw___cdecl:
+ case tok::kw___vectorcall:
+ case tok::kw___ptr32:
+ case tok::kw___ptr64:
+ case tok::kw___w64:
+ case tok::kw___unaligned:
+ case tok::kw___sptr:
+ case tok::kw___uptr:
+ EndLoc = ConsumeToken();
+ break;
+ default:
+ return EndLoc;
+ }
+ }
+}
+
+void Parser::ParseBorlandTypeAttributes(ParsedAttributes &attrs) {
+ // Treat these like attributes
+ while (Tok.is(tok::kw___pascal)) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+ }
+}
+
+void Parser::ParseOpenCLKernelAttributes(ParsedAttributes &attrs) {
+ // Treat these like attributes
+ while (Tok.is(tok::kw___kernel)) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+ }
+}
+
+void Parser::ParseCUDAFunctionAttributes(ParsedAttributes &attrs) {
+ while (Tok.is(tok::kw___noinline__)) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+ }
+}
+
+void Parser::ParseOpenCLQualifiers(ParsedAttributes &Attrs) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = Tok.getLocation();
+ Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+}
+
+bool Parser::isHLSLQualifier(const Token &Tok) const {
+ return Tok.is(tok::kw_groupshared);
+}
+
+void Parser::ParseHLSLQualifiers(ParsedAttributes &Attrs) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+}
+
+void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) {
+ // Treat these like attributes, even though they're type specifiers.
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::kw__Nonnull:
+ case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
+ case tok::kw__Null_unspecified: {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ if (!getLangOpts().ObjC)
+ Diag(AttrNameLoc, diag::ext_nullability)
+ << AttrName;
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+ break;
+ }
+ default:
+ return;
+ }
+ }
+}
+
+static bool VersionNumberSeparator(const char Separator) {
+ return (Separator == '.' || Separator == '_');
+}
+
+/// Parse a version number.
+///
+/// version:
+/// simple-integer
+/// simple-integer '.' simple-integer
+/// simple-integer '_' simple-integer
+/// simple-integer '.' simple-integer '.' simple-integer
+/// simple-integer '_' simple-integer '_' simple-integer
+VersionTuple Parser::ParseVersionTuple(SourceRange &Range) {
+ Range = SourceRange(Tok.getLocation(), Tok.getEndLoc());
+
+ if (!Tok.is(tok::numeric_constant)) {
+ Diag(Tok, diag::err_expected_version);
+ SkipUntil(tok::comma, tok::r_paren,
+ StopAtSemi | StopBeforeMatch | StopAtCodeCompletion);
+ return VersionTuple();
+ }
+
+ // Parse the major (and possibly minor and subminor) versions, which
+ // are stored in the numeric constant. We utilize a quirk of the
+ // lexer, which is that it handles something like 1.2.3 as a single
+ // numeric constant, rather than two separate tokens.
+ SmallString<512> Buffer;
+ Buffer.resize(Tok.getLength()+1);
+ const char *ThisTokBegin = &Buffer[0];
+
+ // Get the spelling of the token, which eliminates trigraphs, etc.
+ bool Invalid = false;
+ unsigned ActualLength = PP.getSpelling(Tok, ThisTokBegin, &Invalid);
+ if (Invalid)
+ return VersionTuple();
+
+ // Parse the major version.
+ unsigned AfterMajor = 0;
+ unsigned Major = 0;
+ while (AfterMajor < ActualLength && isDigit(ThisTokBegin[AfterMajor])) {
+ Major = Major * 10 + ThisTokBegin[AfterMajor] - '0';
+ ++AfterMajor;
+ }
+
+ if (AfterMajor == 0) {
+ Diag(Tok, diag::err_expected_version);
+ SkipUntil(tok::comma, tok::r_paren,
+ StopAtSemi | StopBeforeMatch | StopAtCodeCompletion);
+ return VersionTuple();
+ }
+
+ if (AfterMajor == ActualLength) {
+ ConsumeToken();
+
+ // We only had a single version component.
+ if (Major == 0) {
+ Diag(Tok, diag::err_zero_version);
+ return VersionTuple();
+ }
+
+ return VersionTuple(Major);
+ }
+
+ const char AfterMajorSeparator = ThisTokBegin[AfterMajor];
+ if (!VersionNumberSeparator(AfterMajorSeparator)
+ || (AfterMajor + 1 == ActualLength)) {
+ Diag(Tok, diag::err_expected_version);
+ SkipUntil(tok::comma, tok::r_paren,
+ StopAtSemi | StopBeforeMatch | StopAtCodeCompletion);
+ return VersionTuple();
+ }
+
+ // Parse the minor version.
+ unsigned AfterMinor = AfterMajor + 1;
+ unsigned Minor = 0;
+ while (AfterMinor < ActualLength && isDigit(ThisTokBegin[AfterMinor])) {
+ Minor = Minor * 10 + ThisTokBegin[AfterMinor] - '0';
+ ++AfterMinor;
+ }
+
+ if (AfterMinor == ActualLength) {
+ ConsumeToken();
+
+ // We had major.minor.
+ if (Major == 0 && Minor == 0) {
+ Diag(Tok, diag::err_zero_version);
+ return VersionTuple();
+ }
+
+ return VersionTuple(Major, Minor);
+ }
+
+ const char AfterMinorSeparator = ThisTokBegin[AfterMinor];
+ // If what follows is not a '.' or '_', we have a problem.
+ if (!VersionNumberSeparator(AfterMinorSeparator)) {
+ Diag(Tok, diag::err_expected_version);
+ SkipUntil(tok::comma, tok::r_paren,
+ StopAtSemi | StopBeforeMatch | StopAtCodeCompletion);
+ return VersionTuple();
+ }
+
+ // Warn if separators, be it '.' or '_', do not match.
+ if (AfterMajorSeparator != AfterMinorSeparator)
+ Diag(Tok, diag::warn_expected_consistent_version_separator);
+
+ // Parse the subminor version.
+ unsigned AfterSubminor = AfterMinor + 1;
+ unsigned Subminor = 0;
+ while (AfterSubminor < ActualLength && isDigit(ThisTokBegin[AfterSubminor])) {
+ Subminor = Subminor * 10 + ThisTokBegin[AfterSubminor] - '0';
+ ++AfterSubminor;
+ }
+
+ if (AfterSubminor != ActualLength) {
+ Diag(Tok, diag::err_expected_version);
+ SkipUntil(tok::comma, tok::r_paren,
+ StopAtSemi | StopBeforeMatch | StopAtCodeCompletion);
+ return VersionTuple();
+ }
+ ConsumeToken();
+ return VersionTuple(Major, Minor, Subminor);
+}
+
+/// Parse the contents of the "availability" attribute.
+///
+/// availability-attribute:
+/// 'availability' '(' platform ',' opt-strict version-arg-list,
+/// opt-replacement, opt-message')'
+///
+/// platform:
+/// identifier
+///
+/// opt-strict:
+/// 'strict' ','
+///
+/// version-arg-list:
+/// version-arg
+/// version-arg ',' version-arg-list
+///
+/// version-arg:
+/// 'introduced' '=' version
+/// 'deprecated' '=' version
+/// 'obsoleted' = version
+/// 'unavailable'
+/// opt-replacement:
+/// 'replacement' '=' <string>
+/// opt-message:
+/// 'message' '=' <string>
+void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
+ SourceLocation AvailabilityLoc,
+ ParsedAttributes &attrs,
+ SourceLocation *endLoc,
+ IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc,
+ ParsedAttr::Syntax Syntax) {
+ enum { Introduced, Deprecated, Obsoleted, Unknown };
+ AvailabilityChange Changes[Unknown];
+ ExprResult MessageExpr, ReplacementExpr;
+
+ // Opening '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return;
+ }
+
+ // Parse the platform name.
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_availability_expected_platform);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ IdentifierLoc *Platform = ParseIdentifierLoc();
+ if (const IdentifierInfo *const Ident = Platform->Ident) {
+ // Canonicalize platform name from "macosx" to "macos".
+ if (Ident->getName() == "macosx")
+ Platform->Ident = PP.getIdentifierInfo("macos");
+ // Canonicalize platform name from "macosx_app_extension" to
+ // "macos_app_extension".
+ else if (Ident->getName() == "macosx_app_extension")
+ Platform->Ident = PP.getIdentifierInfo("macos_app_extension");
+ else
+ Platform->Ident = PP.getIdentifierInfo(
+ AvailabilityAttr::canonicalizePlatformName(Ident->getName()));
+ }
+
+ // Parse the ',' following the platform name.
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ // If we haven't grabbed the pointers for the identifiers
+ // "introduced", "deprecated", and "obsoleted", do so now.
+ if (!Ident_introduced) {
+ Ident_introduced = PP.getIdentifierInfo("introduced");
+ Ident_deprecated = PP.getIdentifierInfo("deprecated");
+ Ident_obsoleted = PP.getIdentifierInfo("obsoleted");
+ Ident_unavailable = PP.getIdentifierInfo("unavailable");
+ Ident_message = PP.getIdentifierInfo("message");
+ Ident_strict = PP.getIdentifierInfo("strict");
+ Ident_replacement = PP.getIdentifierInfo("replacement");
+ }
+
+ // Parse the optional "strict", the optional "replacement" and the set of
+ // introductions/deprecations/removals.
+ SourceLocation UnavailableLoc, StrictLoc;
+ do {
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_availability_expected_change);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ IdentifierInfo *Keyword = Tok.getIdentifierInfo();
+ SourceLocation KeywordLoc = ConsumeToken();
+
+ if (Keyword == Ident_strict) {
+ if (StrictLoc.isValid()) {
+ Diag(KeywordLoc, diag::err_availability_redundant)
+ << Keyword << SourceRange(StrictLoc);
+ }
+ StrictLoc = KeywordLoc;
+ continue;
+ }
+
+ if (Keyword == Ident_unavailable) {
+ if (UnavailableLoc.isValid()) {
+ Diag(KeywordLoc, diag::err_availability_redundant)
+ << Keyword << SourceRange(UnavailableLoc);
+ }
+ UnavailableLoc = KeywordLoc;
+ continue;
+ }
+
+ if (Keyword == Ident_deprecated && Platform->Ident &&
+ Platform->Ident->isStr("swift")) {
+ // For swift, we deprecate for all versions.
+ if (Changes[Deprecated].KeywordLoc.isValid()) {
+ Diag(KeywordLoc, diag::err_availability_redundant)
+ << Keyword
+ << SourceRange(Changes[Deprecated].KeywordLoc);
+ }
+
+ Changes[Deprecated].KeywordLoc = KeywordLoc;
+ // Use a fake version here.
+ Changes[Deprecated].Version = VersionTuple(1);
+ continue;
+ }
+
+ if (Tok.isNot(tok::equal)) {
+ Diag(Tok, diag::err_expected_after) << Keyword << tok::equal;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ ConsumeToken();
+ if (Keyword == Ident_message || Keyword == Ident_replacement) {
+ if (Tok.isNot(tok::string_literal)) {
+ Diag(Tok, diag::err_expected_string_literal)
+ << /*Source='availability attribute'*/2;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ if (Keyword == Ident_message)
+ MessageExpr = ParseStringLiteralExpression();
+ else
+ ReplacementExpr = ParseStringLiteralExpression();
+ // Also reject wide string literals.
+ if (StringLiteral *MessageStringLiteral =
+ cast_or_null<StringLiteral>(MessageExpr.get())) {
+ if (!MessageStringLiteral->isOrdinary()) {
+ Diag(MessageStringLiteral->getSourceRange().getBegin(),
+ diag::err_expected_string_literal)
+ << /*Source='availability attribute'*/ 2;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ }
+ if (Keyword == Ident_message)
+ break;
+ else
+ continue;
+ }
+
+ // Special handling of 'NA' only when applied to introduced or
+ // deprecated.
+ if ((Keyword == Ident_introduced || Keyword == Ident_deprecated) &&
+ Tok.is(tok::identifier)) {
+ IdentifierInfo *NA = Tok.getIdentifierInfo();
+ if (NA->getName() == "NA") {
+ ConsumeToken();
+ if (Keyword == Ident_introduced)
+ UnavailableLoc = KeywordLoc;
+ continue;
+ }
+ }
+
+ SourceRange VersionRange;
+ VersionTuple Version = ParseVersionTuple(VersionRange);
+
+ if (Version.empty()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ unsigned Index;
+ if (Keyword == Ident_introduced)
+ Index = Introduced;
+ else if (Keyword == Ident_deprecated)
+ Index = Deprecated;
+ else if (Keyword == Ident_obsoleted)
+ Index = Obsoleted;
+ else
+ Index = Unknown;
+
+ if (Index < Unknown) {
+ if (!Changes[Index].KeywordLoc.isInvalid()) {
+ Diag(KeywordLoc, diag::err_availability_redundant)
+ << Keyword
+ << SourceRange(Changes[Index].KeywordLoc,
+ Changes[Index].VersionRange.getEnd());
+ }
+
+ Changes[Index].KeywordLoc = KeywordLoc;
+ Changes[Index].Version = Version;
+ Changes[Index].VersionRange = VersionRange;
+ } else {
+ Diag(KeywordLoc, diag::err_availability_unknown_change)
+ << Keyword << VersionRange;
+ }
+
+ } while (TryConsumeToken(tok::comma));
+
+ // Closing ')'.
+ if (T.consumeClose())
+ return;
+
+ if (endLoc)
+ *endLoc = T.getCloseLocation();
+
+ // The 'unavailable' availability cannot be combined with any other
+ // availability changes. Make sure that hasn't happened.
+ if (UnavailableLoc.isValid()) {
+ bool Complained = false;
+ for (unsigned Index = Introduced; Index != Unknown; ++Index) {
+ if (Changes[Index].KeywordLoc.isValid()) {
+ if (!Complained) {
+ Diag(UnavailableLoc, diag::warn_availability_and_unavailable)
+ << SourceRange(Changes[Index].KeywordLoc,
+ Changes[Index].VersionRange.getEnd());
+ Complained = true;
+ }
+
+ // Clear out the availability.
+ Changes[Index] = AvailabilityChange();
+ }
+ }
+ }
+
+ // Record this attribute
+ attrs.addNew(&Availability,
+ SourceRange(AvailabilityLoc, T.getCloseLocation()),
+ ScopeName, ScopeLoc,
+ Platform,
+ Changes[Introduced],
+ Changes[Deprecated],
+ Changes[Obsoleted],
+ UnavailableLoc, MessageExpr.get(),
+ Syntax, StrictLoc, ReplacementExpr.get());
+}
+
+/// Parse the contents of the "external_source_symbol" attribute.
+///
+/// external-source-symbol-attribute:
+/// 'external_source_symbol' '(' keyword-arg-list ')'
+///
+/// keyword-arg-list:
+/// keyword-arg
+/// keyword-arg ',' keyword-arg-list
+///
+/// keyword-arg:
+/// 'language' '=' <string>
+/// 'defined_in' '=' <string>
+/// 'generated_declaration'
+void Parser::ParseExternalSourceSymbolAttribute(
+ IdentifierInfo &ExternalSourceSymbol, SourceLocation Loc,
+ ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, ParsedAttr::Syntax Syntax) {
+ // Opening '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume())
+ return;
+
+ // Initialize the pointers for the keyword identifiers when required.
+ if (!Ident_language) {
+ Ident_language = PP.getIdentifierInfo("language");
+ Ident_defined_in = PP.getIdentifierInfo("defined_in");
+ Ident_generated_declaration = PP.getIdentifierInfo("generated_declaration");
+ }
+
+ ExprResult Language;
+ bool HasLanguage = false;
+ ExprResult DefinedInExpr;
+ bool HasDefinedIn = false;
+ IdentifierLoc *GeneratedDeclaration = nullptr;
+
+ // Parse the language/defined_in/generated_declaration keywords
+ do {
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_external_source_symbol_expected_keyword);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ SourceLocation KeywordLoc = Tok.getLocation();
+ IdentifierInfo *Keyword = Tok.getIdentifierInfo();
+ if (Keyword == Ident_generated_declaration) {
+ if (GeneratedDeclaration) {
+ Diag(Tok, diag::err_external_source_symbol_duplicate_clause) << Keyword;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ GeneratedDeclaration = ParseIdentifierLoc();
+ continue;
+ }
+
+ if (Keyword != Ident_language && Keyword != Ident_defined_in) {
+ Diag(Tok, diag::err_external_source_symbol_expected_keyword);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ ConsumeToken();
+ if (ExpectAndConsume(tok::equal, diag::err_expected_after,
+ Keyword->getName())) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ bool HadLanguage = HasLanguage, HadDefinedIn = HasDefinedIn;
+ if (Keyword == Ident_language)
+ HasLanguage = true;
+ else
+ HasDefinedIn = true;
+
+ if (Tok.isNot(tok::string_literal)) {
+ Diag(Tok, diag::err_expected_string_literal)
+ << /*Source='external_source_symbol attribute'*/ 3
+ << /*language | source container*/ (Keyword != Ident_language);
+ SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
+ continue;
+ }
+ if (Keyword == Ident_language) {
+ if (HadLanguage) {
+ Diag(KeywordLoc, diag::err_external_source_symbol_duplicate_clause)
+ << Keyword;
+ ParseStringLiteralExpression();
+ continue;
+ }
+ Language = ParseStringLiteralExpression();
+ } else {
+ assert(Keyword == Ident_defined_in && "Invalid clause keyword!");
+ if (HadDefinedIn) {
+ Diag(KeywordLoc, diag::err_external_source_symbol_duplicate_clause)
+ << Keyword;
+ ParseStringLiteralExpression();
+ continue;
+ }
+ DefinedInExpr = ParseStringLiteralExpression();
+ }
+ } while (TryConsumeToken(tok::comma));
+
+ // Closing ')'.
+ if (T.consumeClose())
+ return;
+ if (EndLoc)
+ *EndLoc = T.getCloseLocation();
+
+ ArgsUnion Args[] = {Language.get(), DefinedInExpr.get(),
+ GeneratedDeclaration};
+ Attrs.addNew(&ExternalSourceSymbol, SourceRange(Loc, T.getCloseLocation()),
+ ScopeName, ScopeLoc, Args, std::size(Args), Syntax);
+}
+
+/// Parse the contents of the "objc_bridge_related" attribute.
+/// objc_bridge_related '(' related_class ',' opt-class_method ',' opt-instance_method ')'
+/// related_class:
+/// Identifier
+///
+/// opt-class_method:
+/// Identifier: | <empty>
+///
+/// opt-instance_method:
+/// Identifier | <empty>
+///
+void Parser::ParseObjCBridgeRelatedAttribute(
+ IdentifierInfo &ObjCBridgeRelated, SourceLocation ObjCBridgeRelatedLoc,
+ ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, ParsedAttr::Syntax Syntax) {
+ // Opening '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return;
+ }
+
+ // Parse the related class name.
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_objcbridge_related_expected_related_class);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ IdentifierLoc *RelatedClass = ParseIdentifierLoc();
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ // Parse class method name. It's non-optional in the sense that a trailing
+ // comma is required, but it can be the empty string, and then we record a
+ // nullptr.
+ IdentifierLoc *ClassMethod = nullptr;
+ if (Tok.is(tok::identifier)) {
+ ClassMethod = ParseIdentifierLoc();
+ if (!TryConsumeToken(tok::colon)) {
+ Diag(Tok, diag::err_objcbridge_related_selector_name);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ }
+ if (!TryConsumeToken(tok::comma)) {
+ if (Tok.is(tok::colon))
+ Diag(Tok, diag::err_objcbridge_related_selector_name);
+ else
+ Diag(Tok, diag::err_expected) << tok::comma;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ // Parse instance method name. Also non-optional but empty string is
+ // permitted.
+ IdentifierLoc *InstanceMethod = nullptr;
+ if (Tok.is(tok::identifier))
+ InstanceMethod = ParseIdentifierLoc();
+ else if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ // Closing ')'.
+ if (T.consumeClose())
+ return;
+
+ if (EndLoc)
+ *EndLoc = T.getCloseLocation();
+
+ // Record this attribute
+ Attrs.addNew(&ObjCBridgeRelated,
+ SourceRange(ObjCBridgeRelatedLoc, T.getCloseLocation()),
+ ScopeName, ScopeLoc, RelatedClass, ClassMethod, InstanceMethod,
+ Syntax);
+}
+
+void Parser::ParseSwiftNewTypeAttribute(
+ IdentifierInfo &AttrName, SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, ParsedAttr::Syntax Syntax) {
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+
+ // Opening '('
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return;
+ }
+
+ if (Tok.is(tok::r_paren)) {
+ Diag(Tok.getLocation(), diag::err_argument_required_after_attribute);
+ T.consumeClose();
+ return;
+ }
+ if (Tok.isNot(tok::kw_struct) && Tok.isNot(tok::kw_enum)) {
+ Diag(Tok, diag::warn_attribute_type_not_supported)
+ << &AttrName << Tok.getIdentifierInfo();
+ if (!isTokenSpecial())
+ ConsumeToken();
+ T.consumeClose();
+ return;
+ }
+
+ auto *SwiftType = IdentifierLoc::create(Actions.Context, Tok.getLocation(),
+ Tok.getIdentifierInfo());
+ ConsumeToken();
+
+ // Closing ')'
+ if (T.consumeClose())
+ return;
+ if (EndLoc)
+ *EndLoc = T.getCloseLocation();
+
+ ArgsUnion Args[] = {SwiftType};
+ Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, T.getCloseLocation()),
+ ScopeName, ScopeLoc, Args, std::size(Args), Syntax);
+}
+
+void Parser::ParseTypeTagForDatatypeAttribute(IdentifierInfo &AttrName,
+ SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs,
+ SourceLocation *EndLoc,
+ IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc,
+ ParsedAttr::Syntax Syntax) {
+ assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ T.skipToEnd();
+ return;
+ }
+ IdentifierLoc *ArgumentKind = ParseIdentifierLoc();
+
+ if (ExpectAndConsume(tok::comma)) {
+ T.skipToEnd();
+ return;
+ }
+
+ SourceRange MatchingCTypeRange;
+ TypeResult MatchingCType = ParseTypeName(&MatchingCTypeRange);
+ if (MatchingCType.isInvalid()) {
+ T.skipToEnd();
+ return;
+ }
+
+ bool LayoutCompatible = false;
+ bool MustBeNull = false;
+ while (TryConsumeToken(tok::comma)) {
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ T.skipToEnd();
+ return;
+ }
+ IdentifierInfo *Flag = Tok.getIdentifierInfo();
+ if (Flag->isStr("layout_compatible"))
+ LayoutCompatible = true;
+ else if (Flag->isStr("must_be_null"))
+ MustBeNull = true;
+ else {
+ Diag(Tok, diag::err_type_safety_unknown_flag) << Flag;
+ T.skipToEnd();
+ return;
+ }
+ ConsumeToken(); // consume flag
+ }
+
+ if (!T.consumeClose()) {
+ Attrs.addNewTypeTagForDatatype(&AttrName, AttrNameLoc, ScopeName, ScopeLoc,
+ ArgumentKind, MatchingCType.get(),
+ LayoutCompatible, MustBeNull, Syntax);
+ }
+
+ if (EndLoc)
+ *EndLoc = T.getCloseLocation();
+}
+
+/// DiagnoseProhibitedCXX11Attribute - We have found the opening square brackets
+/// of a C++11 attribute-specifier in a location where an attribute is not
+/// permitted. By C++11 [dcl.attr.grammar]p6, this is ill-formed. Diagnose this
+/// situation.
+///
+/// \return \c true if we skipped an attribute-like chunk of tokens, \c false if
+/// this doesn't appear to actually be an attribute-specifier, and the caller
+/// should try to parse it.
+bool Parser::DiagnoseProhibitedCXX11Attribute() {
+ assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square));
+
+ switch (isCXX11AttributeSpecifier(/*Disambiguate*/true)) {
+ case CAK_NotAttributeSpecifier:
+ // No diagnostic: we're in Obj-C++11 and this is not actually an attribute.
+ return false;
+
+ case CAK_InvalidAttributeSpecifier:
+ Diag(Tok.getLocation(), diag::err_l_square_l_square_not_attribute);
+ return false;
+
+ case CAK_AttributeSpecifier:
+ // Parse and discard the attributes.
+ SourceLocation BeginLoc = ConsumeBracket();
+ ConsumeBracket();
+ SkipUntil(tok::r_square);
+ assert(Tok.is(tok::r_square) && "isCXX11AttributeSpecifier lied");
+ SourceLocation EndLoc = ConsumeBracket();
+ Diag(BeginLoc, diag::err_attributes_not_allowed)
+ << SourceRange(BeginLoc, EndLoc);
+ return true;
+ }
+ llvm_unreachable("All cases handled above.");
+}
+
+/// We have found the opening square brackets of a C++11
+/// attribute-specifier in a location where an attribute is not permitted, but
+/// we know where the attributes ought to be written. Parse them anyway, and
+/// provide a fixit moving them to the right place.
+void Parser::DiagnoseMisplacedCXX11Attribute(ParsedAttributes &Attrs,
+ SourceLocation CorrectLocation) {
+ assert((Tok.is(tok::l_square) && NextToken().is(tok::l_square)) ||
+ Tok.is(tok::kw_alignas));
+
+ // Consume the attributes.
+ SourceLocation Loc = Tok.getLocation();
+ ParseCXX11Attributes(Attrs);
+ CharSourceRange AttrRange(SourceRange(Loc, Attrs.Range.getEnd()), true);
+ // FIXME: use err_attributes_misplaced
+ Diag(Loc, diag::err_attributes_not_allowed)
+ << FixItHint::CreateInsertionFromRange(CorrectLocation, AttrRange)
+ << FixItHint::CreateRemoval(AttrRange);
+}
+
+void Parser::DiagnoseProhibitedAttributes(
+ const SourceRange &Range, const SourceLocation CorrectLocation) {
+ if (CorrectLocation.isValid()) {
+ CharSourceRange AttrRange(Range, true);
+ Diag(CorrectLocation, diag::err_attributes_misplaced)
+ << FixItHint::CreateInsertionFromRange(CorrectLocation, AttrRange)
+ << FixItHint::CreateRemoval(AttrRange);
+ } else
+ Diag(Range.getBegin(), diag::err_attributes_not_allowed) << Range;
+}
+
+void Parser::ProhibitCXX11Attributes(ParsedAttributes &Attrs, unsigned DiagID,
+ bool DiagnoseEmptyAttrs,
+ bool WarnOnUnknownAttrs) {
+
+ if (DiagnoseEmptyAttrs && Attrs.empty() && Attrs.Range.isValid()) {
+ // An attribute list has been parsed, but it was empty.
+ // This is the case for [[]].
+ const auto &LangOpts = getLangOpts();
+ auto &SM = PP.getSourceManager();
+ Token FirstLSquare;
+ Lexer::getRawToken(Attrs.Range.getBegin(), FirstLSquare, SM, LangOpts);
+
+ if (FirstLSquare.is(tok::l_square)) {
+ std::optional<Token> SecondLSquare =
+ Lexer::findNextToken(FirstLSquare.getLocation(), SM, LangOpts);
+
+ if (SecondLSquare && SecondLSquare->is(tok::l_square)) {
+ // The attribute range starts with [[, but is empty. So this must
+ // be [[]], which we are supposed to diagnose because
+ // DiagnoseEmptyAttrs is true.
+ Diag(Attrs.Range.getBegin(), DiagID) << Attrs.Range;
+ return;
+ }
+ }
+ }
+
+ for (const ParsedAttr &AL : Attrs) {
+ if (!AL.isCXX11Attribute() && !AL.isC2xAttribute())
+ continue;
+ if (AL.getKind() == ParsedAttr::UnknownAttribute) {
+ if (WarnOnUnknownAttrs)
+ Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored)
+ << AL << AL.getRange();
+ } else {
+ Diag(AL.getLoc(), DiagID) << AL;
+ AL.setInvalid();
+ }
+ }
+}
+
+void Parser::DiagnoseCXX11AttributeExtension(ParsedAttributes &Attrs) {
+ for (const ParsedAttr &PA : Attrs) {
+ if (PA.isCXX11Attribute() || PA.isC2xAttribute())
+ Diag(PA.getLoc(), diag::ext_cxx11_attr_placement) << PA << PA.getRange();
+ }
+}
+
+// Usually, `__attribute__((attrib)) class Foo {} var` means that attribute
+// applies to var, not the type Foo.
+// As an exception to the rule, __declspec(align(...)) before the
+// class-key affects the type instead of the variable.
+// Also, Microsoft-style [attributes] seem to affect the type instead of the
+// variable.
+// This function moves attributes that should apply to the type off DS to Attrs.
+void Parser::stripTypeAttributesOffDeclSpec(ParsedAttributes &Attrs,
+ DeclSpec &DS,
+ Sema::TagUseKind TUK) {
+ if (TUK == Sema::TUK_Reference)
+ return;
+
+ llvm::SmallVector<ParsedAttr *, 1> ToBeMoved;
+
+ for (ParsedAttr &AL : DS.getAttributes()) {
+ if ((AL.getKind() == ParsedAttr::AT_Aligned &&
+ AL.isDeclspecAttribute()) ||
+ AL.isMicrosoftAttribute())
+ ToBeMoved.push_back(&AL);
+ }
+
+ for (ParsedAttr *AL : ToBeMoved) {
+ DS.getAttributes().remove(AL);
+ Attrs.addAtEnd(AL);
+ }
+}
+
+/// ParseDeclaration - Parse a full 'declaration', which consists of
+/// declaration-specifiers, some number of declarators, and a semicolon.
+/// 'Context' should be a DeclaratorContext value. This returns the
+/// location of the semicolon in DeclEnd.
+///
+/// declaration: [C99 6.7]
+/// block-declaration ->
+/// simple-declaration
+/// others [FIXME]
+/// [C++] template-declaration
+/// [C++] namespace-definition
+/// [C++] using-directive
+/// [C++] using-declaration
+/// [C++11/C11] static_assert-declaration
+/// others... [FIXME]
+///
+Parser::DeclGroupPtrTy Parser::ParseDeclaration(DeclaratorContext Context,
+ SourceLocation &DeclEnd,
+ ParsedAttributes &DeclAttrs,
+ ParsedAttributes &DeclSpecAttrs,
+ SourceLocation *DeclSpecStart) {
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+ // Must temporarily exit the objective-c container scope for
+ // parsing c none objective-c decls.
+ ObjCDeclContextSwitch ObjCDC(*this);
+
+ Decl *SingleDecl = nullptr;
+ switch (Tok.getKind()) {
+ case tok::kw_template:
+ case tok::kw_export:
+ ProhibitAttributes(DeclAttrs);
+ ProhibitAttributes(DeclSpecAttrs);
+ SingleDecl =
+ ParseDeclarationStartingWithTemplate(Context, DeclEnd, DeclAttrs);
+ break;
+ case tok::kw_inline:
+ // Could be the start of an inline namespace. Allowed as an ext in C++03.
+ if (getLangOpts().CPlusPlus && NextToken().is(tok::kw_namespace)) {
+ ProhibitAttributes(DeclAttrs);
+ ProhibitAttributes(DeclSpecAttrs);
+ SourceLocation InlineLoc = ConsumeToken();
+ return ParseNamespace(Context, DeclEnd, InlineLoc);
+ }
+ return ParseSimpleDeclaration(Context, DeclEnd, DeclAttrs, DeclSpecAttrs,
+ true, nullptr, DeclSpecStart);
+
+ case tok::kw_cbuffer:
+ case tok::kw_tbuffer:
+ SingleDecl = ParseHLSLBuffer(DeclEnd);
+ break;
+ case tok::kw_namespace:
+ ProhibitAttributes(DeclAttrs);
+ ProhibitAttributes(DeclSpecAttrs);
+ return ParseNamespace(Context, DeclEnd);
+ case tok::kw_using: {
+ ParsedAttributes Attrs(AttrFactory);
+ takeAndConcatenateAttrs(DeclAttrs, DeclSpecAttrs, Attrs);
+ return ParseUsingDirectiveOrDeclaration(Context, ParsedTemplateInfo(),
+ DeclEnd, Attrs);
+ }
+ case tok::kw_static_assert:
+ case tok::kw__Static_assert:
+ ProhibitAttributes(DeclAttrs);
+ ProhibitAttributes(DeclSpecAttrs);
+ SingleDecl = ParseStaticAssertDeclaration(DeclEnd);
+ break;
+ default:
+ return ParseSimpleDeclaration(Context, DeclEnd, DeclAttrs, DeclSpecAttrs,
+ true, nullptr, DeclSpecStart);
+ }
+
+ // This routine returns a DeclGroup, if the thing we parsed only contains a
+ // single decl, convert it now.
+ return Actions.ConvertDeclToDeclGroup(SingleDecl);
+}
+
+/// simple-declaration: [C99 6.7: declaration] [C++ 7p1: dcl.dcl]
+/// declaration-specifiers init-declarator-list[opt] ';'
+/// [C++11] attribute-specifier-seq decl-specifier-seq[opt]
+/// init-declarator-list ';'
+///[C90/C++]init-declarator-list ';' [TODO]
+/// [OMP] threadprivate-directive
+/// [OMP] allocate-directive [TODO]
+///
+/// for-range-declaration: [C++11 6.5p1: stmt.ranged]
+/// attribute-specifier-seq[opt] type-specifier-seq declarator
+///
+/// If RequireSemi is false, this does not check for a ';' at the end of the
+/// declaration. If it is true, it checks for and eats it.
+///
+/// If FRI is non-null, we might be parsing a for-range-declaration instead
+/// of a simple-declaration. If we find that we are, we also parse the
+/// for-range-initializer, and place it here.
+///
+/// DeclSpecStart is used when decl-specifiers are parsed before parsing
+/// the Declaration. The SourceLocation for this Decl is set to
+/// DeclSpecStart if DeclSpecStart is non-null.
+Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(
+ DeclaratorContext Context, SourceLocation &DeclEnd,
+ ParsedAttributes &DeclAttrs, ParsedAttributes &DeclSpecAttrs,
+ bool RequireSemi, ForRangeInit *FRI, SourceLocation *DeclSpecStart) {
+ // Need to retain these for diagnostics before we add them to the DeclSepc.
+ ParsedAttributesView OriginalDeclSpecAttrs;
+ OriginalDeclSpecAttrs.addAll(DeclSpecAttrs.begin(), DeclSpecAttrs.end());
+ OriginalDeclSpecAttrs.Range = DeclSpecAttrs.Range;
+
+ // Parse the common declaration-specifiers piece.
+ ParsingDeclSpec DS(*this);
+ DS.takeAttributesFrom(DeclSpecAttrs);
+
+ DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context);
+ ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, DSContext);
+
+ // If we had a free-standing type definition with a missing semicolon, we
+ // may get this far before the problem becomes obvious.
+ if (DS.hasTagDefinition() &&
+ DiagnoseMissingSemiAfterTagDefinition(DS, AS_none, DSContext))
+ return nullptr;
+
+ // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
+ // declaration-specifiers init-declarator-list[opt] ';'
+ if (Tok.is(tok::semi)) {
+ ProhibitAttributes(DeclAttrs);
+ DeclEnd = Tok.getLocation();
+ if (RequireSemi) ConsumeToken();
+ RecordDecl *AnonRecord = nullptr;
+ Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(
+ getCurScope(), AS_none, DS, ParsedAttributesView::none(), AnonRecord);
+ DS.complete(TheDecl);
+ if (AnonRecord) {
+ Decl* decls[] = {AnonRecord, TheDecl};
+ return Actions.BuildDeclaratorGroup(decls);
+ }
+ return Actions.ConvertDeclToDeclGroup(TheDecl);
+ }
+
+ if (DeclSpecStart)
+ DS.SetRangeStart(*DeclSpecStart);
+
+ return ParseDeclGroup(DS, Context, DeclAttrs, &DeclEnd, FRI);
+}
+
+/// Returns true if this might be the start of a declarator, or a common typo
+/// for a declarator.
+bool Parser::MightBeDeclarator(DeclaratorContext Context) {
+ switch (Tok.getKind()) {
+ case tok::annot_cxxscope:
+ case tok::annot_template_id:
+ case tok::caret:
+ case tok::code_completion:
+ case tok::coloncolon:
+ case tok::ellipsis:
+ case tok::kw___attribute:
+ case tok::kw_operator:
+ case tok::l_paren:
+ case tok::star:
+ return true;
+
+ case tok::amp:
+ case tok::ampamp:
+ return getLangOpts().CPlusPlus;
+
+ case tok::l_square: // Might be an attribute on an unnamed bit-field.
+ return Context == DeclaratorContext::Member && getLangOpts().CPlusPlus11 &&
+ NextToken().is(tok::l_square);
+
+ case tok::colon: // Might be a typo for '::' or an unnamed bit-field.
+ return Context == DeclaratorContext::Member || getLangOpts().CPlusPlus;
+
+ case tok::identifier:
+ switch (NextToken().getKind()) {
+ case tok::code_completion:
+ case tok::coloncolon:
+ case tok::comma:
+ case tok::equal:
+ case tok::equalequal: // Might be a typo for '='.
+ case tok::kw_alignas:
+ case tok::kw_asm:
+ case tok::kw___attribute:
+ case tok::l_brace:
+ case tok::l_paren:
+ case tok::l_square:
+ case tok::less:
+ case tok::r_brace:
+ case tok::r_paren:
+ case tok::r_square:
+ case tok::semi:
+ return true;
+
+ case tok::colon:
+ // At namespace scope, 'identifier:' is probably a typo for 'identifier::'
+ // and in block scope it's probably a label. Inside a class definition,
+ // this is a bit-field.
+ return Context == DeclaratorContext::Member ||
+ (getLangOpts().CPlusPlus && Context == DeclaratorContext::File);
+
+ case tok::identifier: // Possible virt-specifier.
+ return getLangOpts().CPlusPlus11 && isCXX11VirtSpecifier(NextToken());
+
+ default:
+ return false;
+ }
+
+ default:
+ return false;
+ }
+}
+
+/// Skip until we reach something which seems like a sensible place to pick
+/// up parsing after a malformed declaration. This will sometimes stop sooner
+/// than SkipUntil(tok::r_brace) would, but will never stop later.
+void Parser::SkipMalformedDecl() {
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::l_brace:
+ // Skip until matching }, then stop. We've probably skipped over
+ // a malformed class or function definition or similar.
+ ConsumeBrace();
+ SkipUntil(tok::r_brace);
+ if (Tok.isOneOf(tok::comma, tok::l_brace, tok::kw_try)) {
+ // This declaration isn't over yet. Keep skipping.
+ continue;
+ }
+ TryConsumeToken(tok::semi);
+ return;
+
+ case tok::l_square:
+ ConsumeBracket();
+ SkipUntil(tok::r_square);
+ continue;
+
+ case tok::l_paren:
+ ConsumeParen();
+ SkipUntil(tok::r_paren);
+ continue;
+
+ case tok::r_brace:
+ return;
+
+ case tok::semi:
+ ConsumeToken();
+ return;
+
+ case tok::kw_inline:
+ // 'inline namespace' at the start of a line is almost certainly
+ // a good place to pick back up parsing, except in an Objective-C
+ // @interface context.
+ if (Tok.isAtStartOfLine() && NextToken().is(tok::kw_namespace) &&
+ (!ParsingInObjCContainer || CurParsedObjCImpl))
+ return;
+ break;
+
+ case tok::kw_namespace:
+ // 'namespace' at the start of a line is almost certainly a good
+ // place to pick back up parsing, except in an Objective-C
+ // @interface context.
+ if (Tok.isAtStartOfLine() &&
+ (!ParsingInObjCContainer || CurParsedObjCImpl))
+ return;
+ break;
+
+ case tok::at:
+ // @end is very much like } in Objective-C contexts.
+ if (NextToken().isObjCAtKeyword(tok::objc_end) &&
+ ParsingInObjCContainer)
+ return;
+ break;
+
+ case tok::minus:
+ case tok::plus:
+ // - and + probably start new method declarations in Objective-C contexts.
+ if (Tok.isAtStartOfLine() && ParsingInObjCContainer)
+ return;
+ break;
+
+ case tok::eof:
+ case tok::annot_module_begin:
+ case tok::annot_module_end:
+ case tok::annot_module_include:
+ return;
+
+ default:
+ break;
+ }
+
+ ConsumeAnyToken();
+ }
+}
+
+/// ParseDeclGroup - Having concluded that this is either a function
+/// definition or a group of object declarations, actually parse the
+/// result.
+Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
+ DeclaratorContext Context,
+ ParsedAttributes &Attrs,
+ SourceLocation *DeclEnd,
+ ForRangeInit *FRI) {
+ // Parse the first declarator.
+ // Consume all of the attributes from `Attrs` by moving them to our own local
+ // list. This ensures that we will not attempt to interpret them as statement
+ // attributes higher up the callchain.
+ ParsedAttributes LocalAttrs(AttrFactory);
+ LocalAttrs.takeAllFrom(Attrs);
+ ParsingDeclarator D(*this, DS, LocalAttrs, Context);
+ ParseDeclarator(D);
+
+ // Bail out if the first declarator didn't seem well-formed.
+ if (!D.hasName() && !D.mayOmitIdentifier()) {
+ SkipMalformedDecl();
+ return nullptr;
+ }
+
+ if (getLangOpts().HLSL)
+ MaybeParseHLSLSemantics(D);
+
+ if (Tok.is(tok::kw_requires))
+ ParseTrailingRequiresClause(D);
+
+ // Save late-parsed attributes for now; they need to be parsed in the
+ // appropriate function scope after the function Decl has been constructed.
+ // These will be parsed in ParseFunctionDefinition or ParseLexedAttrList.
+ LateParsedAttrList LateParsedAttrs(true);
+ if (D.isFunctionDeclarator()) {
+ MaybeParseGNUAttributes(D, &LateParsedAttrs);
+
+ // The _Noreturn keyword can't appear here, unlike the GNU noreturn
+ // attribute. If we find the keyword here, tell the user to put it
+ // at the start instead.
+ if (Tok.is(tok::kw__Noreturn)) {
+ SourceLocation Loc = ConsumeToken();
+ const char *PrevSpec;
+ unsigned DiagID;
+
+ // We can offer a fixit if it's valid to mark this function as _Noreturn
+ // and we don't have any other declarators in this declaration.
+ bool Fixit = !DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
+ MaybeParseGNUAttributes(D, &LateParsedAttrs);
+ Fixit &= Tok.isOneOf(tok::semi, tok::l_brace, tok::kw_try);
+
+ Diag(Loc, diag::err_c11_noreturn_misplaced)
+ << (Fixit ? FixItHint::CreateRemoval(Loc) : FixItHint())
+ << (Fixit ? FixItHint::CreateInsertion(D.getBeginLoc(), "_Noreturn ")
+ : FixItHint());
+ }
+
+ // Check to see if we have a function *definition* which must have a body.
+ if (Tok.is(tok::equal) && NextToken().is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteAfterFunctionEquals(D);
+ return nullptr;
+ }
+ // We're at the point where the parsing of function declarator is finished.
+ //
+ // A common error is that users accidently add a virtual specifier
+ // (e.g. override) in an out-line method definition.
+ // We attempt to recover by stripping all these specifiers coming after
+ // the declarator.
+ while (auto Specifier = isCXX11VirtSpecifier()) {
+ Diag(Tok, diag::err_virt_specifier_outside_class)
+ << VirtSpecifiers::getSpecifierName(Specifier)
+ << FixItHint::CreateRemoval(Tok.getLocation());
+ ConsumeToken();
+ }
+ // Look at the next token to make sure that this isn't a function
+ // declaration. We have to check this because __attribute__ might be the
+ // start of a function definition in GCC-extended K&R C.
+ if (!isDeclarationAfterDeclarator()) {
+
+ // Function definitions are only allowed at file scope and in C++ classes.
+ // The C++ inline method definition case is handled elsewhere, so we only
+ // need to handle the file scope definition case.
+ if (Context == DeclaratorContext::File) {
+ if (isStartOfFunctionDefinition(D)) {
+ if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
+ Diag(Tok, diag::err_function_declared_typedef);
+
+ // Recover by treating the 'typedef' as spurious.
+ DS.ClearStorageClassSpecs();
+ }
+
+ Decl *TheDecl = ParseFunctionDefinition(D, ParsedTemplateInfo(),
+ &LateParsedAttrs);
+ return Actions.ConvertDeclToDeclGroup(TheDecl);
+ }
+
+ if (isDeclarationSpecifier(ImplicitTypenameContext::No)) {
+ // If there is an invalid declaration specifier right after the
+ // function prototype, then we must be in a missing semicolon case
+ // where this isn't actually a body. Just fall through into the code
+ // that handles it as a prototype, and let the top-level code handle
+ // the erroneous declspec where it would otherwise expect a comma or
+ // semicolon.
+ } else {
+ Diag(Tok, diag::err_expected_fn_body);
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+ } else {
+ if (Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::err_function_definition_not_allowed);
+ SkipMalformedDecl();
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ if (ParseAsmAttributesAfterDeclarator(D))
+ return nullptr;
+
+ // C++0x [stmt.iter]p1: Check if we have a for-range-declarator. If so, we
+ // must parse and analyze the for-range-initializer before the declaration is
+ // analyzed.
+ //
+ // Handle the Objective-C for-in loop variable similarly, although we
+ // don't need to parse the container in advance.
+ if (FRI && (Tok.is(tok::colon) || isTokIdentifier_in())) {
+ bool IsForRangeLoop = false;
+ if (TryConsumeToken(tok::colon, FRI->ColonLoc)) {
+ IsForRangeLoop = true;
+ if (getLangOpts().OpenMP)
+ Actions.startOpenMPCXXRangeFor();
+ if (Tok.is(tok::l_brace))
+ FRI->RangeExpr = ParseBraceInitializer();
+ else
+ FRI->RangeExpr = ParseExpression();
+ }
+
+ Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
+ if (IsForRangeLoop) {
+ Actions.ActOnCXXForRangeDecl(ThisDecl);
+ } else {
+ // Obj-C for loop
+ if (auto *VD = dyn_cast_or_null<VarDecl>(ThisDecl))
+ VD->setObjCForDecl(true);
+ }
+ Actions.FinalizeDeclaration(ThisDecl);
+ D.complete(ThisDecl);
+ return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, ThisDecl);
+ }
+
+ SmallVector<Decl *, 8> DeclsInGroup;
+ Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(
+ D, ParsedTemplateInfo(), FRI);
+ if (LateParsedAttrs.size() > 0)
+ ParseLexedAttributeList(LateParsedAttrs, FirstDecl, true, false);
+ D.complete(FirstDecl);
+ if (FirstDecl)
+ DeclsInGroup.push_back(FirstDecl);
+
+ bool ExpectSemi = Context != DeclaratorContext::ForInit;
+
+ // If we don't have a comma, it is either the end of the list (a ';') or an
+ // error, bail out.
+ SourceLocation CommaLoc;
+ while (TryConsumeToken(tok::comma, CommaLoc)) {
+ if (Tok.isAtStartOfLine() && ExpectSemi && !MightBeDeclarator(Context)) {
+ // This comma was followed by a line-break and something which can't be
+ // the start of a declarator. The comma was probably a typo for a
+ // semicolon.
+ Diag(CommaLoc, diag::err_expected_semi_declaration)
+ << FixItHint::CreateReplacement(CommaLoc, ";");
+ ExpectSemi = false;
+ break;
+ }
+
+ // Parse the next declarator.
+ D.clear();
+ D.setCommaLoc(CommaLoc);
+
+ // Accept attributes in an init-declarator. In the first declarator in a
+ // declaration, these would be part of the declspec. In subsequent
+ // declarators, they become part of the declarator itself, so that they
+ // don't apply to declarators after *this* one. Examples:
+ // short __attribute__((common)) var; -> declspec
+ // short var __attribute__((common)); -> declarator
+ // short x, __attribute__((common)) var; -> declarator
+ MaybeParseGNUAttributes(D);
+
+ // MSVC parses but ignores qualifiers after the comma as an extension.
+ if (getLangOpts().MicrosoftExt)
+ DiagnoseAndSkipExtendedMicrosoftTypeAttributes();
+
+ ParseDeclarator(D);
+
+ if (getLangOpts().HLSL)
+ MaybeParseHLSLSemantics(D);
+
+ if (!D.isInvalidType()) {
+ // C++2a [dcl.decl]p1
+ // init-declarator:
+ // declarator initializer[opt]
+ // declarator requires-clause
+ if (Tok.is(tok::kw_requires))
+ ParseTrailingRequiresClause(D);
+ Decl *ThisDecl = ParseDeclarationAfterDeclarator(D);
+ D.complete(ThisDecl);
+ if (ThisDecl)
+ DeclsInGroup.push_back(ThisDecl);
+ }
+ }
+
+ if (DeclEnd)
+ *DeclEnd = Tok.getLocation();
+
+ if (ExpectSemi && ExpectAndConsumeSemi(
+ Context == DeclaratorContext::File
+ ? diag::err_invalid_token_after_toplevel_declarator
+ : diag::err_expected_semi_declaration)) {
+ // Okay, there was no semicolon and one was expected. If we see a
+ // declaration specifier, just assume it was missing and continue parsing.
+ // Otherwise things are very confused and we skip to recover.
+ if (!isDeclarationSpecifier(ImplicitTypenameContext::No)) {
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ TryConsumeToken(tok::semi);
+ }
+ }
+
+ return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, DeclsInGroup);
+}
+
+/// Parse an optional simple-asm-expr and attributes, and attach them to a
+/// declarator. Returns true on an error.
+bool Parser::ParseAsmAttributesAfterDeclarator(Declarator &D) {
+ // If a simple-asm-expr is present, parse it.
+ if (Tok.is(tok::kw_asm)) {
+ SourceLocation Loc;
+ ExprResult AsmLabel(ParseSimpleAsm(/*ForAsmLabel*/ true, &Loc));
+ if (AsmLabel.isInvalid()) {
+ SkipUntil(tok::semi, StopBeforeMatch);
+ return true;
+ }
+
+ D.setAsmLabel(AsmLabel.get());
+ D.SetRangeEnd(Loc);
+ }
+
+ MaybeParseGNUAttributes(D);
+ return false;
+}
+
+/// Parse 'declaration' after parsing 'declaration-specifiers
+/// declarator'. This method parses the remainder of the declaration
+/// (including any attributes or initializer, among other things) and
+/// finalizes the declaration.
+///
+/// init-declarator: [C99 6.7]
+/// declarator
+/// declarator '=' initializer
+/// [GNU] declarator simple-asm-expr[opt] attributes[opt]
+/// [GNU] declarator simple-asm-expr[opt] attributes[opt] '=' initializer
+/// [C++] declarator initializer[opt]
+///
+/// [C++] initializer:
+/// [C++] '=' initializer-clause
+/// [C++] '(' expression-list ')'
+/// [C++0x] '=' 'default' [TODO]
+/// [C++0x] '=' 'delete'
+/// [C++0x] braced-init-list
+///
+/// According to the standard grammar, =default and =delete are function
+/// definitions, but that definitely doesn't fit with the parser here.
+///
+Decl *Parser::ParseDeclarationAfterDeclarator(
+ Declarator &D, const ParsedTemplateInfo &TemplateInfo) {
+ if (ParseAsmAttributesAfterDeclarator(D))
+ return nullptr;
+
+ return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo);
+}
+
+Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
+ Declarator &D, const ParsedTemplateInfo &TemplateInfo, ForRangeInit *FRI) {
+ // RAII type used to track whether we're inside an initializer.
+ struct InitializerScopeRAII {
+ Parser &P;
+ Declarator &D;
+ Decl *ThisDecl;
+
+ InitializerScopeRAII(Parser &P, Declarator &D, Decl *ThisDecl)
+ : P(P), D(D), ThisDecl(ThisDecl) {
+ if (ThisDecl && P.getLangOpts().CPlusPlus) {
+ Scope *S = nullptr;
+ if (D.getCXXScopeSpec().isSet()) {
+ P.EnterScope(0);
+ S = P.getCurScope();
+ }
+ P.Actions.ActOnCXXEnterDeclInitializer(S, ThisDecl);
+ }
+ }
+ ~InitializerScopeRAII() { pop(); }
+ void pop() {
+ if (ThisDecl && P.getLangOpts().CPlusPlus) {
+ Scope *S = nullptr;
+ if (D.getCXXScopeSpec().isSet())
+ S = P.getCurScope();
+ P.Actions.ActOnCXXExitDeclInitializer(S, ThisDecl);
+ if (S)
+ P.ExitScope();
+ }
+ ThisDecl = nullptr;
+ }
+ };
+
+ enum class InitKind { Uninitialized, Equal, CXXDirect, CXXBraced };
+ InitKind TheInitKind;
+ // If a '==' or '+=' is found, suggest a fixit to '='.
+ if (isTokenEqualOrEqualTypo())
+ TheInitKind = InitKind::Equal;
+ else if (Tok.is(tok::l_paren))
+ TheInitKind = InitKind::CXXDirect;
+ else if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace) &&
+ (!CurParsedObjCImpl || !D.isFunctionDeclarator()))
+ TheInitKind = InitKind::CXXBraced;
+ else
+ TheInitKind = InitKind::Uninitialized;
+ if (TheInitKind != InitKind::Uninitialized)
+ D.setHasInitializer();
+
+ // Inform Sema that we just parsed this declarator.
+ Decl *ThisDecl = nullptr;
+ Decl *OuterDecl = nullptr;
+ switch (TemplateInfo.Kind) {
+ case ParsedTemplateInfo::NonTemplate:
+ ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
+ break;
+
+ case ParsedTemplateInfo::Template:
+ case ParsedTemplateInfo::ExplicitSpecialization: {
+ ThisDecl = Actions.ActOnTemplateDeclarator(getCurScope(),
+ *TemplateInfo.TemplateParams,
+ D);
+ if (VarTemplateDecl *VT = dyn_cast_or_null<VarTemplateDecl>(ThisDecl)) {
+ // Re-direct this decl to refer to the templated decl so that we can
+ // initialize it.
+ ThisDecl = VT->getTemplatedDecl();
+ OuterDecl = VT;
+ }
+ break;
+ }
+ case ParsedTemplateInfo::ExplicitInstantiation: {
+ if (Tok.is(tok::semi)) {
+ DeclResult ThisRes = Actions.ActOnExplicitInstantiation(
+ getCurScope(), TemplateInfo.ExternLoc, TemplateInfo.TemplateLoc, D);
+ if (ThisRes.isInvalid()) {
+ SkipUntil(tok::semi, StopBeforeMatch);
+ return nullptr;
+ }
+ ThisDecl = ThisRes.get();
+ } else {
+ // FIXME: This check should be for a variable template instantiation only.
+
+ // Check that this is a valid instantiation
+ if (D.getName().getKind() != UnqualifiedIdKind::IK_TemplateId) {
+ // If the declarator-id is not a template-id, issue a diagnostic and
+ // recover by ignoring the 'template' keyword.
+ Diag(Tok, diag::err_template_defn_explicit_instantiation)
+ << 2 << FixItHint::CreateRemoval(TemplateInfo.TemplateLoc);
+ ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
+ } else {
+ SourceLocation LAngleLoc =
+ PP.getLocForEndOfToken(TemplateInfo.TemplateLoc);
+ Diag(D.getIdentifierLoc(),
+ diag::err_explicit_instantiation_with_definition)
+ << SourceRange(TemplateInfo.TemplateLoc)
+ << FixItHint::CreateInsertion(LAngleLoc, "<>");
+
+ // Recover as if it were an explicit specialization.
+ TemplateParameterLists FakedParamLists;
+ FakedParamLists.push_back(Actions.ActOnTemplateParameterList(
+ 0, SourceLocation(), TemplateInfo.TemplateLoc, LAngleLoc,
+ std::nullopt, LAngleLoc, nullptr));
+
+ ThisDecl =
+ Actions.ActOnTemplateDeclarator(getCurScope(), FakedParamLists, D);
+ }
+ }
+ break;
+ }
+ }
+
+ switch (TheInitKind) {
+ // Parse declarator '=' initializer.
+ case InitKind::Equal: {
+ SourceLocation EqualLoc = ConsumeToken();
+
+ if (Tok.is(tok::kw_delete)) {
+ if (D.isFunctionDeclarator())
+ Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
+ << 1 /* delete */;
+ else
+ Diag(ConsumeToken(), diag::err_deleted_non_function);
+ } else if (Tok.is(tok::kw_default)) {
+ if (D.isFunctionDeclarator())
+ Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
+ << 0 /* default */;
+ else
+ Diag(ConsumeToken(), diag::err_default_special_members)
+ << getLangOpts().CPlusPlus20;
+ } else {
+ InitializerScopeRAII InitScope(*this, D, ThisDecl);
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteInitializer(getCurScope(), ThisDecl);
+ Actions.FinalizeDeclaration(ThisDecl);
+ return nullptr;
+ }
+
+ PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl);
+ ExprResult Init = ParseInitializer();
+
+ // If this is the only decl in (possibly) range based for statement,
+ // our best guess is that the user meant ':' instead of '='.
+ if (Tok.is(tok::r_paren) && FRI && D.isFirstDeclarator()) {
+ Diag(EqualLoc, diag::err_single_decl_assign_in_for_range)
+ << FixItHint::CreateReplacement(EqualLoc, ":");
+ // We are trying to stop parser from looking for ';' in this for
+ // statement, therefore preventing spurious errors to be issued.
+ FRI->ColonLoc = EqualLoc;
+ Init = ExprError();
+ FRI->RangeExpr = Init;
+ }
+
+ InitScope.pop();
+
+ if (Init.isInvalid()) {
+ SmallVector<tok::TokenKind, 2> StopTokens;
+ StopTokens.push_back(tok::comma);
+ if (D.getContext() == DeclaratorContext::ForInit ||
+ D.getContext() == DeclaratorContext::SelectionInit)
+ StopTokens.push_back(tok::r_paren);
+ SkipUntil(StopTokens, StopAtSemi | StopBeforeMatch);
+ Actions.ActOnInitializerError(ThisDecl);
+ } else
+ Actions.AddInitializerToDecl(ThisDecl, Init.get(),
+ /*DirectInit=*/false);
+ }
+ break;
+ }
+ case InitKind::CXXDirect: {
+ // Parse C++ direct initializer: '(' expression-list ')'
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ ExprVector Exprs;
+
+ InitializerScopeRAII InitScope(*this, D, ThisDecl);
+
+ auto ThisVarDecl = dyn_cast_or_null<VarDecl>(ThisDecl);
+ auto RunSignatureHelp = [&]() {
+ QualType PreferredType = Actions.ProduceConstructorSignatureHelp(
+ ThisVarDecl->getType()->getCanonicalTypeInternal(),
+ ThisDecl->getLocation(), Exprs, T.getOpenLocation(),
+ /*Braced=*/false);
+ CalledSignatureHelp = true;
+ return PreferredType;
+ };
+ auto SetPreferredType = [&] {
+ PreferredType.enterFunctionArgument(Tok.getLocation(), RunSignatureHelp);
+ };
+
+ llvm::function_ref<void()> ExpressionStarts;
+ if (ThisVarDecl) {
+ // ParseExpressionList can sometimes succeed even when ThisDecl is not
+ // VarDecl. This is an error and it is reported in a call to
+ // Actions.ActOnInitializerError(). However, we call
+ // ProduceConstructorSignatureHelp only on VarDecls.
+ ExpressionStarts = SetPreferredType;
+ }
+ if (ParseExpressionList(Exprs, ExpressionStarts)) {
+ if (ThisVarDecl && PP.isCodeCompletionReached() && !CalledSignatureHelp) {
+ Actions.ProduceConstructorSignatureHelp(
+ ThisVarDecl->getType()->getCanonicalTypeInternal(),
+ ThisDecl->getLocation(), Exprs, T.getOpenLocation(),
+ /*Braced=*/false);
+ CalledSignatureHelp = true;
+ }
+ Actions.ActOnInitializerError(ThisDecl);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ } else {
+ // Match the ')'.
+ T.consumeClose();
+ InitScope.pop();
+
+ ExprResult Initializer = Actions.ActOnParenListExpr(T.getOpenLocation(),
+ T.getCloseLocation(),
+ Exprs);
+ Actions.AddInitializerToDecl(ThisDecl, Initializer.get(),
+ /*DirectInit=*/true);
+ }
+ break;
+ }
+ case InitKind::CXXBraced: {
+ // Parse C++0x braced-init-list.
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+
+ InitializerScopeRAII InitScope(*this, D, ThisDecl);
+
+ PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl);
+ ExprResult Init(ParseBraceInitializer());
+
+ InitScope.pop();
+
+ if (Init.isInvalid()) {
+ Actions.ActOnInitializerError(ThisDecl);
+ } else
+ Actions.AddInitializerToDecl(ThisDecl, Init.get(), /*DirectInit=*/true);
+ break;
+ }
+ case InitKind::Uninitialized: {
+ Actions.ActOnUninitializedDecl(ThisDecl);
+ break;
+ }
+ }
+
+ Actions.FinalizeDeclaration(ThisDecl);
+ return OuterDecl ? OuterDecl : ThisDecl;
+}
+
+/// ParseSpecifierQualifierList
+/// specifier-qualifier-list:
+/// type-specifier specifier-qualifier-list[opt]
+/// type-qualifier specifier-qualifier-list[opt]
+/// [GNU] attributes specifier-qualifier-list[opt]
+///
+void Parser::ParseSpecifierQualifierList(
+ DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
+ AccessSpecifier AS, DeclSpecContext DSC) {
+ /// specifier-qualifier-list is a subset of declaration-specifiers. Just
+ /// parse declaration-specifiers and complain about extra stuff.
+ /// TODO: diagnose attribute-specifiers and alignment-specifiers.
+ ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC, nullptr,
+ AllowImplicitTypename);
+
+ // Validate declspec for type-name.
+ unsigned Specs = DS.getParsedSpecifiers();
+ if (isTypeSpecifier(DSC) && !DS.hasTypeSpecifier()) {
+ Diag(Tok, diag::err_expected_type);
+ DS.SetTypeSpecError();
+ } else if (Specs == DeclSpec::PQ_None && !DS.hasAttributes()) {
+ Diag(Tok, diag::err_typename_requires_specqual);
+ if (!DS.hasTypeSpecifier())
+ DS.SetTypeSpecError();
+ }
+
+ // Issue diagnostic and remove storage class if present.
+ if (Specs & DeclSpec::PQ_StorageClassSpecifier) {
+ if (DS.getStorageClassSpecLoc().isValid())
+ Diag(DS.getStorageClassSpecLoc(),diag::err_typename_invalid_storageclass);
+ else
+ Diag(DS.getThreadStorageClassSpecLoc(),
+ diag::err_typename_invalid_storageclass);
+ DS.ClearStorageClassSpecs();
+ }
+
+ // Issue diagnostic and remove function specifier if present.
+ if (Specs & DeclSpec::PQ_FunctionSpecifier) {
+ if (DS.isInlineSpecified())
+ Diag(DS.getInlineSpecLoc(), diag::err_typename_invalid_functionspec);
+ if (DS.isVirtualSpecified())
+ Diag(DS.getVirtualSpecLoc(), diag::err_typename_invalid_functionspec);
+ if (DS.hasExplicitSpecifier())
+ Diag(DS.getExplicitSpecLoc(), diag::err_typename_invalid_functionspec);
+ if (DS.isNoreturnSpecified())
+ Diag(DS.getNoreturnSpecLoc(), diag::err_typename_invalid_functionspec);
+ DS.ClearFunctionSpecs();
+ }
+
+ // Issue diagnostic and remove constexpr specifier if present.
+ if (DS.hasConstexprSpecifier() && DSC != DeclSpecContext::DSC_condition) {
+ Diag(DS.getConstexprSpecLoc(), diag::err_typename_invalid_constexpr)
+ << static_cast<int>(DS.getConstexprSpecifier());
+ DS.ClearConstexprSpec();
+ }
+}
+
+/// isValidAfterIdentifierInDeclaratorAfterDeclSpec - Return true if the
+/// specified token is valid after the identifier in a declarator which
+/// immediately follows the declspec. For example, these things are valid:
+///
+/// int x [ 4]; // direct-declarator
+/// int x ( int y); // direct-declarator
+/// int(int x ) // direct-declarator
+/// int x ; // simple-declaration
+/// int x = 17; // init-declarator-list
+/// int x , y; // init-declarator-list
+/// int x __asm__ ("foo"); // init-declarator-list
+/// int x : 4; // struct-declarator
+/// int x { 5}; // C++'0x unified initializers
+///
+/// This is not, because 'x' does not immediately follow the declspec (though
+/// ')' happens to be valid anyway).
+/// int (x)
+///
+static bool isValidAfterIdentifierInDeclarator(const Token &T) {
+ return T.isOneOf(tok::l_square, tok::l_paren, tok::r_paren, tok::semi,
+ tok::comma, tok::equal, tok::kw_asm, tok::l_brace,
+ tok::colon);
+}
+
+/// ParseImplicitInt - This method is called when we have an non-typename
+/// identifier in a declspec (which normally terminates the decl spec) when
+/// the declspec has no type specifier. In this case, the declspec is either
+/// malformed or is "implicit int" (in K&R and C89).
+///
+/// This method handles diagnosing this prettily and returns false if the
+/// declspec is done being processed. If it recovers and thinks there may be
+/// other pieces of declspec after it, it returns true.
+///
+bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
+ const ParsedTemplateInfo &TemplateInfo,
+ AccessSpecifier AS, DeclSpecContext DSC,
+ ParsedAttributes &Attrs) {
+ assert(Tok.is(tok::identifier) && "should have identifier");
+
+ SourceLocation Loc = Tok.getLocation();
+ // If we see an identifier that is not a type name, we normally would
+ // parse it as the identifier being declared. However, when a typename
+ // is typo'd or the definition is not included, this will incorrectly
+ // parse the typename as the identifier name and fall over misparsing
+ // later parts of the diagnostic.
+ //
+ // As such, we try to do some look-ahead in cases where this would
+ // otherwise be an "implicit-int" case to see if this is invalid. For
+ // example: "static foo_t x = 4;" In this case, if we parsed foo_t as
+ // an identifier with implicit int, we'd get a parse error because the
+ // next token is obviously invalid for a type. Parse these as a case
+ // with an invalid type specifier.
+ assert(!DS.hasTypeSpecifier() && "Type specifier checked above");
+
+ // Since we know that this either implicit int (which is rare) or an
+ // error, do lookahead to try to do better recovery. This never applies
+ // within a type specifier. Outside of C++, we allow this even if the
+ // language doesn't "officially" support implicit int -- we support
+ // implicit int as an extension in some language modes.
+ if (!isTypeSpecifier(DSC) && getLangOpts().isImplicitIntAllowed() &&
+ isValidAfterIdentifierInDeclarator(NextToken())) {
+ // If this token is valid for implicit int, e.g. "static x = 4", then
+ // we just avoid eating the identifier, so it will be parsed as the
+ // identifier in the declarator.
+ return false;
+ }
+
+ // Early exit as Sema has a dedicated missing_actual_pipe_type diagnostic
+ // for incomplete declarations such as `pipe p`.
+ if (getLangOpts().OpenCLCPlusPlus && DS.isTypeSpecPipe())
+ return false;
+
+ if (getLangOpts().CPlusPlus &&
+ DS.getStorageClassSpec() == DeclSpec::SCS_auto) {
+ // Don't require a type specifier if we have the 'auto' storage class
+ // specifier in C++98 -- we'll promote it to a type specifier.
+ if (SS)
+ AnnotateScopeToken(*SS, /*IsNewAnnotation*/false);
+ return false;
+ }
+
+ if (getLangOpts().CPlusPlus && (!SS || SS->isEmpty()) &&
+ getLangOpts().MSVCCompat) {
+ // Lookup of an unqualified type name has failed in MSVC compatibility mode.
+ // Give Sema a chance to recover if we are in a template with dependent base
+ // classes.
+ if (ParsedType T = Actions.ActOnMSVCUnknownTypeName(
+ *Tok.getIdentifierInfo(), Tok.getLocation(),
+ DSC == DeclSpecContext::DSC_template_type_arg)) {
+ const char *PrevSpec;
+ unsigned DiagID;
+ DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, T,
+ Actions.getASTContext().getPrintingPolicy());
+ DS.SetRangeEnd(Tok.getLocation());
+ ConsumeToken();
+ return false;
+ }
+ }
+
+ // Otherwise, if we don't consume this token, we are going to emit an
+ // error anyway. Try to recover from various common problems. Check
+ // to see if this was a reference to a tag name without a tag specified.
+ // This is a common problem in C (saying 'foo' instead of 'struct foo').
+ //
+ // C++ doesn't need this, and isTagName doesn't take SS.
+ if (SS == nullptr) {
+ const char *TagName = nullptr, *FixitTagName = nullptr;
+ tok::TokenKind TagKind = tok::unknown;
+
+ switch (Actions.isTagName(*Tok.getIdentifierInfo(), getCurScope())) {
+ default: break;
+ case DeclSpec::TST_enum:
+ TagName="enum" ; FixitTagName = "enum " ; TagKind=tok::kw_enum ;break;
+ case DeclSpec::TST_union:
+ TagName="union" ; FixitTagName = "union " ;TagKind=tok::kw_union ;break;
+ case DeclSpec::TST_struct:
+ TagName="struct"; FixitTagName = "struct ";TagKind=tok::kw_struct;break;
+ case DeclSpec::TST_interface:
+ TagName="__interface"; FixitTagName = "__interface ";
+ TagKind=tok::kw___interface;break;
+ case DeclSpec::TST_class:
+ TagName="class" ; FixitTagName = "class " ;TagKind=tok::kw_class ;break;
+ }
+
+ if (TagName) {
+ IdentifierInfo *TokenName = Tok.getIdentifierInfo();
+ LookupResult R(Actions, TokenName, SourceLocation(),
+ Sema::LookupOrdinaryName);
+
+ Diag(Loc, diag::err_use_of_tag_name_without_tag)
+ << TokenName << TagName << getLangOpts().CPlusPlus
+ << FixItHint::CreateInsertion(Tok.getLocation(), FixitTagName);
+
+ if (Actions.LookupParsedName(R, getCurScope(), SS)) {
+ for (LookupResult::iterator I = R.begin(), IEnd = R.end();
+ I != IEnd; ++I)
+ Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type)
+ << TokenName << TagName;
+ }
+
+ // Parse this as a tag as if the missing tag were present.
+ if (TagKind == tok::kw_enum)
+ ParseEnumSpecifier(Loc, DS, TemplateInfo, AS,
+ DeclSpecContext::DSC_normal);
+ else
+ ParseClassSpecifier(TagKind, Loc, DS, TemplateInfo, AS,
+ /*EnteringContext*/ false,
+ DeclSpecContext::DSC_normal, Attrs);
+ return true;
+ }
+ }
+
+ // Determine whether this identifier could plausibly be the name of something
+ // being declared (with a missing type).
+ if (!isTypeSpecifier(DSC) && (!SS || DSC == DeclSpecContext::DSC_top_level ||
+ DSC == DeclSpecContext::DSC_class)) {
+ // Look ahead to the next token to try to figure out what this declaration
+ // was supposed to be.
+ switch (NextToken().getKind()) {
+ case tok::l_paren: {
+ // static x(4); // 'x' is not a type
+ // x(int n); // 'x' is not a type
+ // x (*p)[]; // 'x' is a type
+ //
+ // Since we're in an error case, we can afford to perform a tentative
+ // parse to determine which case we're in.
+ TentativeParsingAction PA(*this);
+ ConsumeToken();
+ TPResult TPR = TryParseDeclarator(/*mayBeAbstract*/false);
+ PA.Revert();
+
+ if (TPR != TPResult::False) {
+ // The identifier is followed by a parenthesized declarator.
+ // It's supposed to be a type.
+ break;
+ }
+
+ // If we're in a context where we could be declaring a constructor,
+ // check whether this is a constructor declaration with a bogus name.
+ if (DSC == DeclSpecContext::DSC_class ||
+ (DSC == DeclSpecContext::DSC_top_level && SS)) {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (Actions.isCurrentClassNameTypo(II, SS)) {
+ Diag(Loc, diag::err_constructor_bad_name)
+ << Tok.getIdentifierInfo() << II
+ << FixItHint::CreateReplacement(Tok.getLocation(), II->getName());
+ Tok.setIdentifierInfo(II);
+ }
+ }
+ // Fall through.
+ [[fallthrough]];
+ }
+ case tok::comma:
+ case tok::equal:
+ case tok::kw_asm:
+ case tok::l_brace:
+ case tok::l_square:
+ case tok::semi:
+ // This looks like a variable or function declaration. The type is
+ // probably missing. We're done parsing decl-specifiers.
+ // But only if we are not in a function prototype scope.
+ if (getCurScope()->isFunctionPrototypeScope())
+ break;
+ if (SS)
+ AnnotateScopeToken(*SS, /*IsNewAnnotation*/false);
+ return false;
+
+ default:
+ // This is probably supposed to be a type. This includes cases like:
+ // int f(itn);
+ // struct S { unsigned : 4; };
+ break;
+ }
+ }
+
+ // This is almost certainly an invalid type name. Let Sema emit a diagnostic
+ // and attempt to recover.
+ ParsedType T;
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ bool IsTemplateName = getLangOpts().CPlusPlus && NextToken().is(tok::less);
+ Actions.DiagnoseUnknownTypeName(II, Loc, getCurScope(), SS, T,
+ IsTemplateName);
+ if (T) {
+ // The action has suggested that the type T could be used. Set that as
+ // the type in the declaration specifiers, consume the would-be type
+ // name token, and we're done.
+ const char *PrevSpec;
+ unsigned DiagID;
+ DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, T,
+ Actions.getASTContext().getPrintingPolicy());
+ DS.SetRangeEnd(Tok.getLocation());
+ ConsumeToken();
+ // There may be other declaration specifiers after this.
+ return true;
+ } else if (II != Tok.getIdentifierInfo()) {
+ // If no type was suggested, the correction is to a keyword
+ Tok.setKind(II->getTokenID());
+ // There may be other declaration specifiers after this.
+ return true;
+ }
+
+ // Otherwise, the action had no suggestion for us. Mark this as an error.
+ DS.SetTypeSpecError();
+ DS.SetRangeEnd(Tok.getLocation());
+ ConsumeToken();
+
+ // Eat any following template arguments.
+ if (IsTemplateName) {
+ SourceLocation LAngle, RAngle;
+ TemplateArgList Args;
+ ParseTemplateIdAfterTemplateName(true, LAngle, Args, RAngle);
+ }
+
+ // TODO: Could inject an invalid typedef decl in an enclosing scope to
+ // avoid rippling error messages on subsequent uses of the same type,
+ // could be useful if #include was forgotten.
+ return true;
+}
+
+/// Determine the declaration specifier context from the declarator
+/// context.
+///
+/// \param Context the declarator context, which is one of the
+/// DeclaratorContext enumerator values.
+Parser::DeclSpecContext
+Parser::getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context) {
+ switch (Context) {
+ case DeclaratorContext::Member:
+ return DeclSpecContext::DSC_class;
+ case DeclaratorContext::File:
+ return DeclSpecContext::DSC_top_level;
+ case DeclaratorContext::TemplateParam:
+ return DeclSpecContext::DSC_template_param;
+ case DeclaratorContext::TemplateArg:
+ return DeclSpecContext::DSC_template_arg;
+ case DeclaratorContext::TemplateTypeArg:
+ return DeclSpecContext::DSC_template_type_arg;
+ case DeclaratorContext::TrailingReturn:
+ case DeclaratorContext::TrailingReturnVar:
+ return DeclSpecContext::DSC_trailing;
+ case DeclaratorContext::AliasDecl:
+ case DeclaratorContext::AliasTemplate:
+ return DeclSpecContext::DSC_alias_declaration;
+ case DeclaratorContext::Association:
+ return DeclSpecContext::DSC_association;
+ case DeclaratorContext::TypeName:
+ return DeclSpecContext::DSC_type_specifier;
+ case DeclaratorContext::Condition:
+ return DeclSpecContext::DSC_condition;
+ case DeclaratorContext::ConversionId:
+ return DeclSpecContext::DSC_conv_operator;
+ case DeclaratorContext::Prototype:
+ case DeclaratorContext::ObjCResult:
+ case DeclaratorContext::ObjCParameter:
+ case DeclaratorContext::KNRTypeList:
+ case DeclaratorContext::FunctionalCast:
+ case DeclaratorContext::Block:
+ case DeclaratorContext::ForInit:
+ case DeclaratorContext::SelectionInit:
+ case DeclaratorContext::CXXNew:
+ case DeclaratorContext::CXXCatch:
+ case DeclaratorContext::ObjCCatch:
+ case DeclaratorContext::BlockLiteral:
+ case DeclaratorContext::LambdaExpr:
+ case DeclaratorContext::LambdaExprParameter:
+ case DeclaratorContext::RequiresExpr:
+ return DeclSpecContext::DSC_normal;
+ }
+
+ llvm_unreachable("Missing DeclaratorContext case");
+}
+
+/// ParseAlignArgument - Parse the argument to an alignment-specifier.
+///
+/// FIXME: Simply returns an alignof() expression if the argument is a
+/// type. Ideally, the type should be propagated directly into Sema.
+///
+/// [C11] type-id
+/// [C11] constant-expression
+/// [C++0x] type-id ...[opt]
+/// [C++0x] assignment-expression ...[opt]
+ExprResult Parser::ParseAlignArgument(SourceLocation Start,
+ SourceLocation &EllipsisLoc) {
+ ExprResult ER;
+ if (isTypeIdInParens()) {
+ SourceLocation TypeLoc = Tok.getLocation();
+ ParsedType Ty = ParseTypeName().get();
+ SourceRange TypeRange(Start, Tok.getLocation());
+ ER = Actions.ActOnUnaryExprOrTypeTraitExpr(TypeLoc, UETT_AlignOf, true,
+ Ty.getAsOpaquePtr(), TypeRange);
+ } else
+ ER = ParseConstantExpression();
+
+ if (getLangOpts().CPlusPlus11)
+ TryConsumeToken(tok::ellipsis, EllipsisLoc);
+
+ return ER;
+}
+
+/// ParseAlignmentSpecifier - Parse an alignment-specifier, and add the
+/// attribute to Attrs.
+///
+/// alignment-specifier:
+/// [C11] '_Alignas' '(' type-id ')'
+/// [C11] '_Alignas' '(' constant-expression ')'
+/// [C++11] 'alignas' '(' type-id ...[opt] ')'
+/// [C++11] 'alignas' '(' assignment-expression ...[opt] ')'
+void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
+ SourceLocation *EndLoc) {
+ assert(Tok.isOneOf(tok::kw_alignas, tok::kw__Alignas) &&
+ "Not an alignment-specifier!");
+
+ IdentifierInfo *KWName = Tok.getIdentifierInfo();
+ SourceLocation KWLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume())
+ return;
+
+ SourceLocation EllipsisLoc;
+ ExprResult ArgExpr = ParseAlignArgument(T.getOpenLocation(), EllipsisLoc);
+ if (ArgExpr.isInvalid()) {
+ T.skipToEnd();
+ return;
+ }
+
+ T.consumeClose();
+ if (EndLoc)
+ *EndLoc = T.getCloseLocation();
+
+ ArgsVector ArgExprs;
+ ArgExprs.push_back(ArgExpr.get());
+ Attrs.addNew(KWName, KWLoc, nullptr, KWLoc, ArgExprs.data(), 1,
+ ParsedAttr::AS_Keyword, EllipsisLoc);
+}
+
+ExprResult Parser::ParseExtIntegerArgument() {
+ assert(Tok.isOneOf(tok::kw__ExtInt, tok::kw__BitInt) &&
+ "Not an extended int type");
+ ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume())
+ return ExprError();
+
+ ExprResult ER = ParseConstantExpression();
+ if (ER.isInvalid()) {
+ T.skipToEnd();
+ return ExprError();
+ }
+
+ if(T.consumeClose())
+ return ExprError();
+ return ER;
+}
+
+/// Determine whether we're looking at something that might be a declarator
+/// in a simple-declaration. If it can't possibly be a declarator, maybe
+/// diagnose a missing semicolon after a prior tag definition in the decl
+/// specifier.
+///
+/// \return \c true if an error occurred and this can't be any kind of
+/// declaration.
+bool
+Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
+ DeclSpecContext DSContext,
+ LateParsedAttrList *LateAttrs) {
+ assert(DS.hasTagDefinition() && "shouldn't call this");
+
+ bool EnteringContext = (DSContext == DeclSpecContext::DSC_class ||
+ DSContext == DeclSpecContext::DSC_top_level);
+
+ if (getLangOpts().CPlusPlus &&
+ Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_decltype,
+ tok::annot_template_id) &&
+ TryAnnotateCXXScopeToken(EnteringContext)) {
+ SkipMalformedDecl();
+ return true;
+ }
+
+ bool HasScope = Tok.is(tok::annot_cxxscope);
+ // Make a copy in case GetLookAheadToken invalidates the result of NextToken.
+ Token AfterScope = HasScope ? NextToken() : Tok;
+
+ // Determine whether the following tokens could possibly be a
+ // declarator.
+ bool MightBeDeclarator = true;
+ if (Tok.isOneOf(tok::kw_typename, tok::annot_typename)) {
+ // A declarator-id can't start with 'typename'.
+ MightBeDeclarator = false;
+ } else if (AfterScope.is(tok::annot_template_id)) {
+ // If we have a type expressed as a template-id, this cannot be a
+ // declarator-id (such a type cannot be redeclared in a simple-declaration).
+ TemplateIdAnnotation *Annot =
+ static_cast<TemplateIdAnnotation *>(AfterScope.getAnnotationValue());
+ if (Annot->Kind == TNK_Type_template)
+ MightBeDeclarator = false;
+ } else if (AfterScope.is(tok::identifier)) {
+ const Token &Next = HasScope ? GetLookAheadToken(2) : NextToken();
+
+ // These tokens cannot come after the declarator-id in a
+ // simple-declaration, and are likely to come after a type-specifier.
+ if (Next.isOneOf(tok::star, tok::amp, tok::ampamp, tok::identifier,
+ tok::annot_cxxscope, tok::coloncolon)) {
+ // Missing a semicolon.
+ MightBeDeclarator = false;
+ } else if (HasScope) {
+ // If the declarator-id has a scope specifier, it must redeclare a
+ // previously-declared entity. If that's a type (and this is not a
+ // typedef), that's an error.
+ CXXScopeSpec SS;
+ Actions.RestoreNestedNameSpecifierAnnotation(
+ Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS);
+ IdentifierInfo *Name = AfterScope.getIdentifierInfo();
+ Sema::NameClassification Classification = Actions.ClassifyName(
+ getCurScope(), SS, Name, AfterScope.getLocation(), Next,
+ /*CCC=*/nullptr);
+ switch (Classification.getKind()) {
+ case Sema::NC_Error:
+ SkipMalformedDecl();
+ return true;
+
+ case Sema::NC_Keyword:
+ llvm_unreachable("typo correction is not possible here");
+
+ case Sema::NC_Type:
+ case Sema::NC_TypeTemplate:
+ case Sema::NC_UndeclaredNonType:
+ case Sema::NC_UndeclaredTemplate:
+ // Not a previously-declared non-type entity.
+ MightBeDeclarator = false;
+ break;
+
+ case Sema::NC_Unknown:
+ case Sema::NC_NonType:
+ case Sema::NC_DependentNonType:
+ case Sema::NC_OverloadSet:
+ case Sema::NC_VarTemplate:
+ case Sema::NC_FunctionTemplate:
+ case Sema::NC_Concept:
+ // Might be a redeclaration of a prior entity.
+ break;
+ }
+ }
+ }
+
+ if (MightBeDeclarator)
+ return false;
+
+ const PrintingPolicy &PPol = Actions.getASTContext().getPrintingPolicy();
+ Diag(PP.getLocForEndOfToken(DS.getRepAsDecl()->getEndLoc()),
+ diag::err_expected_after)
+ << DeclSpec::getSpecifierName(DS.getTypeSpecType(), PPol) << tok::semi;
+
+ // Try to recover from the typo, by dropping the tag definition and parsing
+ // the problematic tokens as a type.
+ //
+ // FIXME: Split the DeclSpec into pieces for the standalone
+ // declaration and pieces for the following declaration, instead
+ // of assuming that all the other pieces attach to new declaration,
+ // and call ParsedFreeStandingDeclSpec as appropriate.
+ DS.ClearTypeSpecType();
+ ParsedTemplateInfo NotATemplate;
+ ParseDeclarationSpecifiers(DS, NotATemplate, AS, DSContext, LateAttrs);
+ return false;
+}
+
+// Choose the apprpriate diagnostic error for why fixed point types are
+// disabled, set the previous specifier, and mark as invalid.
+static void SetupFixedPointError(const LangOptions &LangOpts,
+ const char *&PrevSpec, unsigned &DiagID,
+ bool &isInvalid) {
+ assert(!LangOpts.FixedPoint);
+ DiagID = diag::err_fixed_point_not_enabled;
+ PrevSpec = ""; // Not used by diagnostic
+ isInvalid = true;
+}
+
+/// ParseDeclarationSpecifiers
+/// declaration-specifiers: [C99 6.7]
+/// storage-class-specifier declaration-specifiers[opt]
+/// type-specifier declaration-specifiers[opt]
+/// [C99] function-specifier declaration-specifiers[opt]
+/// [C11] alignment-specifier declaration-specifiers[opt]
+/// [GNU] attributes declaration-specifiers[opt]
+/// [Clang] '__module_private__' declaration-specifiers[opt]
+/// [ObjC1] '__kindof' declaration-specifiers[opt]
+///
+/// storage-class-specifier: [C99 6.7.1]
+/// 'typedef'
+/// 'extern'
+/// 'static'
+/// 'auto'
+/// 'register'
+/// [C++] 'mutable'
+/// [C++11] 'thread_local'
+/// [C11] '_Thread_local'
+/// [GNU] '__thread'
+/// function-specifier: [C99 6.7.4]
+/// [C99] 'inline'
+/// [C++] 'virtual'
+/// [C++] 'explicit'
+/// [OpenCL] '__kernel'
+/// 'friend': [C++ dcl.friend]
+/// 'constexpr': [C++0x dcl.constexpr]
+void Parser::ParseDeclarationSpecifiers(
+ DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS,
+ DeclSpecContext DSContext, LateParsedAttrList *LateAttrs,
+ ImplicitTypenameContext AllowImplicitTypename) {
+ 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());
+ }
+
+ // If we are in a operator context, convert it back into a type specifier
+ // context for better error handling later on.
+ if (DSContext == DeclSpecContext::DSC_conv_operator) {
+ // No implicit typename here.
+ AllowImplicitTypename = ImplicitTypenameContext::No;
+ DSContext = DeclSpecContext::DSC_type_specifier;
+ }
+
+ bool EnteringContext = (DSContext == DeclSpecContext::DSC_class ||
+ DSContext == DeclSpecContext::DSC_top_level);
+ bool AttrsLastTime = false;
+ ParsedAttributes 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 / C2x attributes that aren't type attributes.
+ for (const ParsedAttr &PA : attrs) {
+ if (!PA.isCXX11Attribute() && !PA.isC2xAttribute())
+ continue;
+ if (PA.getKind() == ParsedAttr::UnknownAttribute)
+ // We will warn about the unknown attribute elsewhere (in
+ // SemaDeclAttr.cpp)
+ continue;
+ // GCC ignores this attribute when placed on the DeclSpec in [[]]
+ // syntax, so we do the same.
+ if (PA.getKind() == ParsedAttr::AT_VectorSize) {
+ Diag(PA.getLoc(), diag::warn_attribute_ignored) << PA;
+ PA.setInvalid();
+ continue;
+ }
+ // We reject AT_LifetimeBound and AT_AnyX86NoCfCheck, even though they
+ // are type attributes, because we historically haven't allowed these
+ // to be used as type attributes in C++11 / C2x syntax.
+ if (PA.isTypeAttr() && PA.getKind() != ParsedAttr::AT_LifetimeBound &&
+ PA.getKind() != ParsedAttr::AT_AnyX86NoCfCheck)
+ continue;
+ Diag(PA.getLoc(), diag::err_attribute_not_type_attr) << PA;
+ PA.setInvalid();
+ }
+
+ 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;
+ }
+
+ // Class context can appear inside a function/block, so prioritise that.
+ 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 (getCurScope()->getFnParent() || getCurScope()->getBlockParent())
+ CCC = Sema::PCC_LocalDeclarationSpecifiers;
+ 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,
+ /*DeductionGuide=*/false,
+ DS.isFriendSpecified())) {
+ // 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, AllowImplicitTypename);
+ 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 (AllowImplicitTypename == ImplicitTypenameContext::Yes &&
+ Next.is(tok::annot_template_id) &&
+ static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
+ ->Kind == TNK_Dependent_template_name) {
+ DS.getTypeSpecScope() = SS;
+ ConsumeAnnotationToken(); // The C++ scope.
+ AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
+ continue;
+ }
+
+ 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,
+ /*DeductionGuide=*/false,
+ DS.isFriendSpecified()))
+ 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), AllowImplicitTypename);
+
+ 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();
+ ParsedAttributes 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:
+ ParseIdentifier: {
+ // 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,
+ /*DeductionGuide=*/false,
+ DS.isFriendSpecified()))
+ 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;
+ ParsedAttributes 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
+ // arguments, drop it and just form 'auto' or 'decltype(auto)'.
+ if (TemplateId->hasInvalidArgs())
+ TemplateId = nullptr;
+
+ // Any of the following tokens are likely the start of the user
+ // forgetting 'auto' or 'decltype(auto)', so diagnose.
+ // Note: if updating this list, please make sure we update
+ // isCXXDeclarationSpecifier's check for IsPlaceholderSpecifier to have
+ // a matching list.
+ if (NextToken().isOneOf(tok::identifier, tok::kw_const,
+ tok::kw_volatile, tok::kw_restrict, tok::amp,
+ tok::ampamp)) {
+ 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();
+ DS.setTypeArgumentRange(Tracker.getRange());
+ // 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, AutoLoc, 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,
+ /*DeductionGuide=*/false,
+ DS.isFriendSpecified()))
+ 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, AllowImplicitTypename);
+ continue;
+ }
+
+ // Attributes support.
+ case tok::kw___attribute:
+ case tok::kw___declspec:
+ ParseAttributes(PAKM_GNU | PAKM_Declspec, DS.getAttributes(), 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;
+
+ // CUDA/HIP single token adornments.
+ case tok::kw___noinline__:
+ ParseCUDAFunctionAttributes(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.
+ ParsedAttributes 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;
+
+ // C2x/GNU typeof support.
+ case tok::kw_typeof:
+ case tok::kw_typeof_unqual:
+ 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;
+
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
+ // HACK: libstdc++ already uses '__remove_cv' as an alias template so we
+ // work around this by expecting all transform type traits to be suffixed
+ // with '('. They're an identifier otherwise.
+ if (!MaybeParseTypeTransformTypeSpecifier(DS))
+ goto ParseIdentifier;
+ 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;
+ }
+ [[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;
+ [[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::kw_groupshared:
+ // NOTE: ParseHLSLQualifiers will consume the qualifier token.
+ ParseHLSLQualifiers(DS.getAttributes());
+ continue;
+
+ 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;
+ }
+}
+
+/// ParseStructDeclaration - Parse a struct declaration without the terminating
+/// semicolon.
+///
+/// Note that a struct declaration refers to a declaration in a struct,
+/// not to the declaration of a struct.
+///
+/// struct-declaration:
+/// [C2x] attributes-specifier-seq[opt]
+/// specifier-qualifier-list struct-declarator-list
+/// [GNU] __extension__ struct-declaration
+/// [GNU] specifier-qualifier-list
+/// struct-declarator-list:
+/// struct-declarator
+/// struct-declarator-list ',' struct-declarator
+/// [GNU] struct-declarator-list ',' attributes[opt] struct-declarator
+/// struct-declarator:
+/// declarator
+/// [GNU] declarator attributes[opt]
+/// declarator[opt] ':' constant-expression
+/// [GNU] declarator[opt] ':' constant-expression attributes[opt]
+///
+void Parser::ParseStructDeclaration(
+ ParsingDeclSpec &DS,
+ llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback) {
+
+ if (Tok.is(tok::kw___extension__)) {
+ // __extension__ silences extension warnings in the subexpression.
+ ExtensionRAIIObject O(Diags); // Use RAII to do this.
+ ConsumeToken();
+ return ParseStructDeclaration(DS, FieldsCallback);
+ }
+
+ // Parse leading attributes.
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+
+ // Parse the common specifier-qualifiers-list piece.
+ ParseSpecifierQualifierList(DS);
+
+ // If there are no declarators, this is a free-standing declaration
+ // specifier. Let the actions module cope with it.
+ if (Tok.is(tok::semi)) {
+ // C2x 6.7.2.1p9 : "The optional attribute specifier sequence in a
+ // member declaration appertains to each of the members declared by the
+ // member declarator list; it shall not appear if the optional member
+ // declarator list is omitted."
+ ProhibitAttributes(Attrs);
+ RecordDecl *AnonRecord = nullptr;
+ Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(
+ getCurScope(), AS_none, DS, ParsedAttributesView::none(), AnonRecord);
+ assert(!AnonRecord && "Did not expect anonymous struct or union here");
+ DS.complete(TheDecl);
+ return;
+ }
+
+ // Read struct-declarators until we find the semicolon.
+ bool FirstDeclarator = true;
+ SourceLocation CommaLoc;
+ while (true) {
+ ParsingFieldDeclarator DeclaratorInfo(*this, DS, Attrs);
+ DeclaratorInfo.D.setCommaLoc(CommaLoc);
+
+ // Attributes are only allowed here on successive declarators.
+ if (!FirstDeclarator) {
+ // However, this does not apply for [[]] attributes (which could show up
+ // before or after the __attribute__ attributes).
+ DiagnoseAndSkipCXX11Attributes();
+ MaybeParseGNUAttributes(DeclaratorInfo.D);
+ DiagnoseAndSkipCXX11Attributes();
+ }
+
+ /// struct-declarator: declarator
+ /// struct-declarator: declarator[opt] ':' constant-expression
+ if (Tok.isNot(tok::colon)) {
+ // Don't parse FOO:BAR as if it were a typo for FOO::BAR.
+ ColonProtectionRAIIObject X(*this);
+ ParseDeclarator(DeclaratorInfo.D);
+ } else
+ DeclaratorInfo.D.SetIdentifier(nullptr, Tok.getLocation());
+
+ if (TryConsumeToken(tok::colon)) {
+ ExprResult Res(ParseConstantExpression());
+ if (Res.isInvalid())
+ SkipUntil(tok::semi, StopBeforeMatch);
+ else
+ DeclaratorInfo.BitfieldSize = Res.get();
+ }
+
+ // If attributes exist after the declarator, parse them.
+ MaybeParseGNUAttributes(DeclaratorInfo.D);
+
+ // We're done with this declarator; invoke the callback.
+ FieldsCallback(DeclaratorInfo);
+
+ // If we don't have a comma, it is either the end of the list (a ';')
+ // or an error, bail out.
+ if (!TryConsumeToken(tok::comma, CommaLoc))
+ return;
+
+ FirstDeclarator = false;
+ }
+}
+
+/// ParseStructUnionBody
+/// struct-contents:
+/// struct-declaration-list
+/// [EXT] empty
+/// [GNU] "struct-declaration-list" without terminating ';'
+/// struct-declaration-list:
+/// struct-declaration
+/// struct-declaration-list struct-declaration
+/// [OBC] '@' 'defs' '(' class-name ')'
+///
+void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
+ DeclSpec::TST TagType, RecordDecl *TagDecl) {
+ PrettyDeclStackTraceEntry CrashInfo(Actions.Context, TagDecl, RecordLoc,
+ "parsing struct/union body");
+ assert(!getLangOpts().CPlusPlus && "C++ declarations not supported");
+
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ if (T.consumeOpen())
+ return;
+
+ ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope);
+ Actions.ActOnTagStartDefinition(getCurScope(), TagDecl);
+
+ // While we still have something to read, read the declarations in the struct.
+ while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
+ Tok.isNot(tok::eof)) {
+ // Each iteration of this loop reads one struct-declaration.
+
+ // Check for extraneous top-level semicolon.
+ if (Tok.is(tok::semi)) {
+ ConsumeExtraSemi(InsideStruct, TagType);
+ continue;
+ }
+
+ // Parse _Static_assert declaration.
+ if (Tok.isOneOf(tok::kw__Static_assert, tok::kw_static_assert)) {
+ SourceLocation DeclEnd;
+ ParseStaticAssertDeclaration(DeclEnd);
+ continue;
+ }
+
+ if (Tok.is(tok::annot_pragma_pack)) {
+ HandlePragmaPack();
+ continue;
+ }
+
+ if (Tok.is(tok::annot_pragma_align)) {
+ HandlePragmaAlign();
+ continue;
+ }
+
+ if (Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp)) {
+ // Result can be ignored, because it must be always empty.
+ AccessSpecifier AS = AS_none;
+ ParsedAttributes Attrs(AttrFactory);
+ (void)ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, Attrs);
+ continue;
+ }
+
+ if (tok::isPragmaAnnotation(Tok.getKind())) {
+ Diag(Tok.getLocation(), diag::err_pragma_misplaced_in_decl)
+ << DeclSpec::getSpecifierName(
+ TagType, Actions.getASTContext().getPrintingPolicy());
+ ConsumeAnnotationToken();
+ continue;
+ }
+
+ if (!Tok.is(tok::at)) {
+ auto CFieldCallback = [&](ParsingFieldDeclarator &FD) {
+ // Install the declarator into the current TagDecl.
+ Decl *Field =
+ Actions.ActOnField(getCurScope(), TagDecl,
+ FD.D.getDeclSpec().getSourceRange().getBegin(),
+ FD.D, FD.BitfieldSize);
+ FD.complete(Field);
+ };
+
+ // Parse all the comma separated declarators.
+ ParsingDeclSpec DS(*this);
+ ParseStructDeclaration(DS, CFieldCallback);
+ } else { // Handle @defs
+ ConsumeToken();
+ if (!Tok.isObjCAtKeyword(tok::objc_defs)) {
+ Diag(Tok, diag::err_unexpected_at);
+ SkipUntil(tok::semi);
+ continue;
+ }
+ ConsumeToken();
+ ExpectAndConsume(tok::l_paren);
+ if (!Tok.is(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ SkipUntil(tok::semi);
+ continue;
+ }
+ SmallVector<Decl *, 16> Fields;
+ Actions.ActOnDefs(getCurScope(), TagDecl, Tok.getLocation(),
+ Tok.getIdentifierInfo(), Fields);
+ ConsumeToken();
+ ExpectAndConsume(tok::r_paren);
+ }
+
+ if (TryConsumeToken(tok::semi))
+ continue;
+
+ if (Tok.is(tok::r_brace)) {
+ ExpectAndConsume(tok::semi, diag::ext_expected_semi_decl_list);
+ break;
+ }
+
+ ExpectAndConsume(tok::semi, diag::err_expected_semi_decl_list);
+ // Skip to end of block or statement to avoid ext-warning on extra ';'.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ // If we stopped at a ';', eat it.
+ TryConsumeToken(tok::semi);
+ }
+
+ T.consumeClose();
+
+ ParsedAttributes attrs(AttrFactory);
+ // If attributes exist after struct contents, parse them.
+ MaybeParseGNUAttributes(attrs);
+
+ SmallVector<Decl *, 32> FieldDecls(TagDecl->fields());
+
+ Actions.ActOnFields(getCurScope(), RecordLoc, TagDecl, FieldDecls,
+ T.getOpenLocation(), T.getCloseLocation(), attrs);
+ StructScope.Exit();
+ Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange());
+}
+
+/// ParseEnumSpecifier
+/// enum-specifier: [C99 6.7.2.2]
+/// 'enum' identifier[opt] '{' enumerator-list '}'
+///[C99/C++]'enum' identifier[opt] '{' enumerator-list ',' '}'
+/// [GNU] 'enum' attributes[opt] identifier[opt] '{' enumerator-list ',' [opt]
+/// '}' attributes[opt]
+/// [MS] 'enum' __declspec[opt] identifier[opt] '{' enumerator-list ',' [opt]
+/// '}'
+/// 'enum' identifier
+/// [GNU] 'enum' attributes[opt] identifier
+///
+/// [C++11] enum-head '{' enumerator-list[opt] '}'
+/// [C++11] enum-head '{' enumerator-list ',' '}'
+///
+/// enum-head: [C++11]
+/// enum-key attribute-specifier-seq[opt] identifier[opt] enum-base[opt]
+/// enum-key attribute-specifier-seq[opt] nested-name-specifier
+/// identifier enum-base[opt]
+///
+/// enum-key: [C++11]
+/// 'enum'
+/// 'enum' 'class'
+/// 'enum' 'struct'
+///
+/// enum-base: [C++11]
+/// ':' type-specifier-seq
+///
+/// [C++] elaborated-type-specifier:
+/// [C++] 'enum' nested-name-specifier[opt] identifier
+///
+void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
+ const ParsedTemplateInfo &TemplateInfo,
+ AccessSpecifier AS, DeclSpecContext DSC) {
+ // Parse the tag portion of this.
+ if (Tok.is(tok::code_completion)) {
+ // Code completion for an enum name.
+ cutOffParsing();
+ Actions.CodeCompleteTag(getCurScope(), DeclSpec::TST_enum);
+ DS.SetTypeSpecError(); // Needed by ActOnUsingDeclaration.
+ return;
+ }
+
+ // If attributes exist after tag, parse them.
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseAttributes(PAKM_GNU | PAKM_Declspec | PAKM_CXX11, attrs);
+
+ SourceLocation ScopedEnumKWLoc;
+ bool IsScopedUsingClassTag = false;
+
+ // In C++11, recognize 'enum class' and 'enum struct'.
+ if (Tok.isOneOf(tok::kw_class, tok::kw_struct) && getLangOpts().CPlusPlus) {
+ Diag(Tok, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_scoped_enum
+ : diag::ext_scoped_enum);
+ IsScopedUsingClassTag = Tok.is(tok::kw_class);
+ ScopedEnumKWLoc = ConsumeToken();
+
+ // Attributes are not allowed between these keywords. Diagnose,
+ // but then just treat them like they appeared in the right place.
+ ProhibitAttributes(attrs);
+
+ // They are allowed afterwards, though.
+ MaybeParseAttributes(PAKM_GNU | PAKM_Declspec | PAKM_CXX11, attrs);
+ }
+
+ // C++11 [temp.explicit]p12:
+ // The usual access controls do not apply to names used to specify
+ // explicit instantiations.
+ // We extend this to also cover explicit specializations. Note that
+ // we don't suppress if this turns out to be an elaborated type
+ // specifier.
+ bool shouldDelayDiagsInTag =
+ (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
+ TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+ SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
+
+ // Determine whether this declaration is permitted to have an enum-base.
+ AllowDefiningTypeSpec AllowEnumSpecifier =
+ isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus);
+ bool CanBeOpaqueEnumDeclaration =
+ DS.isEmpty() && isOpaqueEnumDeclarationContext(DSC);
+ bool CanHaveEnumBase = (getLangOpts().CPlusPlus11 || getLangOpts().ObjC ||
+ getLangOpts().MicrosoftExt) &&
+ (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes ||
+ CanBeOpaqueEnumDeclaration);
+
+ CXXScopeSpec &SS = DS.getTypeSpecScope();
+ if (getLangOpts().CPlusPlus) {
+ // "enum foo : bar;" is not a potential typo for "enum foo::bar;".
+ ColonProtectionRAIIObject X(*this);
+
+ CXXScopeSpec Spec;
+ if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/true))
+ return;
+
+ if (Spec.isSet() && Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ DS.SetTypeSpecError();
+ if (Tok.isNot(tok::l_brace)) {
+ // Has no name and is not a definition.
+ // Skip the rest of this declarator, up until the comma or semicolon.
+ SkipUntil(tok::comma, StopAtSemi);
+ return;
+ }
+ }
+
+ SS = Spec;
+ }
+
+ // Must have either 'enum name' or 'enum {...}' or (rarely) 'enum : T { ... }'.
+ if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace) &&
+ Tok.isNot(tok::colon)) {
+ Diag(Tok, diag::err_expected_either) << tok::identifier << tok::l_brace;
+
+ DS.SetTypeSpecError();
+ // Skip the rest of this declarator, up until the comma or semicolon.
+ SkipUntil(tok::comma, StopAtSemi);
+ return;
+ }
+
+ // If an identifier is present, consume and remember it.
+ IdentifierInfo *Name = nullptr;
+ SourceLocation NameLoc;
+ if (Tok.is(tok::identifier)) {
+ Name = Tok.getIdentifierInfo();
+ NameLoc = ConsumeToken();
+ }
+
+ if (!Name && ScopedEnumKWLoc.isValid()) {
+ // C++0x 7.2p2: The optional identifier shall not be omitted in the
+ // declaration of a scoped enumeration.
+ Diag(Tok, diag::err_scoped_enum_missing_identifier);
+ ScopedEnumKWLoc = SourceLocation();
+ IsScopedUsingClassTag = false;
+ }
+
+ // Okay, end the suppression area. We'll decide whether to emit the
+ // diagnostics in a second.
+ if (shouldDelayDiagsInTag)
+ diagsFromTag.done();
+
+ TypeResult BaseType;
+ SourceRange BaseRange;
+
+ bool CanBeBitfield =
+ getCurScope()->isClassScope() && ScopedEnumKWLoc.isInvalid() && Name;
+
+ // Parse the fixed underlying type.
+ if (Tok.is(tok::colon)) {
+ // This might be an enum-base or part of some unrelated enclosing context.
+ //
+ // 'enum E : base' is permitted in two circumstances:
+ //
+ // 1) As a defining-type-specifier, when followed by '{'.
+ // 2) As the sole constituent of a complete declaration -- when DS is empty
+ // and the next token is ';'.
+ //
+ // The restriction to defining-type-specifiers is important to allow parsing
+ // a ? new enum E : int{}
+ // _Generic(a, enum E : int{})
+ // properly.
+ //
+ // One additional consideration applies:
+ //
+ // C++ [dcl.enum]p1:
+ // A ':' following "enum nested-name-specifier[opt] identifier" within
+ // the decl-specifier-seq of a member-declaration is parsed as part of
+ // an enum-base.
+ //
+ // Other language modes supporting enumerations with fixed underlying types
+ // do not have clear rules on this, so we disambiguate to determine whether
+ // the tokens form a bit-field width or an enum-base.
+
+ if (CanBeBitfield && !isEnumBase(CanBeOpaqueEnumDeclaration)) {
+ // Outside C++11, do not interpret the tokens as an enum-base if they do
+ // not make sense as one. In C++11, it's an error if this happens.
+ if (getLangOpts().CPlusPlus11)
+ Diag(Tok.getLocation(), diag::err_anonymous_enum_bitfield);
+ } else if (CanHaveEnumBase || !ColonIsSacred) {
+ SourceLocation ColonLoc = ConsumeToken();
+
+ // Parse a type-specifier-seq as a type. We can't just ParseTypeName here,
+ // because under -fms-extensions,
+ // enum E : int *p;
+ // declares 'enum E : int; E *p;' not 'enum E : int*; E p;'.
+ DeclSpec DS(AttrFactory);
+ // enum-base is not assumed to be a type and therefore requires the
+ // typename keyword [p0634r3].
+ ParseSpecifierQualifierList(DS, ImplicitTypenameContext::No, AS,
+ DeclSpecContext::DSC_type_specifier);
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ BaseType = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+
+ BaseRange = SourceRange(ColonLoc, DeclaratorInfo.getSourceRange().getEnd());
+
+ if (!getLangOpts().ObjC) {
+ if (getLangOpts().CPlusPlus11)
+ Diag(ColonLoc, diag::warn_cxx98_compat_enum_fixed_underlying_type)
+ << BaseRange;
+ else if (getLangOpts().CPlusPlus)
+ Diag(ColonLoc, diag::ext_cxx11_enum_fixed_underlying_type)
+ << BaseRange;
+ else if (getLangOpts().MicrosoftExt)
+ Diag(ColonLoc, diag::ext_ms_c_enum_fixed_underlying_type)
+ << BaseRange;
+ else
+ Diag(ColonLoc, diag::ext_clang_c_enum_fixed_underlying_type)
+ << BaseRange;
+ }
+ }
+ }
+
+ // There are four options here. If we have 'friend enum foo;' then this is a
+ // friend declaration, and cannot have an accompanying definition. If we have
+ // 'enum foo;', then this is a forward declaration. If we have
+ // 'enum foo {...' then this is a definition. Otherwise we have something
+ // like 'enum foo xyz', a reference.
+ //
+ // This is needed to handle stuff like this right (C99 6.7.2.3p11):
+ // enum foo {..}; void bar() { enum foo; } <- new foo in bar.
+ // enum foo {..}; void bar() { enum foo x; } <- use of old foo.
+ //
+ Sema::TagUseKind TUK;
+ if (AllowEnumSpecifier == AllowDefiningTypeSpec::No)
+ TUK = Sema::TUK_Reference;
+ else if (Tok.is(tok::l_brace)) {
+ if (DS.isFriendSpecified()) {
+ Diag(Tok.getLocation(), diag::err_friend_decl_defines_type)
+ << SourceRange(DS.getFriendSpecLoc());
+ ConsumeBrace();
+ SkipUntil(tok::r_brace, StopAtSemi);
+ // Discard any other definition-only pieces.
+ attrs.clear();
+ ScopedEnumKWLoc = SourceLocation();
+ IsScopedUsingClassTag = false;
+ BaseType = TypeResult();
+ TUK = Sema::TUK_Friend;
+ } else {
+ TUK = Sema::TUK_Definition;
+ }
+ } else if (!isTypeSpecifier(DSC) &&
+ (Tok.is(tok::semi) ||
+ (Tok.isAtStartOfLine() &&
+ !isValidAfterTypeSpecifier(CanBeBitfield)))) {
+ // An opaque-enum-declaration is required to be standalone (no preceding or
+ // following tokens in the declaration). Sema enforces this separately by
+ // diagnosing anything else in the DeclSpec.
+ TUK = DS.isFriendSpecified() ? Sema::TUK_Friend : Sema::TUK_Declaration;
+ if (Tok.isNot(tok::semi)) {
+ // A semicolon was missing after this declaration. Diagnose and recover.
+ ExpectAndConsume(tok::semi, diag::err_expected_after, "enum");
+ PP.EnterToken(Tok, /*IsReinject=*/true);
+ Tok.setKind(tok::semi);
+ }
+ } else {
+ TUK = Sema::TUK_Reference;
+ }
+
+ bool IsElaboratedTypeSpecifier =
+ TUK == Sema::TUK_Reference || TUK == Sema::TUK_Friend;
+
+ // If this is an elaborated type specifier nested in a larger declaration,
+ // and we delayed diagnostics before, just merge them into the current pool.
+ if (TUK == Sema::TUK_Reference && shouldDelayDiagsInTag) {
+ diagsFromTag.redelay();
+ }
+
+ MultiTemplateParamsArg TParams;
+ if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
+ TUK != Sema::TUK_Reference) {
+ if (!getLangOpts().CPlusPlus11 || !SS.isSet()) {
+ // Skip the rest of this declarator, up until the comma or semicolon.
+ Diag(Tok, diag::err_enum_template);
+ SkipUntil(tok::comma, StopAtSemi);
+ return;
+ }
+
+ if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) {
+ // Enumerations can't be explicitly instantiated.
+ DS.SetTypeSpecError();
+ Diag(StartLoc, diag::err_explicit_instantiation_enum);
+ return;
+ }
+
+ assert(TemplateInfo.TemplateParams && "no template parameters");
+ TParams = MultiTemplateParamsArg(TemplateInfo.TemplateParams->data(),
+ TemplateInfo.TemplateParams->size());
+ }
+
+ if (!Name && TUK != Sema::TUK_Definition) {
+ Diag(Tok, diag::err_enumerator_unnamed_no_def);
+
+ DS.SetTypeSpecError();
+ // Skip the rest of this declarator, up until the comma or semicolon.
+ SkipUntil(tok::comma, StopAtSemi);
+ return;
+ }
+
+ // An elaborated-type-specifier has a much more constrained grammar:
+ //
+ // 'enum' nested-name-specifier[opt] identifier
+ //
+ // If we parsed any other bits, reject them now.
+ //
+ // MSVC and (for now at least) Objective-C permit a full enum-specifier
+ // or opaque-enum-declaration anywhere.
+ if (IsElaboratedTypeSpecifier && !getLangOpts().MicrosoftExt &&
+ !getLangOpts().ObjC) {
+ ProhibitCXX11Attributes(attrs, diag::err_attributes_not_allowed,
+ /*DiagnoseEmptyAttrs=*/true);
+ if (BaseType.isUsable())
+ Diag(BaseRange.getBegin(), diag::ext_enum_base_in_type_specifier)
+ << (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes) << BaseRange;
+ else if (ScopedEnumKWLoc.isValid())
+ Diag(ScopedEnumKWLoc, diag::ext_elaborated_enum_class)
+ << FixItHint::CreateRemoval(ScopedEnumKWLoc) << IsScopedUsingClassTag;
+ }
+
+ stripTypeAttributesOffDeclSpec(attrs, DS, TUK);
+
+ Sema::SkipBodyInfo SkipBody;
+ if (!Name && TUK == Sema::TUK_Definition && Tok.is(tok::l_brace) &&
+ NextToken().is(tok::identifier))
+ SkipBody = Actions.shouldSkipAnonEnumBody(getCurScope(),
+ NextToken().getIdentifierInfo(),
+ NextToken().getLocation());
+
+ bool Owned = false;
+ bool IsDependent = false;
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ Decl *TagDecl =
+ Actions.ActOnTag(getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS,
+ Name, NameLoc, attrs, AS, DS.getModulePrivateSpecLoc(),
+ TParams, Owned, IsDependent, ScopedEnumKWLoc,
+ IsScopedUsingClassTag,
+ BaseType, DSC == DeclSpecContext::DSC_type_specifier,
+ DSC == DeclSpecContext::DSC_template_param ||
+ DSC == DeclSpecContext::DSC_template_type_arg,
+ OffsetOfState, &SkipBody).get();
+
+ if (SkipBody.ShouldSkip) {
+ assert(TUK == Sema::TUK_Definition && "can only skip a definition");
+
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+ T.skipToEnd();
+
+ if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc,
+ NameLoc.isValid() ? NameLoc : StartLoc,
+ PrevSpec, DiagID, TagDecl, Owned,
+ Actions.getASTContext().getPrintingPolicy()))
+ Diag(StartLoc, DiagID) << PrevSpec;
+ return;
+ }
+
+ if (IsDependent) {
+ // This enum has a dependent nested-name-specifier. Handle it as a
+ // dependent tag.
+ if (!Name) {
+ DS.SetTypeSpecError();
+ Diag(Tok, diag::err_expected_type_name_after_typename);
+ return;
+ }
+
+ TypeResult Type = Actions.ActOnDependentTag(
+ getCurScope(), DeclSpec::TST_enum, TUK, SS, Name, StartLoc, NameLoc);
+ if (Type.isInvalid()) {
+ DS.SetTypeSpecError();
+ return;
+ }
+
+ if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc,
+ NameLoc.isValid() ? NameLoc : StartLoc,
+ PrevSpec, DiagID, Type.get(),
+ Actions.getASTContext().getPrintingPolicy()))
+ Diag(StartLoc, DiagID) << PrevSpec;
+
+ return;
+ }
+
+ if (!TagDecl) {
+ // The action failed to produce an enumeration tag. If this is a
+ // definition, consume the entire definition.
+ if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) {
+ ConsumeBrace();
+ SkipUntil(tok::r_brace, StopAtSemi);
+ }
+
+ DS.SetTypeSpecError();
+ return;
+ }
+
+ if (Tok.is(tok::l_brace) && TUK == Sema::TUK_Definition) {
+ Decl *D = SkipBody.CheckSameAsPrevious ? SkipBody.New : TagDecl;
+ ParseEnumBody(StartLoc, D);
+ if (SkipBody.CheckSameAsPrevious &&
+ !Actions.ActOnDuplicateDefinition(TagDecl, SkipBody)) {
+ DS.SetTypeSpecError();
+ return;
+ }
+ }
+
+ if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc,
+ NameLoc.isValid() ? NameLoc : StartLoc,
+ PrevSpec, DiagID, TagDecl, Owned,
+ Actions.getASTContext().getPrintingPolicy()))
+ Diag(StartLoc, DiagID) << PrevSpec;
+}
+
+/// ParseEnumBody - Parse a {} enclosed enumerator-list.
+/// enumerator-list:
+/// enumerator
+/// enumerator-list ',' enumerator
+/// enumerator:
+/// enumeration-constant attributes[opt]
+/// enumeration-constant attributes[opt] '=' constant-expression
+/// enumeration-constant:
+/// identifier
+///
+void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) {
+ // Enter the scope of the enum body and start the definition.
+ ParseScope EnumScope(this, Scope::DeclScope | Scope::EnumScope);
+ Actions.ActOnTagStartDefinition(getCurScope(), EnumDecl);
+
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+
+ // C does not allow an empty enumerator-list, C++ does [dcl.enum].
+ if (Tok.is(tok::r_brace) && !getLangOpts().CPlusPlus)
+ Diag(Tok, diag::err_empty_enum);
+
+ SmallVector<Decl *, 32> EnumConstantDecls;
+ SmallVector<SuppressAccessChecks, 32> EnumAvailabilityDiags;
+
+ Decl *LastEnumConstDecl = nullptr;
+
+ // Parse the enumerator-list.
+ while (Tok.isNot(tok::r_brace)) {
+ // Parse enumerator. If failed, try skipping till the start of the next
+ // enumerator definition.
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
+ if (SkipUntil(tok::comma, tok::r_brace, StopBeforeMatch) &&
+ TryConsumeToken(tok::comma))
+ continue;
+ break;
+ }
+ IdentifierInfo *Ident = Tok.getIdentifierInfo();
+ SourceLocation IdentLoc = ConsumeToken();
+
+ // If attributes exist after the enumerator, parse them.
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseGNUAttributes(attrs);
+ if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
+ if (getLangOpts().CPlusPlus)
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
+ ? diag::warn_cxx14_compat_ns_enum_attribute
+ : diag::ext_ns_enum_attribute)
+ << 1 /*enumerator*/;
+ ParseCXX11Attributes(attrs);
+ }
+
+ SourceLocation EqualLoc;
+ ExprResult AssignedVal;
+ EnumAvailabilityDiags.emplace_back(*this);
+
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ if (TryConsumeToken(tok::equal, EqualLoc)) {
+ AssignedVal = ParseConstantExpressionInExprEvalContext();
+ if (AssignedVal.isInvalid())
+ SkipUntil(tok::comma, tok::r_brace, StopBeforeMatch);
+ }
+
+ // Install the enumerator constant into EnumDecl.
+ Decl *EnumConstDecl = Actions.ActOnEnumConstant(
+ getCurScope(), EnumDecl, LastEnumConstDecl, IdentLoc, Ident, attrs,
+ EqualLoc, AssignedVal.get());
+ EnumAvailabilityDiags.back().done();
+
+ EnumConstantDecls.push_back(EnumConstDecl);
+ LastEnumConstDecl = EnumConstDecl;
+
+ if (Tok.is(tok::identifier)) {
+ // We're missing a comma between enumerators.
+ SourceLocation Loc = getEndOfPreviousToken();
+ Diag(Loc, diag::err_enumerator_list_missing_comma)
+ << FixItHint::CreateInsertion(Loc, ", ");
+ continue;
+ }
+
+ // Emumerator definition must be finished, only comma or r_brace are
+ // allowed here.
+ SourceLocation CommaLoc;
+ if (Tok.isNot(tok::r_brace) && !TryConsumeToken(tok::comma, CommaLoc)) {
+ if (EqualLoc.isValid())
+ Diag(Tok.getLocation(), diag::err_expected_either) << tok::r_brace
+ << tok::comma;
+ else
+ Diag(Tok.getLocation(), diag::err_expected_end_of_enumerator);
+ if (SkipUntil(tok::comma, tok::r_brace, StopBeforeMatch)) {
+ if (TryConsumeToken(tok::comma, CommaLoc))
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ // If comma is followed by r_brace, emit appropriate warning.
+ if (Tok.is(tok::r_brace) && CommaLoc.isValid()) {
+ if (!getLangOpts().C99 && !getLangOpts().CPlusPlus11)
+ Diag(CommaLoc, getLangOpts().CPlusPlus ?
+ diag::ext_enumerator_list_comma_cxx :
+ diag::ext_enumerator_list_comma_c)
+ << FixItHint::CreateRemoval(CommaLoc);
+ else if (getLangOpts().CPlusPlus11)
+ Diag(CommaLoc, diag::warn_cxx98_compat_enumerator_list_comma)
+ << FixItHint::CreateRemoval(CommaLoc);
+ break;
+ }
+ }
+
+ // Eat the }.
+ T.consumeClose();
+
+ // If attributes exist after the identifier list, parse them.
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseGNUAttributes(attrs);
+
+ Actions.ActOnEnumBody(StartLoc, T.getRange(), EnumDecl, EnumConstantDecls,
+ getCurScope(), attrs);
+
+ // Now handle enum constant availability diagnostics.
+ assert(EnumConstantDecls.size() == EnumAvailabilityDiags.size());
+ for (size_t i = 0, e = EnumConstantDecls.size(); i != e; ++i) {
+ ParsingDeclRAIIObject PD(*this, ParsingDeclRAIIObject::NoParent);
+ EnumAvailabilityDiags[i].redelay();
+ PD.complete(EnumConstantDecls[i]);
+ }
+
+ EnumScope.Exit();
+ Actions.ActOnTagFinishDefinition(getCurScope(), EnumDecl, T.getRange());
+
+ // The next token must be valid after an enum definition. If not, a ';'
+ // was probably forgotten.
+ bool CanBeBitfield = getCurScope()->isClassScope();
+ if (!isValidAfterTypeSpecifier(CanBeBitfield)) {
+ ExpectAndConsume(tok::semi, diag::err_expected_after, "enum");
+ // Push this token back into the preprocessor and change our current token
+ // to ';' so that the rest of the code recovers as though there were an
+ // ';' after the definition.
+ PP.EnterToken(Tok, /*IsReinject=*/true);
+ Tok.setKind(tok::semi);
+ }
+}
+
+/// isKnownToBeTypeSpecifier - Return true if we know that the specified token
+/// is definitely a type-specifier. Return false if it isn't part of a type
+/// specifier or if we're not sure.
+bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) const {
+ switch (Tok.getKind()) {
+ default: return false;
+ // type-specifiers
+ case tok::kw_short:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw__Complex:
+ case tok::kw__Imaginary:
+ case tok::kw_void:
+ case tok::kw_char:
+ case tok::kw_wchar_t:
+ case tok::kw_char8_t:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ case tok::kw_int:
+ case tok::kw__ExtInt:
+ case tok::kw__BitInt:
+ case tok::kw___bf16:
+ case tok::kw_half:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw__Accum:
+ case tok::kw__Fract:
+ case tok::kw__Float16:
+ case tok::kw___float128:
+ case tok::kw___ibm128:
+ case tok::kw_bool:
+ case tok::kw__Bool:
+ case tok::kw__Decimal32:
+ case tok::kw__Decimal64:
+ case tok::kw__Decimal128:
+ case tok::kw___vector:
+#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
+#include "clang/Basic/OpenCLImageTypes.def"
+
+ // struct-or-union-specifier (C99) or class-specifier (C++)
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw___interface:
+ case tok::kw_union:
+ // enum-specifier
+ case tok::kw_enum:
+
+ // typedef-name
+ case tok::annot_typename:
+ return true;
+ }
+}
+
+/// isTypeSpecifierQualifier - Return true if the current token could be the
+/// start of a specifier-qualifier-list.
+bool Parser::isTypeSpecifierQualifier() {
+ switch (Tok.getKind()) {
+ default: return false;
+
+ case tok::identifier: // foo::bar
+ if (TryAltiVecVectorToken())
+ return true;
+ [[fallthrough]];
+ case tok::kw_typename: // typename T::type
+ // Annotate typenames and C++ scope specifiers. If we get one, just
+ // recurse to handle whatever we get.
+ if (TryAnnotateTypeOrScopeToken())
+ return true;
+ if (Tok.is(tok::identifier))
+ return false;
+ return isTypeSpecifierQualifier();
+
+ case tok::coloncolon: // ::foo::bar
+ if (NextToken().is(tok::kw_new) || // ::new
+ NextToken().is(tok::kw_delete)) // ::delete
+ return false;
+
+ if (TryAnnotateTypeOrScopeToken())
+ return true;
+ return isTypeSpecifierQualifier();
+
+ // GNU attributes support.
+ case tok::kw___attribute:
+ // C2x/GNU typeof support.
+ case tok::kw_typeof:
+ case tok::kw_typeof_unqual:
+
+ // type-specifiers
+ case tok::kw_short:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw__Complex:
+ case tok::kw__Imaginary:
+ case tok::kw_void:
+ case tok::kw_char:
+ case tok::kw_wchar_t:
+ case tok::kw_char8_t:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ case tok::kw_int:
+ case tok::kw__ExtInt:
+ case tok::kw__BitInt:
+ case tok::kw_half:
+ case tok::kw___bf16:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw__Accum:
+ case tok::kw__Fract:
+ case tok::kw__Float16:
+ case tok::kw___float128:
+ case tok::kw___ibm128:
+ case tok::kw_bool:
+ case tok::kw__Bool:
+ case tok::kw__Decimal32:
+ case tok::kw__Decimal64:
+ case tok::kw__Decimal128:
+ case tok::kw___vector:
+#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
+#include "clang/Basic/OpenCLImageTypes.def"
+
+ // struct-or-union-specifier (C99) or class-specifier (C++)
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw___interface:
+ case tok::kw_union:
+ // enum-specifier
+ case tok::kw_enum:
+
+ // type-qualifier
+ case tok::kw_const:
+ case tok::kw_volatile:
+ case tok::kw_restrict:
+ case tok::kw__Sat:
+
+ // Debugger support.
+ case tok::kw___unknown_anytype:
+
+ // typedef-name
+ case tok::annot_typename:
+ return true;
+
+ // GNU ObjC bizarre protocol extension: <proto1,proto2> with implicit 'id'.
+ case tok::less:
+ return getLangOpts().ObjC;
+
+ case tok::kw___cdecl:
+ case tok::kw___stdcall:
+ case tok::kw___fastcall:
+ case tok::kw___thiscall:
+ case tok::kw___regcall:
+ case tok::kw___vectorcall:
+ case tok::kw___w64:
+ case tok::kw___ptr64:
+ case tok::kw___ptr32:
+ case tok::kw___pascal:
+ case tok::kw___unaligned:
+
+ case tok::kw__Nonnull:
+ case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
+ case tok::kw__Null_unspecified:
+
+ case tok::kw___kindof:
+
+ case tok::kw___private:
+ case tok::kw___local:
+ case tok::kw___global:
+ case tok::kw___constant:
+ case tok::kw___generic:
+ case tok::kw___read_only:
+ case tok::kw___read_write:
+ case tok::kw___write_only:
+
+ case tok::kw_groupshared:
+ return true;
+
+ case tok::kw_private:
+ return getLangOpts().OpenCL;
+
+ // C11 _Atomic
+ case tok::kw__Atomic:
+ return true;
+ }
+}
+
+Parser::DeclGroupPtrTy Parser::ParseTopLevelStmtDecl() {
+ assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode");
+
+ // Parse a top-level-stmt.
+ Parser::StmtVector Stmts;
+ ParsedStmtContext SubStmtCtx = ParsedStmtContext();
+ StmtResult R = ParseStatementOrDeclaration(Stmts, SubStmtCtx);
+ if (!R.isUsable())
+ return nullptr;
+
+ SmallVector<Decl *, 2> DeclsInGroup;
+ DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(R.get()));
+ // Currently happens for things like -fms-extensions and use `__if_exists`.
+ for (Stmt *S : Stmts)
+ DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(S));
+
+ return Actions.BuildDeclaratorGroup(DeclsInGroup);
+}
+
+/// isDeclarationSpecifier() - Return true if the current token is part of a
+/// declaration specifier.
+///
+/// \param AllowImplicitTypename whether this is a context where T::type [T
+/// dependent] can appear.
+/// \param DisambiguatingWithExpression True to indicate that the purpose of
+/// this check is to disambiguate between an expression and a declaration.
+bool Parser::isDeclarationSpecifier(
+ ImplicitTypenameContext AllowImplicitTypename,
+ bool DisambiguatingWithExpression) {
+ switch (Tok.getKind()) {
+ default: return false;
+
+ // OpenCL 2.0 and later define this keyword.
+ case tok::kw_pipe:
+ return getLangOpts().OpenCL &&
+ getLangOpts().getOpenCLCompatibleVersion() >= 200;
+
+ case tok::identifier: // foo::bar
+ // Unfortunate hack to support "Class.factoryMethod" notation.
+ if (getLangOpts().ObjC && NextToken().is(tok::period))
+ return false;
+ if (TryAltiVecVectorToken())
+ return true;
+ [[fallthrough]];
+ case tok::kw_decltype: // decltype(T())::type
+ case tok::kw_typename: // typename T::type
+ // Annotate typenames and C++ scope specifiers. If we get one, just
+ // recurse to handle whatever we get.
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
+ return true;
+ if (TryAnnotateTypeConstraint())
+ return true;
+ if (Tok.is(tok::identifier))
+ return false;
+
+ // If we're in Objective-C and we have an Objective-C class type followed
+ // by an identifier and then either ':' or ']', in a place where an
+ // expression is permitted, then this is probably a class message send
+ // missing the initial '['. In this case, we won't consider this to be
+ // the start of a declaration.
+ if (DisambiguatingWithExpression &&
+ isStartOfObjCClassMessageMissingOpenBracket())
+ return false;
+
+ return isDeclarationSpecifier(AllowImplicitTypename);
+
+ case tok::coloncolon: // ::foo::bar
+ if (!getLangOpts().CPlusPlus)
+ return false;
+ if (NextToken().is(tok::kw_new) || // ::new
+ NextToken().is(tok::kw_delete)) // ::delete
+ return false;
+
+ // Annotate typenames and C++ scope specifiers. If we get one, just
+ // recurse to handle whatever we get.
+ if (TryAnnotateTypeOrScopeToken())
+ return true;
+ return isDeclarationSpecifier(ImplicitTypenameContext::No);
+
+ // storage-class-specifier
+ case tok::kw_typedef:
+ case tok::kw_extern:
+ case tok::kw___private_extern__:
+ case tok::kw_static:
+ case tok::kw_auto:
+ case tok::kw___auto_type:
+ case tok::kw_register:
+ case tok::kw___thread:
+ case tok::kw_thread_local:
+ case tok::kw__Thread_local:
+
+ // Modules
+ case tok::kw___module_private__:
+
+ // Debugger support
+ case tok::kw___unknown_anytype:
+
+ // type-specifiers
+ case tok::kw_short:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw__Complex:
+ case tok::kw__Imaginary:
+ case tok::kw_void:
+ case tok::kw_char:
+ case tok::kw_wchar_t:
+ case tok::kw_char8_t:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+
+ case tok::kw_int:
+ case tok::kw__ExtInt:
+ case tok::kw__BitInt:
+ case tok::kw_half:
+ case tok::kw___bf16:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw__Accum:
+ case tok::kw__Fract:
+ case tok::kw__Float16:
+ case tok::kw___float128:
+ case tok::kw___ibm128:
+ case tok::kw_bool:
+ case tok::kw__Bool:
+ case tok::kw__Decimal32:
+ case tok::kw__Decimal64:
+ case tok::kw__Decimal128:
+ case tok::kw___vector:
+
+ // struct-or-union-specifier (C99) or class-specifier (C++)
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw_union:
+ case tok::kw___interface:
+ // enum-specifier
+ case tok::kw_enum:
+
+ // type-qualifier
+ case tok::kw_const:
+ case tok::kw_volatile:
+ case tok::kw_restrict:
+ case tok::kw__Sat:
+
+ // function-specifier
+ case tok::kw_inline:
+ case tok::kw_virtual:
+ case tok::kw_explicit:
+ case tok::kw__Noreturn:
+
+ // alignment-specifier
+ case tok::kw__Alignas:
+
+ // friend keyword.
+ case tok::kw_friend:
+
+ // static_assert-declaration
+ case tok::kw_static_assert:
+ case tok::kw__Static_assert:
+
+ // C2x/GNU typeof support.
+ case tok::kw_typeof:
+ case tok::kw_typeof_unqual:
+
+ // GNU attributes.
+ case tok::kw___attribute:
+
+ // C++11 decltype and constexpr.
+ case tok::annot_decltype:
+ case tok::kw_constexpr:
+
+ // C++20 consteval and constinit.
+ case tok::kw_consteval:
+ case tok::kw_constinit:
+
+ // C11 _Atomic
+ case tok::kw__Atomic:
+ return true;
+
+ // GNU ObjC bizarre protocol extension: <proto1,proto2> with implicit 'id'.
+ case tok::less:
+ return getLangOpts().ObjC;
+
+ // typedef-name
+ case tok::annot_typename:
+ return !DisambiguatingWithExpression ||
+ !isStartOfObjCClassMessageMissingOpenBracket();
+
+ // placeholder-type-specifier
+ case tok::annot_template_id: {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ if (TemplateId->hasInvalidName())
+ return true;
+ // FIXME: What about type templates that have only been annotated as
+ // annot_template_id, not as annot_typename?
+ return isTypeConstraintAnnotation() &&
+ (NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype));
+ }
+
+ case tok::annot_cxxscope: {
+ TemplateIdAnnotation *TemplateId =
+ NextToken().is(tok::annot_template_id)
+ ? takeTemplateIdAnnotation(NextToken())
+ : nullptr;
+ if (TemplateId && TemplateId->hasInvalidName())
+ return true;
+ // FIXME: What about type templates that have only been annotated as
+ // annot_template_id, not as annot_typename?
+ if (NextToken().is(tok::identifier) && TryAnnotateTypeConstraint())
+ return true;
+ return isTypeConstraintAnnotation() &&
+ GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype);
+ }
+
+ case tok::kw___declspec:
+ case tok::kw___cdecl:
+ case tok::kw___stdcall:
+ case tok::kw___fastcall:
+ case tok::kw___thiscall:
+ case tok::kw___regcall:
+ case tok::kw___vectorcall:
+ case tok::kw___w64:
+ case tok::kw___sptr:
+ case tok::kw___uptr:
+ case tok::kw___ptr64:
+ case tok::kw___ptr32:
+ case tok::kw___forceinline:
+ case tok::kw___pascal:
+ case tok::kw___unaligned:
+
+ case tok::kw__Nonnull:
+ case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
+ case tok::kw__Null_unspecified:
+
+ case tok::kw___kindof:
+
+ case tok::kw___private:
+ case tok::kw___local:
+ case tok::kw___global:
+ case tok::kw___constant:
+ case tok::kw___generic:
+ case tok::kw___read_only:
+ case tok::kw___read_write:
+ case tok::kw___write_only:
+#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
+#include "clang/Basic/OpenCLImageTypes.def"
+
+ case tok::kw_groupshared:
+ return true;
+
+ case tok::kw_private:
+ return getLangOpts().OpenCL;
+ }
+}
+
+bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
+ DeclSpec::FriendSpecified IsFriend) {
+ TentativeParsingAction TPA(*this);
+
+ // Parse the C++ scope specifier.
+ CXXScopeSpec SS;
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/true)) {
+ TPA.Revert();
+ return false;
+ }
+
+ // Parse the constructor name.
+ if (Tok.is(tok::identifier)) {
+ // We already know that we have a constructor name; just consume
+ // the token.
+ ConsumeToken();
+ } else if (Tok.is(tok::annot_template_id)) {
+ ConsumeAnnotationToken();
+ } else {
+ TPA.Revert();
+ return false;
+ }
+
+ // There may be attributes here, appertaining to the constructor name or type
+ // we just stepped past.
+ SkipCXX11Attributes();
+
+ // Current class name must be followed by a left parenthesis.
+ if (Tok.isNot(tok::l_paren)) {
+ TPA.Revert();
+ return false;
+ }
+ ConsumeParen();
+
+ // A right parenthesis, or ellipsis followed by a right parenthesis signals
+ // that we have a constructor.
+ if (Tok.is(tok::r_paren) ||
+ (Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren))) {
+ TPA.Revert();
+ return true;
+ }
+
+ // A C++11 attribute here signals that we have a constructor, and is an
+ // attribute on the first constructor parameter.
+ if (getLangOpts().CPlusPlus11 &&
+ isCXX11AttributeSpecifier(/*Disambiguate*/ false,
+ /*OuterMightBeMessageSend*/ true)) {
+ TPA.Revert();
+ return true;
+ }
+
+ // If we need to, enter the specified scope.
+ DeclaratorScopeObj DeclScopeObj(*this, SS);
+ if (SS.isSet() && Actions.ShouldEnterDeclaratorScope(getCurScope(), SS))
+ DeclScopeObj.EnterDeclaratorScope();
+
+ // Optionally skip Microsoft attributes.
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseMicrosoftAttributes(Attrs);
+
+ // Check whether the next token(s) are part of a declaration
+ // specifier, in which case we have the start of a parameter and,
+ // therefore, we know that this is a constructor.
+ // Due to an ambiguity with implicit typename, the above is not enough.
+ // Additionally, check to see if we are a friend.
+ bool IsConstructor = false;
+ if (isDeclarationSpecifier(IsFriend ? ImplicitTypenameContext::No
+ : ImplicitTypenameContext::Yes))
+ IsConstructor = true;
+ else if (Tok.is(tok::identifier) ||
+ (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier))) {
+ // We've seen "C ( X" or "C ( X::Y", but "X" / "X::Y" is not a type.
+ // This might be a parenthesized member name, but is more likely to
+ // be a constructor declaration with an invalid argument type. Keep
+ // looking.
+ if (Tok.is(tok::annot_cxxscope))
+ ConsumeAnnotationToken();
+ ConsumeToken();
+
+ // If this is not a constructor, we must be parsing a declarator,
+ // which must have one of the following syntactic forms (see the
+ // grammar extract at the start of ParseDirectDeclarator):
+ switch (Tok.getKind()) {
+ case tok::l_paren:
+ // C(X ( int));
+ case tok::l_square:
+ // C(X [ 5]);
+ // C(X [ [attribute]]);
+ case tok::coloncolon:
+ // C(X :: Y);
+ // C(X :: *p);
+ // Assume this isn't a constructor, rather than assuming it's a
+ // constructor with an unnamed parameter of an ill-formed type.
+ break;
+
+ case tok::r_paren:
+ // C(X )
+
+ // Skip past the right-paren and any following attributes to get to
+ // the function body or trailing-return-type.
+ ConsumeParen();
+ SkipCXX11Attributes();
+
+ if (DeductionGuide) {
+ // C(X) -> ... is a deduction guide.
+ IsConstructor = Tok.is(tok::arrow);
+ break;
+ }
+ if (Tok.is(tok::colon) || Tok.is(tok::kw_try)) {
+ // Assume these were meant to be constructors:
+ // C(X) : (the name of a bit-field cannot be parenthesized).
+ // C(X) try (this is otherwise ill-formed).
+ IsConstructor = true;
+ }
+ if (Tok.is(tok::semi) || Tok.is(tok::l_brace)) {
+ // If we have a constructor name within the class definition,
+ // assume these were meant to be constructors:
+ // C(X) {
+ // C(X) ;
+ // ... because otherwise we would be declaring a non-static data
+ // member that is ill-formed because it's of the same type as its
+ // surrounding class.
+ //
+ // FIXME: We can actually do this whether or not the name is qualified,
+ // because if it is qualified in this context it must be being used as
+ // a constructor name.
+ // currently, so we're somewhat conservative here.
+ IsConstructor = IsUnqualified;
+ }
+ break;
+
+ default:
+ IsConstructor = true;
+ break;
+ }
+ }
+
+ TPA.Revert();
+ return IsConstructor;
+}
+
+/// ParseTypeQualifierListOpt
+/// type-qualifier-list: [C99 6.7.5]
+/// type-qualifier
+/// [vendor] attributes
+/// [ only if AttrReqs & AR_VendorAttributesParsed ]
+/// type-qualifier-list type-qualifier
+/// [vendor] type-qualifier-list attributes
+/// [ only if AttrReqs & AR_VendorAttributesParsed ]
+/// [C++0x] attribute-specifier[opt] is allowed before cv-qualifier-seq
+/// [ only if AttReqs & AR_CXX11AttributesParsed ]
+/// Note: vendor can be GNU, MS, etc and can be explicitly controlled via
+/// AttrRequirements bitmask values.
+void Parser::ParseTypeQualifierListOpt(
+ DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed,
+ bool IdentifierRequired,
+ std::optional<llvm::function_ref<void()>> CodeCompletionHandler) {
+ if (standardAttributesAllowed() && (AttrReqs & AR_CXX11AttributesParsed) &&
+ isCXX11AttributeSpecifier()) {
+ ParsedAttributes Attrs(AttrFactory);
+ ParseCXX11Attributes(Attrs);
+ DS.takeAttributesFrom(Attrs);
+ }
+
+ SourceLocation EndLoc;
+
+ while (true) {
+ bool isInvalid = false;
+ const char *PrevSpec = nullptr;
+ unsigned DiagID = 0;
+ SourceLocation Loc = Tok.getLocation();
+
+ switch (Tok.getKind()) {
+ case tok::code_completion:
+ cutOffParsing();
+ if (CodeCompletionHandler)
+ (*CodeCompletionHandler)();
+ else
+ Actions.CodeCompleteTypeQualifiers(DS);
+ return;
+
+ 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;
+ case tok::kw__Atomic:
+ if (!AtomicAllowed)
+ goto DoneWithTypeQuals;
+ if (!getLangOpts().C11)
+ Diag(Tok, diag::ext_c11_feature) << Tok.getName();
+ isInvalid = DS.SetTypeQual(DeclSpec::TQ_atomic, Loc, PrevSpec, DiagID,
+ getLangOpts());
+ break;
+
+ // OpenCL qualifiers:
+ case tok::kw_private:
+ if (!getLangOpts().OpenCL)
+ goto DoneWithTypeQuals;
+ [[fallthrough]];
+ case tok::kw___private:
+ case tok::kw___global:
+ case tok::kw___local:
+ case tok::kw___constant:
+ case tok::kw___generic:
+ case tok::kw___read_only:
+ case tok::kw___write_only:
+ case tok::kw___read_write:
+ ParseOpenCLQualifiers(DS.getAttributes());
+ break;
+
+ case tok::kw_groupshared:
+ // NOTE: ParseHLSLQualifiers will consume the qualifier token.
+ ParseHLSLQualifiers(DS.getAttributes());
+ continue;
+
+ case tok::kw___unaligned:
+ isInvalid = DS.SetTypeQual(DeclSpec::TQ_unaligned, Loc, PrevSpec, DiagID,
+ getLangOpts());
+ break;
+ case tok::kw___uptr:
+ // GNU libc headers in C mode use '__uptr' as an identifier which conflicts
+ // with the MS modifier keyword.
+ if ((AttrReqs & AR_DeclspecAttributesParsed) && !getLangOpts().CPlusPlus &&
+ IdentifierRequired && DS.isEmpty() && NextToken().is(tok::semi)) {
+ if (TryKeywordIdentFallback(false))
+ continue;
+ }
+ [[fallthrough]];
+ case tok::kw___sptr:
+ case tok::kw___w64:
+ case tok::kw___ptr64:
+ case tok::kw___ptr32:
+ case tok::kw___cdecl:
+ case tok::kw___stdcall:
+ case tok::kw___fastcall:
+ case tok::kw___thiscall:
+ case tok::kw___regcall:
+ case tok::kw___vectorcall:
+ if (AttrReqs & AR_DeclspecAttributesParsed) {
+ ParseMicrosoftTypeAttributes(DS.getAttributes());
+ continue;
+ }
+ goto DoneWithTypeQuals;
+ case tok::kw___pascal:
+ if (AttrReqs & AR_VendorAttributesParsed) {
+ ParseBorlandTypeAttributes(DS.getAttributes());
+ continue;
+ }
+ goto DoneWithTypeQuals;
+
+ // 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;
+
+ case tok::kw___attribute:
+ if (AttrReqs & AR_GNUAttributesParsedAndRejected)
+ // When GNU attributes are expressly forbidden, diagnose their usage.
+ Diag(Tok, diag::err_attributes_not_allowed);
+
+ // Parse the attributes even if they are rejected to ensure that error
+ // recovery is graceful.
+ if (AttrReqs & AR_GNUAttributesParsed ||
+ AttrReqs & AR_GNUAttributesParsedAndRejected) {
+ ParseGNUAttributes(DS.getAttributes());
+ continue; // do *not* consume the next token!
+ }
+ // otherwise, FALL THROUGH!
+ [[fallthrough]];
+ default:
+ DoneWithTypeQuals:
+ // If this is not a type-qualifier token, we're done reading type
+ // qualifiers. First verify that DeclSpec's are consistent.
+ DS.Finish(Actions, Actions.getASTContext().getPrintingPolicy());
+ if (EndLoc.isValid())
+ DS.SetRangeEnd(EndLoc);
+ return;
+ }
+
+ // If the specifier combination wasn't legal, issue a diagnostic.
+ if (isInvalid) {
+ assert(PrevSpec && "Method did not return previous specifier!");
+ Diag(Tok, DiagID) << PrevSpec;
+ }
+ EndLoc = ConsumeToken();
+ }
+}
+
+/// ParseDeclarator - Parse and verify a newly-initialized declarator.
+void Parser::ParseDeclarator(Declarator &D) {
+ /// This implements the 'declarator' production in the C grammar, then checks
+ /// for well-formedness and issues diagnostics.
+ Actions.runWithSufficientStackSpace(D.getBeginLoc(), [&] {
+ ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator);
+ });
+}
+
+static bool isPtrOperatorToken(tok::TokenKind Kind, const LangOptions &Lang,
+ DeclaratorContext TheContext) {
+ if (Kind == tok::star || Kind == tok::caret)
+ return true;
+
+ // OpenCL 2.0 and later define this keyword.
+ if (Kind == tok::kw_pipe && Lang.OpenCL &&
+ Lang.getOpenCLCompatibleVersion() >= 200)
+ return true;
+
+ if (!Lang.CPlusPlus)
+ return false;
+
+ if (Kind == tok::amp)
+ return true;
+
+ // We parse rvalue refs in C++03, because otherwise the errors are scary.
+ // But we must not parse them in conversion-type-ids and new-type-ids, since
+ // those can be legitimately followed by a && operator.
+ // (The same thing can in theory happen after a trailing-return-type, but
+ // since those are a C++11 feature, there is no rejects-valid issue there.)
+ if (Kind == tok::ampamp)
+ return Lang.CPlusPlus11 || (TheContext != DeclaratorContext::ConversionId &&
+ TheContext != DeclaratorContext::CXXNew);
+
+ return false;
+}
+
+// Indicates whether the given declarator is a pipe declarator.
+static bool isPipeDeclarator(const Declarator &D) {
+ const unsigned NumTypes = D.getNumTypeObjects();
+
+ for (unsigned Idx = 0; Idx != NumTypes; ++Idx)
+ if (DeclaratorChunk::Pipe == D.getTypeObject(Idx).Kind)
+ return true;
+
+ return false;
+}
+
+/// ParseDeclaratorInternal - Parse a C or C++ declarator. The direct-declarator
+/// is parsed by the function passed to it. Pass null, and the direct-declarator
+/// isn't parsed at all, making this function effectively parse the C++
+/// ptr-operator production.
+///
+/// If the grammar of this construct is extended, matching changes must also be
+/// made to TryParseDeclarator and MightBeDeclarator, and possibly to
+/// isConstructorDeclarator.
+///
+/// declarator: [C99 6.7.5] [C++ 8p4, dcl.decl]
+/// [C] pointer[opt] direct-declarator
+/// [C++] direct-declarator
+/// [C++] ptr-operator declarator
+///
+/// pointer: [C99 6.7.5]
+/// '*' type-qualifier-list[opt]
+/// '*' type-qualifier-list[opt] pointer
+///
+/// ptr-operator:
+/// '*' cv-qualifier-seq[opt]
+/// '&'
+/// [C++0x] '&&'
+/// [GNU] '&' restrict[opt] attributes[opt]
+/// [GNU?] '&&' restrict[opt] attributes[opt]
+/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt]
+void Parser::ParseDeclaratorInternal(Declarator &D,
+ DirectDeclParseFunction DirectDeclParser) {
+ if (Diags.hasAllExtensionsSilenced())
+ D.setExtension();
+
+ // C++ member pointers start with a '::' or a nested-name.
+ // Member pointers get special handling, since there's no place for the
+ // scope spec in the generic path below.
+ if (getLangOpts().CPlusPlus &&
+ (Tok.is(tok::coloncolon) || Tok.is(tok::kw_decltype) ||
+ (Tok.is(tok::identifier) &&
+ (NextToken().is(tok::coloncolon) || NextToken().is(tok::less))) ||
+ Tok.is(tok::annot_cxxscope))) {
+ bool EnteringContext = D.getContext() == DeclaratorContext::File ||
+ D.getContext() == DeclaratorContext::Member;
+ CXXScopeSpec SS;
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false, EnteringContext);
+
+ if (SS.isNotEmpty()) {
+ if (Tok.isNot(tok::star)) {
+ // The scope spec really belongs to the direct-declarator.
+ if (D.mayHaveIdentifier())
+ D.getCXXScopeSpec() = SS;
+ else
+ AnnotateScopeToken(SS, true);
+
+ if (DirectDeclParser)
+ (this->*DirectDeclParser)(D);
+ return;
+ }
+
+ if (SS.isValid()) {
+ checkCompoundToken(SS.getEndLoc(), tok::coloncolon,
+ CompoundToken::MemberPtr);
+ }
+
+ SourceLocation StarLoc = ConsumeToken();
+ D.SetRangeEnd(StarLoc);
+ DeclSpec DS(AttrFactory);
+ ParseTypeQualifierListOpt(DS);
+ D.ExtendWithDeclSpec(DS);
+
+ // Recurse to parse whatever is left.
+ Actions.runWithSufficientStackSpace(D.getBeginLoc(), [&] {
+ ParseDeclaratorInternal(D, DirectDeclParser);
+ });
+
+ // Sema will have to catch (syntactically invalid) pointers into global
+ // scope. It has to catch pointers into namespace scope anyway.
+ D.AddTypeInfo(DeclaratorChunk::getMemberPointer(
+ SS, DS.getTypeQualifiers(), StarLoc, DS.getEndLoc()),
+ std::move(DS.getAttributes()),
+ /* Don't replace range end. */ SourceLocation());
+ return;
+ }
+ }
+
+ tok::TokenKind Kind = Tok.getKind();
+
+ if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclarator(D)) {
+ DeclSpec DS(AttrFactory);
+ ParseTypeQualifierListOpt(DS);
+
+ D.AddTypeInfo(
+ DeclaratorChunk::getPipe(DS.getTypeQualifiers(), DS.getPipeLoc()),
+ std::move(DS.getAttributes()), SourceLocation());
+ }
+
+ // Not a pointer, C++ reference, or block.
+ if (!isPtrOperatorToken(Kind, getLangOpts(), D.getContext())) {
+ if (DirectDeclParser)
+ (this->*DirectDeclParser)(D);
+ return;
+ }
+
+ // Otherwise, '*' -> pointer, '^' -> block, '&' -> lvalue reference,
+ // '&&' -> rvalue reference
+ SourceLocation Loc = ConsumeToken(); // Eat the *, ^, & or &&.
+ D.SetRangeEnd(Loc);
+
+ if (Kind == tok::star || Kind == tok::caret) {
+ // Is a pointer.
+ DeclSpec DS(AttrFactory);
+
+ // GNU attributes are not allowed here in a new-type-id, but Declspec and
+ // C++11 attributes are allowed.
+ unsigned Reqs = AR_CXX11AttributesParsed | AR_DeclspecAttributesParsed |
+ ((D.getContext() != DeclaratorContext::CXXNew)
+ ? AR_GNUAttributesParsed
+ : AR_GNUAttributesParsedAndRejected);
+ ParseTypeQualifierListOpt(DS, Reqs, true, !D.mayOmitIdentifier());
+ D.ExtendWithDeclSpec(DS);
+
+ // Recursively parse the declarator.
+ Actions.runWithSufficientStackSpace(
+ D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); });
+ if (Kind == tok::star)
+ // Remember that we parsed a pointer type, and remember the type-quals.
+ D.AddTypeInfo(DeclaratorChunk::getPointer(
+ DS.getTypeQualifiers(), Loc, DS.getConstSpecLoc(),
+ DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(),
+ DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc()),
+ std::move(DS.getAttributes()), SourceLocation());
+ else
+ // Remember that we parsed a Block type, and remember the type-quals.
+ D.AddTypeInfo(
+ DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc),
+ std::move(DS.getAttributes()), SourceLocation());
+ } else {
+ // Is a reference
+ DeclSpec DS(AttrFactory);
+
+ // Complain about rvalue references in C++03, but then go on and build
+ // the declarator.
+ if (Kind == tok::ampamp)
+ Diag(Loc, getLangOpts().CPlusPlus11 ?
+ diag::warn_cxx98_compat_rvalue_reference :
+ diag::ext_rvalue_reference);
+
+ // GNU-style and C++11 attributes are allowed here, as is restrict.
+ ParseTypeQualifierListOpt(DS);
+ D.ExtendWithDeclSpec(DS);
+
+ // C++ 8.3.2p1: cv-qualified references are ill-formed except when the
+ // cv-qualifiers are introduced through the use of a typedef or of a
+ // template type argument, in which case the cv-qualifiers are ignored.
+ if (DS.getTypeQualifiers() != DeclSpec::TQ_unspecified) {
+ if (DS.getTypeQualifiers() & DeclSpec::TQ_const)
+ Diag(DS.getConstSpecLoc(),
+ diag::err_invalid_reference_qualifier_application) << "const";
+ if (DS.getTypeQualifiers() & DeclSpec::TQ_volatile)
+ Diag(DS.getVolatileSpecLoc(),
+ diag::err_invalid_reference_qualifier_application) << "volatile";
+ // 'restrict' is permitted as an extension.
+ if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic)
+ Diag(DS.getAtomicSpecLoc(),
+ diag::err_invalid_reference_qualifier_application) << "_Atomic";
+ }
+
+ // Recursively parse the declarator.
+ Actions.runWithSufficientStackSpace(
+ D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); });
+
+ if (D.getNumTypeObjects() > 0) {
+ // C++ [dcl.ref]p4: There shall be no references to references.
+ DeclaratorChunk& InnerChunk = D.getTypeObject(D.getNumTypeObjects() - 1);
+ if (InnerChunk.Kind == DeclaratorChunk::Reference) {
+ if (const IdentifierInfo *II = D.getIdentifier())
+ Diag(InnerChunk.Loc, diag::err_illegal_decl_reference_to_reference)
+ << II;
+ else
+ Diag(InnerChunk.Loc, diag::err_illegal_decl_reference_to_reference)
+ << "type name";
+
+ // Once we've complained about the reference-to-reference, we
+ // can go ahead and build the (technically ill-formed)
+ // declarator: reference collapsing will take care of it.
+ }
+ }
+
+ // Remember that we parsed a reference type.
+ D.AddTypeInfo(DeclaratorChunk::getReference(DS.getTypeQualifiers(), Loc,
+ Kind == tok::amp),
+ std::move(DS.getAttributes()), SourceLocation());
+ }
+}
+
+// When correcting from misplaced brackets before the identifier, the location
+// is saved inside the declarator so that other diagnostic messages can use
+// them. This extracts and returns that location, or returns the provided
+// location if a stored location does not exist.
+static SourceLocation getMissingDeclaratorIdLoc(Declarator &D,
+ SourceLocation Loc) {
+ if (D.getName().StartLocation.isInvalid() &&
+ D.getName().EndLocation.isValid())
+ return D.getName().EndLocation;
+
+ return Loc;
+}
+
+/// ParseDirectDeclarator
+/// direct-declarator: [C99 6.7.5]
+/// [C99] identifier
+/// '(' declarator ')'
+/// [GNU] '(' attributes declarator ')'
+/// [C90] direct-declarator '[' constant-expression[opt] ']'
+/// [C99] direct-declarator '[' type-qual-list[opt] assignment-expr[opt] ']'
+/// [C99] direct-declarator '[' 'static' type-qual-list[opt] assign-expr ']'
+/// [C99] direct-declarator '[' type-qual-list 'static' assignment-expr ']'
+/// [C99] direct-declarator '[' type-qual-list[opt] '*' ']'
+/// [C++11] direct-declarator '[' constant-expression[opt] ']'
+/// attribute-specifier-seq[opt]
+/// direct-declarator '(' parameter-type-list ')'
+/// direct-declarator '(' identifier-list[opt] ')'
+/// [GNU] direct-declarator '(' parameter-forward-declarations
+/// parameter-type-list[opt] ')'
+/// [C++] direct-declarator '(' parameter-declaration-clause ')'
+/// cv-qualifier-seq[opt] exception-specification[opt]
+/// [C++11] direct-declarator '(' parameter-declaration-clause ')'
+/// attribute-specifier-seq[opt] cv-qualifier-seq[opt]
+/// ref-qualifier[opt] exception-specification[opt]
+/// [C++] declarator-id
+/// [C++11] declarator-id attribute-specifier-seq[opt]
+///
+/// declarator-id: [C++ 8]
+/// '...'[opt] id-expression
+/// '::'[opt] nested-name-specifier[opt] type-name
+///
+/// id-expression: [C++ 5.1]
+/// unqualified-id
+/// qualified-id
+///
+/// unqualified-id: [C++ 5.1]
+/// identifier
+/// operator-function-id
+/// conversion-function-id
+/// '~' class-name
+/// template-id
+///
+/// C++17 adds the following, which we also handle here:
+///
+/// simple-declaration:
+/// <decl-spec> '[' identifier-list ']' brace-or-equal-initializer ';'
+///
+/// Note, any additional constructs added here may need corresponding changes
+/// in isConstructorDeclarator.
+void Parser::ParseDirectDeclarator(Declarator &D) {
+ DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec());
+
+ if (getLangOpts().CPlusPlus && D.mayHaveIdentifier()) {
+ // This might be a C++17 structured binding.
+ if (Tok.is(tok::l_square) && !D.mayOmitIdentifier() &&
+ D.getCXXScopeSpec().isEmpty())
+ return ParseDecompositionDeclarator(D);
+
+ // Don't parse FOO:BAR as if it were a typo for FOO::BAR inside a class, in
+ // this context it is a bitfield. Also in range-based for statement colon
+ // may delimit for-range-declaration.
+ ColonProtectionRAIIObject X(
+ *this, D.getContext() == DeclaratorContext::Member ||
+ (D.getContext() == DeclaratorContext::ForInit &&
+ getLangOpts().CPlusPlus11));
+
+ // ParseDeclaratorInternal might already have parsed the scope.
+ if (D.getCXXScopeSpec().isEmpty()) {
+ bool EnteringContext = D.getContext() == DeclaratorContext::File ||
+ D.getContext() == DeclaratorContext::Member;
+ ParseOptionalCXXScopeSpecifier(
+ D.getCXXScopeSpec(), /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false, EnteringContext);
+ }
+
+ if (D.getCXXScopeSpec().isValid()) {
+ if (Actions.ShouldEnterDeclaratorScope(getCurScope(),
+ D.getCXXScopeSpec()))
+ // Change the declaration context for name lookup, until this function
+ // is exited (and the declarator has been parsed).
+ DeclScopeObj.EnterDeclaratorScope();
+ else if (getObjCDeclContext()) {
+ // Ensure that we don't interpret the next token as an identifier when
+ // dealing with declarations in an Objective-C container.
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ D.setInvalidType(true);
+ ConsumeToken();
+ goto PastIdentifier;
+ }
+ }
+
+ // C++0x [dcl.fct]p14:
+ // There is a syntactic ambiguity when an ellipsis occurs at the end of a
+ // parameter-declaration-clause without a preceding comma. In this case,
+ // the ellipsis is parsed as part of the abstract-declarator if the type
+ // of the parameter either names a template parameter pack that has not
+ // been expanded or contains auto; otherwise, it is parsed as part of the
+ // parameter-declaration-clause.
+ if (Tok.is(tok::ellipsis) && D.getCXXScopeSpec().isEmpty() &&
+ !((D.getContext() == DeclaratorContext::Prototype ||
+ D.getContext() == DeclaratorContext::LambdaExprParameter ||
+ D.getContext() == DeclaratorContext::BlockLiteral) &&
+ NextToken().is(tok::r_paren) && !D.hasGroupingParens() &&
+ !Actions.containsUnexpandedParameterPacks(D) &&
+ D.getDeclSpec().getTypeSpecType() != TST_auto)) {
+ SourceLocation EllipsisLoc = ConsumeToken();
+ if (isPtrOperatorToken(Tok.getKind(), getLangOpts(), D.getContext())) {
+ // The ellipsis was put in the wrong place. Recover, and explain to
+ // the user what they should have done.
+ ParseDeclarator(D);
+ if (EllipsisLoc.isValid())
+ DiagnoseMisplacedEllipsisInDeclarator(EllipsisLoc, D);
+ return;
+ } else
+ D.setEllipsisLoc(EllipsisLoc);
+
+ // The ellipsis can't be followed by a parenthesized declarator. We
+ // check for that in ParseParenDeclarator, after we have disambiguated
+ // the l_paren token.
+ }
+
+ if (Tok.isOneOf(tok::identifier, tok::kw_operator, tok::annot_template_id,
+ tok::tilde)) {
+ // We found something that indicates the start of an unqualified-id.
+ // Parse that unqualified-id.
+ bool AllowConstructorName;
+ bool AllowDeductionGuide;
+ if (D.getDeclSpec().hasTypeSpecifier()) {
+ AllowConstructorName = false;
+ AllowDeductionGuide = false;
+ } else if (D.getCXXScopeSpec().isSet()) {
+ AllowConstructorName = (D.getContext() == DeclaratorContext::File ||
+ D.getContext() == DeclaratorContext::Member);
+ AllowDeductionGuide = false;
+ } else {
+ AllowConstructorName = (D.getContext() == DeclaratorContext::Member);
+ AllowDeductionGuide = (D.getContext() == DeclaratorContext::File ||
+ D.getContext() == DeclaratorContext::Member);
+ }
+
+ bool HadScope = D.getCXXScopeSpec().isValid();
+ if (ParseUnqualifiedId(D.getCXXScopeSpec(),
+ /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/true,
+ /*AllowDestructorName=*/true, AllowConstructorName,
+ AllowDeductionGuide, nullptr, D.getName()) ||
+ // Once we're past the identifier, if the scope was bad, mark the
+ // whole declarator bad.
+ D.getCXXScopeSpec().isInvalid()) {
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ D.setInvalidType(true);
+ } else {
+ // ParseUnqualifiedId might have parsed a scope specifier during error
+ // recovery. If it did so, enter that scope.
+ if (!HadScope && D.getCXXScopeSpec().isValid() &&
+ Actions.ShouldEnterDeclaratorScope(getCurScope(),
+ D.getCXXScopeSpec()))
+ DeclScopeObj.EnterDeclaratorScope();
+
+ // Parsed the unqualified-id; update range information and move along.
+ if (D.getSourceRange().getBegin().isInvalid())
+ D.SetRangeBegin(D.getName().getSourceRange().getBegin());
+ D.SetRangeEnd(D.getName().getSourceRange().getEnd());
+ }
+ goto PastIdentifier;
+ }
+
+ if (D.getCXXScopeSpec().isNotEmpty()) {
+ // We have a scope specifier but no following unqualified-id.
+ Diag(PP.getLocForEndOfToken(D.getCXXScopeSpec().getEndLoc()),
+ diag::err_expected_unqualified_id)
+ << /*C++*/1;
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ goto PastIdentifier;
+ }
+ } else if (Tok.is(tok::identifier) && D.mayHaveIdentifier()) {
+ assert(!getLangOpts().CPlusPlus &&
+ "There's a C++-specific check for tok::identifier above");
+ assert(Tok.getIdentifierInfo() && "Not an identifier?");
+ D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ D.SetRangeEnd(Tok.getLocation());
+ ConsumeToken();
+ goto PastIdentifier;
+ } else if (Tok.is(tok::identifier) && !D.mayHaveIdentifier()) {
+ // We're not allowed an identifier here, but we got one. Try to figure out
+ // if the user was trying to attach a name to the type, or whether the name
+ // is some unrelated trailing syntax.
+ bool DiagnoseIdentifier = false;
+ if (D.hasGroupingParens())
+ // An identifier within parens is unlikely to be intended to be anything
+ // other than a name being "declared".
+ DiagnoseIdentifier = true;
+ else if (D.getContext() == DeclaratorContext::TemplateArg)
+ // T<int N> is an accidental identifier; T<int N indicates a missing '>'.
+ DiagnoseIdentifier =
+ NextToken().isOneOf(tok::comma, tok::greater, tok::greatergreater);
+ else if (D.getContext() == DeclaratorContext::AliasDecl ||
+ D.getContext() == DeclaratorContext::AliasTemplate)
+ // The most likely error is that the ';' was forgotten.
+ DiagnoseIdentifier = NextToken().isOneOf(tok::comma, tok::semi);
+ else if ((D.getContext() == DeclaratorContext::TrailingReturn ||
+ D.getContext() == DeclaratorContext::TrailingReturnVar) &&
+ !isCXX11VirtSpecifier(Tok))
+ DiagnoseIdentifier = NextToken().isOneOf(
+ tok::comma, tok::semi, tok::equal, tok::l_brace, tok::kw_try);
+ if (DiagnoseIdentifier) {
+ Diag(Tok.getLocation(), diag::err_unexpected_unqualified_id)
+ << FixItHint::CreateRemoval(Tok.getLocation());
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ ConsumeToken();
+ goto PastIdentifier;
+ }
+ }
+
+ if (Tok.is(tok::l_paren)) {
+ // If this might be an abstract-declarator followed by a direct-initializer,
+ // check whether this is a valid declarator chunk. If it can't be, assume
+ // that it's an initializer instead.
+ if (D.mayOmitIdentifier() && D.mayBeFollowedByCXXDirectInit()) {
+ RevertingTentativeParsingAction PA(*this);
+ if (TryParseDeclarator(true, D.mayHaveIdentifier(), true) ==
+ TPResult::False) {
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ goto PastIdentifier;
+ }
+ }
+
+ // direct-declarator: '(' declarator ')'
+ // direct-declarator: '(' attributes declarator ')'
+ // Example: 'char (*X)' or 'int (*XX)(void)'
+ ParseParenDeclarator(D);
+
+ // If the declarator was parenthesized, we entered the declarator
+ // scope when parsing the parenthesized declarator, then exited
+ // the scope already. Re-enter the scope, if we need to.
+ if (D.getCXXScopeSpec().isSet()) {
+ // If there was an error parsing parenthesized declarator, declarator
+ // scope may have been entered before. Don't do it again.
+ if (!D.isInvalidType() &&
+ Actions.ShouldEnterDeclaratorScope(getCurScope(),
+ D.getCXXScopeSpec()))
+ // Change the declaration context for name lookup, until this function
+ // is exited (and the declarator has been parsed).
+ DeclScopeObj.EnterDeclaratorScope();
+ }
+ } else if (D.mayOmitIdentifier()) {
+ // This could be something simple like "int" (in which case the declarator
+ // portion is empty), if an abstract-declarator is allowed.
+ D.SetIdentifier(nullptr, Tok.getLocation());
+
+ // The grammar for abstract-pack-declarator does not allow grouping parens.
+ // FIXME: Revisit this once core issue 1488 is resolved.
+ if (D.hasEllipsis() && D.hasGroupingParens())
+ Diag(PP.getLocForEndOfToken(D.getEllipsisLoc()),
+ diag::ext_abstract_pack_declarator_parens);
+ } else {
+ if (Tok.getKind() == tok::annot_pragma_parser_crash)
+ LLVM_BUILTIN_TRAP;
+ if (Tok.is(tok::l_square))
+ return ParseMisplacedBracketDeclarator(D);
+ if (D.getContext() == DeclaratorContext::Member) {
+ // Objective-C++: Detect C++ keywords and try to prevent further errors by
+ // treating these keyword as valid member names.
+ if (getLangOpts().ObjC && getLangOpts().CPlusPlus &&
+ Tok.getIdentifierInfo() &&
+ Tok.getIdentifierInfo()->isCPlusPlusKeyword(getLangOpts())) {
+ Diag(getMissingDeclaratorIdLoc(D, Tok.getLocation()),
+ diag::err_expected_member_name_or_semi_objcxx_keyword)
+ << Tok.getIdentifierInfo()
+ << (D.getDeclSpec().isEmpty() ? SourceRange()
+ : D.getDeclSpec().getSourceRange());
+ D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ D.SetRangeEnd(Tok.getLocation());
+ ConsumeToken();
+ goto PastIdentifier;
+ }
+ Diag(getMissingDeclaratorIdLoc(D, Tok.getLocation()),
+ diag::err_expected_member_name_or_semi)
+ << (D.getDeclSpec().isEmpty() ? SourceRange()
+ : D.getDeclSpec().getSourceRange());
+ } else {
+ if (Tok.getKind() == tok::TokenKind::kw_while) {
+ Diag(Tok, diag::err_while_loop_outside_of_a_function);
+ } else if (getLangOpts().CPlusPlus) {
+ if (Tok.isOneOf(tok::period, tok::arrow))
+ Diag(Tok, diag::err_invalid_operator_on_type) << Tok.is(tok::arrow);
+ else {
+ SourceLocation Loc = D.getCXXScopeSpec().getEndLoc();
+ if (Tok.isAtStartOfLine() && Loc.isValid())
+ Diag(PP.getLocForEndOfToken(Loc), diag::err_expected_unqualified_id)
+ << getLangOpts().CPlusPlus;
+ else
+ Diag(getMissingDeclaratorIdLoc(D, Tok.getLocation()),
+ diag::err_expected_unqualified_id)
+ << getLangOpts().CPlusPlus;
+ }
+ } else {
+ Diag(getMissingDeclaratorIdLoc(D, Tok.getLocation()),
+ diag::err_expected_either)
+ << tok::identifier << tok::l_paren;
+ }
+ }
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ D.setInvalidType(true);
+ }
+
+ PastIdentifier:
+ assert(D.isPastIdentifier() &&
+ "Haven't past the location of the identifier yet?");
+
+ // Don't parse attributes unless we have parsed an unparenthesized name.
+ if (D.hasName() && !D.getNumTypeObjects())
+ MaybeParseCXX11Attributes(D);
+
+ while (true) {
+ if (Tok.is(tok::l_paren)) {
+ bool IsFunctionDeclaration = D.isFunctionDeclaratorAFunctionDeclaration();
+ // Enter function-declaration scope, limiting any declarators to the
+ // function prototype scope, including parameter declarators.
+ ParseScope PrototypeScope(this,
+ Scope::FunctionPrototypeScope|Scope::DeclScope|
+ (IsFunctionDeclaration
+ ? Scope::FunctionDeclarationScope : 0));
+
+ // The paren may be part of a C++ direct initializer, eg. "int x(1);".
+ // In such a case, check if we actually have a function declarator; if it
+ // is not, the declarator has been fully parsed.
+ bool IsAmbiguous = false;
+ if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit()) {
+ // C++2a [temp.res]p5
+ // A qualified-id is assumed to name a type if
+ // - [...]
+ // - it is a decl-specifier of the decl-specifier-seq of a
+ // - [...]
+ // - parameter-declaration in a member-declaration [...]
+ // - parameter-declaration in a declarator of a function or function
+ // template declaration whose declarator-id is qualified [...]
+ auto AllowImplicitTypename = ImplicitTypenameContext::No;
+ if (D.getCXXScopeSpec().isSet())
+ AllowImplicitTypename =
+ (ImplicitTypenameContext)Actions.isDeclaratorFunctionLike(D);
+ else if (D.getContext() == DeclaratorContext::Member) {
+ AllowImplicitTypename = ImplicitTypenameContext::Yes;
+ }
+
+ // The name of the declarator, if any, is tentatively declared within
+ // a possible direct initializer.
+ TentativelyDeclaredIdentifiers.push_back(D.getIdentifier());
+ bool IsFunctionDecl =
+ isCXXFunctionDeclarator(&IsAmbiguous, AllowImplicitTypename);
+ TentativelyDeclaredIdentifiers.pop_back();
+ if (!IsFunctionDecl)
+ break;
+ }
+ ParsedAttributes attrs(AttrFactory);
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ if (IsFunctionDeclaration)
+ Actions.ActOnStartFunctionDeclarationDeclarator(D,
+ TemplateParameterDepth);
+ ParseFunctionDeclarator(D, attrs, T, IsAmbiguous);
+ if (IsFunctionDeclaration)
+ Actions.ActOnFinishFunctionDeclarationDeclarator(D);
+ PrototypeScope.Exit();
+ } else if (Tok.is(tok::l_square)) {
+ ParseBracketDeclarator(D);
+ } else if (Tok.is(tok::kw_requires) && D.hasGroupingParens()) {
+ // This declarator is declaring a function, but the requires clause is
+ // in the wrong place:
+ // void (f() requires true);
+ // instead of
+ // void f() requires true;
+ // or
+ // void (f()) requires true;
+ Diag(Tok, diag::err_requires_clause_inside_parens);
+ ConsumeToken();
+ ExprResult TrailingRequiresClause = Actions.CorrectDelayedTyposInExpr(
+ ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true));
+ if (TrailingRequiresClause.isUsable() && D.isFunctionDeclarator() &&
+ !D.hasTrailingRequiresClause())
+ // We're already ill-formed if we got here but we'll accept it anyway.
+ D.setTrailingRequiresClause(TrailingRequiresClause.get());
+ } else {
+ break;
+ }
+ }
+}
+
+void Parser::ParseDecompositionDeclarator(Declarator &D) {
+ assert(Tok.is(tok::l_square));
+
+ // If this doesn't look like a structured binding, maybe it's a misplaced
+ // array declarator.
+ // FIXME: Consume the l_square first so we don't need extra lookahead for
+ // this.
+ if (!(NextToken().is(tok::identifier) &&
+ GetLookAheadToken(2).isOneOf(tok::comma, tok::r_square)) &&
+ !(NextToken().is(tok::r_square) &&
+ GetLookAheadToken(2).isOneOf(tok::equal, tok::l_brace)))
+ return ParseMisplacedBracketDeclarator(D);
+
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+
+ SmallVector<DecompositionDeclarator::Binding, 32> Bindings;
+ while (Tok.isNot(tok::r_square)) {
+ if (!Bindings.empty()) {
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ else {
+ if (Tok.is(tok::identifier)) {
+ SourceLocation EndLoc = getEndOfPreviousToken();
+ Diag(EndLoc, diag::err_expected)
+ << tok::comma << FixItHint::CreateInsertion(EndLoc, ",");
+ } else {
+ Diag(Tok, diag::err_expected_comma_or_rsquare);
+ }
+
+ SkipUntil(tok::r_square, tok::comma, tok::identifier,
+ StopAtSemi | StopBeforeMatch);
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ else if (Tok.isNot(tok::identifier))
+ break;
+ }
+ }
+
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ break;
+ }
+
+ Bindings.push_back({Tok.getIdentifierInfo(), Tok.getLocation()});
+ ConsumeToken();
+ }
+
+ if (Tok.isNot(tok::r_square))
+ // We've already diagnosed a problem here.
+ T.skipToEnd();
+ else {
+ // C++17 does not allow the identifier-list in a structured binding
+ // to be empty.
+ if (Bindings.empty())
+ Diag(Tok.getLocation(), diag::ext_decomp_decl_empty);
+
+ T.consumeClose();
+ }
+
+ return D.setDecompositionBindings(T.getOpenLocation(), Bindings,
+ T.getCloseLocation());
+}
+
+/// ParseParenDeclarator - We parsed the declarator D up to a paren. This is
+/// only called before the identifier, so these are most likely just grouping
+/// parens for precedence. If we find that these are actually function
+/// parameter parens in an abstract-declarator, we call ParseFunctionDeclarator.
+///
+/// direct-declarator:
+/// '(' declarator ')'
+/// [GNU] '(' attributes declarator ')'
+/// direct-declarator '(' parameter-type-list ')'
+/// direct-declarator '(' identifier-list[opt] ')'
+/// [GNU] direct-declarator '(' parameter-forward-declarations
+/// parameter-type-list[opt] ')'
+///
+void Parser::ParseParenDeclarator(Declarator &D) {
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ assert(!D.isPastIdentifier() && "Should be called before passing identifier");
+
+ // Eat any attributes before we look at whether this is a grouping or function
+ // declarator paren. If this is a grouping paren, the attribute applies to
+ // the type being built up, for example:
+ // int (__attribute__(()) *x)(long y)
+ // If this ends up not being a grouping paren, the attribute applies to the
+ // first argument, for example:
+ // int (__attribute__(()) int x)
+ // In either case, we need to eat any attributes to be able to determine what
+ // sort of paren this is.
+ //
+ ParsedAttributes attrs(AttrFactory);
+ bool RequiresArg = false;
+ if (Tok.is(tok::kw___attribute)) {
+ ParseGNUAttributes(attrs);
+
+ // We require that the argument list (if this is a non-grouping paren) be
+ // present even if the attribute list was empty.
+ RequiresArg = true;
+ }
+
+ // Eat any Microsoft extensions.
+ ParseMicrosoftTypeAttributes(attrs);
+
+ // Eat any Borland extensions.
+ if (Tok.is(tok::kw___pascal))
+ ParseBorlandTypeAttributes(attrs);
+
+ // If we haven't past the identifier yet (or where the identifier would be
+ // stored, if this is an abstract declarator), then this is probably just
+ // grouping parens. However, if this could be an abstract-declarator, then
+ // this could also be the start of function arguments (consider 'void()').
+ bool isGrouping;
+
+ if (!D.mayOmitIdentifier()) {
+ // If this can't be an abstract-declarator, this *must* be a grouping
+ // paren, because we haven't seen the identifier yet.
+ isGrouping = true;
+ } else if (Tok.is(tok::r_paren) || // 'int()' is a function.
+ (getLangOpts().CPlusPlus && Tok.is(tok::ellipsis) &&
+ NextToken().is(tok::r_paren)) || // C++ int(...)
+ isDeclarationSpecifier(
+ ImplicitTypenameContext::No) || // 'int(int)' is a function.
+ isCXX11AttributeSpecifier()) { // 'int([[]]int)' is a function.
+ // This handles C99 6.7.5.3p11: in "typedef int X; void foo(X)", X is
+ // considered to be a type, not a K&R identifier-list.
+ isGrouping = false;
+ } else {
+ // Otherwise, this is a grouping paren, e.g. 'int (*X)' or 'int(X)'.
+ isGrouping = true;
+ }
+
+ // If this is a grouping paren, handle:
+ // direct-declarator: '(' declarator ')'
+ // direct-declarator: '(' attributes declarator ')'
+ if (isGrouping) {
+ SourceLocation EllipsisLoc = D.getEllipsisLoc();
+ D.setEllipsisLoc(SourceLocation());
+
+ bool hadGroupingParens = D.hasGroupingParens();
+ D.setGroupingParens(true);
+ ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator);
+ // Match the ')'.
+ T.consumeClose();
+ D.AddTypeInfo(
+ DeclaratorChunk::getParen(T.getOpenLocation(), T.getCloseLocation()),
+ std::move(attrs), T.getCloseLocation());
+
+ D.setGroupingParens(hadGroupingParens);
+
+ // An ellipsis cannot be placed outside parentheses.
+ if (EllipsisLoc.isValid())
+ DiagnoseMisplacedEllipsisInDeclarator(EllipsisLoc, D);
+
+ return;
+ }
+
+ // Okay, if this wasn't a grouping paren, it must be the start of a function
+ // argument list. Recognize that this declarator will never have an
+ // identifier (and remember where it would have been), then call into
+ // ParseFunctionDeclarator to handle of argument list.
+ D.SetIdentifier(nullptr, Tok.getLocation());
+
+ // Enter function-declaration scope, limiting any declarators to the
+ // function prototype scope, including parameter declarators.
+ ParseScope PrototypeScope(this,
+ Scope::FunctionPrototypeScope | Scope::DeclScope |
+ (D.isFunctionDeclaratorAFunctionDeclaration()
+ ? Scope::FunctionDeclarationScope : 0));
+ ParseFunctionDeclarator(D, attrs, T, false, RequiresArg);
+ PrototypeScope.Exit();
+}
+
+void Parser::InitCXXThisScopeForDeclaratorIfRelevant(
+ const Declarator &D, const DeclSpec &DS,
+ std::optional<Sema::CXXThisScopeRAII> &ThisScope) {
+ // C++11 [expr.prim.general]p3:
+ // If a declaration declares a member function or member function
+ // template of a class X, the expression this is a prvalue of type
+ // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
+ // and the end of the function-definition, member-declarator, or
+ // declarator.
+ // FIXME: currently, "static" case isn't handled correctly.
+ bool IsCXX11MemberFunction =
+ getLangOpts().CPlusPlus11 &&
+ D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
+ (D.getContext() == DeclaratorContext::Member
+ ? !D.getDeclSpec().isFriendSpecified()
+ : D.getContext() == DeclaratorContext::File &&
+ D.getCXXScopeSpec().isValid() &&
+ Actions.CurContext->isRecord());
+ if (!IsCXX11MemberFunction)
+ return;
+
+ Qualifiers Q = Qualifiers::fromCVRUMask(DS.getTypeQualifiers());
+ if (D.getDeclSpec().hasConstexprSpecifier() && !getLangOpts().CPlusPlus14)
+ Q.addConst();
+ // FIXME: Collect C++ address spaces.
+ // If there are multiple different address spaces, the source is invalid.
+ // Carry on using the first addr space for the qualifiers of 'this'.
+ // The diagnostic will be given later while creating the function
+ // prototype for the method.
+ if (getLangOpts().OpenCLCPlusPlus) {
+ for (ParsedAttr &attr : DS.getAttributes()) {
+ LangAS ASIdx = attr.asOpenCLLangAS();
+ if (ASIdx != LangAS::Default) {
+ Q.addAddressSpace(ASIdx);
+ break;
+ }
+ }
+ }
+ ThisScope.emplace(Actions, dyn_cast<CXXRecordDecl>(Actions.CurContext), Q,
+ IsCXX11MemberFunction);
+}
+
+/// ParseFunctionDeclarator - We are after the identifier and have parsed the
+/// declarator D up to a paren, which indicates that we are parsing function
+/// arguments.
+///
+/// If FirstArgAttrs is non-null, then the caller parsed those attributes
+/// immediately after the open paren - they will be applied to the DeclSpec
+/// of the first parameter.
+///
+/// If RequiresArg is true, then the first argument of the function is required
+/// to be present and required to not be an identifier list.
+///
+/// For C++, after the parameter-list, it also parses the cv-qualifier-seq[opt],
+/// (C++11) ref-qualifier[opt], exception-specification[opt],
+/// (C++11) attribute-specifier-seq[opt], (C++11) trailing-return-type[opt] and
+/// (C++2a) the trailing requires-clause.
+///
+/// [C++11] exception-specification:
+/// dynamic-exception-specification
+/// noexcept-specification
+///
+void Parser::ParseFunctionDeclarator(Declarator &D,
+ ParsedAttributes &FirstArgAttrs,
+ BalancedDelimiterTracker &Tracker,
+ bool IsAmbiguous,
+ bool RequiresArg) {
+ assert(getCurScope()->isFunctionPrototypeScope() &&
+ "Should call from a Function scope");
+ // lparen is already consumed!
+ assert(D.isPastIdentifier() && "Should not call before identifier!");
+
+ // This should be true when the function has typed arguments.
+ // Otherwise, it is treated as a K&R-style function.
+ bool HasProto = false;
+ // Build up an array of information about the parsed arguments.
+ SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
+ // Remember where we see an ellipsis, if any.
+ SourceLocation EllipsisLoc;
+
+ DeclSpec DS(AttrFactory);
+ bool RefQualifierIsLValueRef = true;
+ SourceLocation RefQualifierLoc;
+ ExceptionSpecificationType ESpecType = EST_None;
+ SourceRange ESpecRange;
+ SmallVector<ParsedType, 2> DynamicExceptions;
+ SmallVector<SourceRange, 2> DynamicExceptionRanges;
+ ExprResult NoexceptExpr;
+ CachedTokens *ExceptionSpecTokens = nullptr;
+ ParsedAttributes FnAttrs(AttrFactory);
+ TypeResult TrailingReturnType;
+ SourceLocation TrailingReturnTypeLoc;
+
+ /* LocalEndLoc is the end location for the local FunctionTypeLoc.
+ EndLoc is the end location for the function declarator.
+ They differ for trailing return types. */
+ SourceLocation StartLoc, LocalEndLoc, EndLoc;
+ SourceLocation LParenLoc, RParenLoc;
+ LParenLoc = Tracker.getOpenLocation();
+ StartLoc = LParenLoc;
+
+ if (isFunctionDeclaratorIdentifierList()) {
+ if (RequiresArg)
+ Diag(Tok, diag::err_argument_required_after_attribute);
+
+ ParseFunctionDeclaratorIdentifierList(D, ParamInfo);
+
+ Tracker.consumeClose();
+ RParenLoc = Tracker.getCloseLocation();
+ LocalEndLoc = RParenLoc;
+ EndLoc = RParenLoc;
+
+ // If there are attributes following the identifier list, parse them and
+ // prohibit them.
+ MaybeParseCXX11Attributes(FnAttrs);
+ ProhibitAttributes(FnAttrs);
+ } else {
+ if (Tok.isNot(tok::r_paren))
+ ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc);
+ else if (RequiresArg)
+ Diag(Tok, diag::err_argument_required_after_attribute);
+
+ // OpenCL disallows functions without a prototype, but it doesn't enforce
+ // strict prototypes as in C2x because it allows a function definition to
+ // have an identifier list. See OpenCL 3.0 6.11/g for more details.
+ HasProto = ParamInfo.size() || getLangOpts().requiresStrictPrototypes() ||
+ getLangOpts().OpenCL;
+
+ // If we have the closing ')', eat it.
+ Tracker.consumeClose();
+ RParenLoc = Tracker.getCloseLocation();
+ LocalEndLoc = RParenLoc;
+ EndLoc = RParenLoc;
+
+ if (getLangOpts().CPlusPlus) {
+ // FIXME: Accept these components in any order, and produce fixits to
+ // correct the order if the user gets it wrong. Ideally we should deal
+ // with the pure-specifier in the same way.
+
+ // Parse cv-qualifier-seq[opt].
+ ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed,
+ /*AtomicAllowed*/ false,
+ /*IdentifierRequired=*/false,
+ llvm::function_ref<void()>([&]() {
+ Actions.CodeCompleteFunctionQualifiers(DS, D);
+ }));
+ if (!DS.getSourceRange().getEnd().isInvalid()) {
+ EndLoc = DS.getSourceRange().getEnd();
+ }
+
+ // Parse ref-qualifier[opt].
+ if (ParseRefQualifier(RefQualifierIsLValueRef, RefQualifierLoc))
+ EndLoc = RefQualifierLoc;
+
+ std::optional<Sema::CXXThisScopeRAII> ThisScope;
+ InitCXXThisScopeForDeclaratorIfRelevant(D, DS, ThisScope);
+
+ // Parse exception-specification[opt].
+ // FIXME: Per [class.mem]p6, all exception-specifications at class scope
+ // should be delayed, including those for non-members (eg, friend
+ // declarations). But only applying this to member declarations is
+ // consistent with what other implementations do.
+ bool Delayed = D.isFirstDeclarationOfMember() &&
+ D.isFunctionDeclaratorAFunctionDeclaration();
+ if (Delayed && Actions.isLibstdcxxEagerExceptionSpecHack(D) &&
+ GetLookAheadToken(0).is(tok::kw_noexcept) &&
+ GetLookAheadToken(1).is(tok::l_paren) &&
+ GetLookAheadToken(2).is(tok::kw_noexcept) &&
+ GetLookAheadToken(3).is(tok::l_paren) &&
+ GetLookAheadToken(4).is(tok::identifier) &&
+ GetLookAheadToken(4).getIdentifierInfo()->isStr("swap")) {
+ // HACK: We've got an exception-specification
+ // noexcept(noexcept(swap(...)))
+ // or
+ // noexcept(noexcept(swap(...)) && noexcept(swap(...)))
+ // on a 'swap' member function. This is a libstdc++ bug; the lookup
+ // for 'swap' will only find the function we're currently declaring,
+ // whereas it expects to find a non-member swap through ADL. Turn off
+ // delayed parsing to give it a chance to find what it expects.
+ Delayed = false;
+ }
+ ESpecType = tryParseExceptionSpecification(Delayed,
+ ESpecRange,
+ DynamicExceptions,
+ DynamicExceptionRanges,
+ NoexceptExpr,
+ ExceptionSpecTokens);
+ if (ESpecType != EST_None)
+ EndLoc = ESpecRange.getEnd();
+
+ // Parse attribute-specifier-seq[opt]. Per DR 979 and DR 1297, this goes
+ // after the exception-specification.
+ MaybeParseCXX11Attributes(FnAttrs);
+
+ // Parse trailing-return-type[opt].
+ LocalEndLoc = EndLoc;
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::arrow)) {
+ Diag(Tok, diag::warn_cxx98_compat_trailing_return_type);
+ if (D.getDeclSpec().getTypeSpecType() == TST_auto)
+ StartLoc = D.getDeclSpec().getTypeSpecTypeLoc();
+ LocalEndLoc = Tok.getLocation();
+ SourceRange Range;
+ TrailingReturnType =
+ ParseTrailingReturnType(Range, D.mayBeFollowedByCXXDirectInit());
+ TrailingReturnTypeLoc = Range.getBegin();
+ EndLoc = Range.getEnd();
+ }
+ } else if (standardAttributesAllowed()) {
+ MaybeParseCXX11Attributes(FnAttrs);
+ }
+ }
+
+ // Collect non-parameter declarations from the prototype if this is a function
+ // declaration. They will be moved into the scope of the function. Only do
+ // this in C and not C++, where the decls will continue to live in the
+ // surrounding context.
+ SmallVector<NamedDecl *, 0> DeclsInPrototype;
+ if (getCurScope()->isFunctionDeclarationScope() && !getLangOpts().CPlusPlus) {
+ for (Decl *D : getCurScope()->decls()) {
+ NamedDecl *ND = dyn_cast<NamedDecl>(D);
+ if (!ND || isa<ParmVarDecl>(ND))
+ continue;
+ DeclsInPrototype.push_back(ND);
+ }
+ }
+
+ // Remember that we parsed a function type, and remember the attributes.
+ D.AddTypeInfo(DeclaratorChunk::getFunction(
+ HasProto, IsAmbiguous, LParenLoc, ParamInfo.data(),
+ ParamInfo.size(), EllipsisLoc, RParenLoc,
+ RefQualifierIsLValueRef, RefQualifierLoc,
+ /*MutableLoc=*/SourceLocation(),
+ ESpecType, ESpecRange, DynamicExceptions.data(),
+ DynamicExceptionRanges.data(), DynamicExceptions.size(),
+ NoexceptExpr.isUsable() ? NoexceptExpr.get() : nullptr,
+ ExceptionSpecTokens, DeclsInPrototype, StartLoc,
+ LocalEndLoc, D, TrailingReturnType, TrailingReturnTypeLoc,
+ &DS),
+ std::move(FnAttrs), EndLoc);
+}
+
+/// ParseRefQualifier - Parses a member function ref-qualifier. Returns
+/// true if a ref-qualifier is found.
+bool Parser::ParseRefQualifier(bool &RefQualifierIsLValueRef,
+ SourceLocation &RefQualifierLoc) {
+ if (Tok.isOneOf(tok::amp, tok::ampamp)) {
+ Diag(Tok, getLangOpts().CPlusPlus11 ?
+ diag::warn_cxx98_compat_ref_qualifier :
+ diag::ext_ref_qualifier);
+
+ RefQualifierIsLValueRef = Tok.is(tok::amp);
+ RefQualifierLoc = ConsumeToken();
+ return true;
+ }
+ return false;
+}
+
+/// isFunctionDeclaratorIdentifierList - This parameter list may have an
+/// identifier list form for a K&R-style function: void foo(a,b,c)
+///
+/// Note that identifier-lists are only allowed for normal declarators, not for
+/// abstract-declarators.
+bool Parser::isFunctionDeclaratorIdentifierList() {
+ return !getLangOpts().requiresStrictPrototypes()
+ && Tok.is(tok::identifier)
+ && !TryAltiVecVectorToken()
+ // K&R identifier lists can't have typedefs as identifiers, per C99
+ // 6.7.5.3p11.
+ && (TryAnnotateTypeOrScopeToken() || !Tok.is(tok::annot_typename))
+ // Identifier lists follow a really simple grammar: the identifiers can
+ // be followed *only* by a ", identifier" or ")". However, K&R
+ // identifier lists are really rare in the brave new modern world, and
+ // it is very common for someone to typo a type in a non-K&R style
+ // list. If we are presented with something like: "void foo(intptr x,
+ // float y)", we don't want to start parsing the function declarator as
+ // though it is a K&R style declarator just because intptr is an
+ // invalid type.
+ //
+ // To handle this, we check to see if the token after the first
+ // identifier is a "," or ")". Only then do we parse it as an
+ // identifier list.
+ && (!Tok.is(tok::eof) &&
+ (NextToken().is(tok::comma) || NextToken().is(tok::r_paren)));
+}
+
+/// ParseFunctionDeclaratorIdentifierList - While parsing a function declarator
+/// we found a K&R-style identifier list instead of a typed parameter list.
+///
+/// After returning, ParamInfo will hold the parsed parameters.
+///
+/// identifier-list: [C99 6.7.5]
+/// identifier
+/// identifier-list ',' identifier
+///
+void Parser::ParseFunctionDeclaratorIdentifierList(
+ Declarator &D,
+ SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo) {
+ // We should never reach this point in C2x or C++.
+ assert(!getLangOpts().requiresStrictPrototypes() &&
+ "Cannot parse an identifier list in C2x or C++");
+
+ // If there was no identifier specified for the declarator, either we are in
+ // an abstract-declarator, or we are in a parameter declarator which was found
+ // to be abstract. In abstract-declarators, identifier lists are not valid:
+ // diagnose this.
+ if (!D.getIdentifier())
+ Diag(Tok, diag::ext_ident_list_in_param);
+
+ // Maintain an efficient lookup of params we have seen so far.
+ llvm::SmallSet<const IdentifierInfo*, 16> ParamsSoFar;
+
+ do {
+ // If this isn't an identifier, report the error and skip until ')'.
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
+ // Forget we parsed anything.
+ ParamInfo.clear();
+ return;
+ }
+
+ IdentifierInfo *ParmII = Tok.getIdentifierInfo();
+
+ // Reject 'typedef int y; int test(x, y)', but continue parsing.
+ if (Actions.getTypeName(*ParmII, Tok.getLocation(), getCurScope()))
+ Diag(Tok, diag::err_unexpected_typedef_ident) << ParmII;
+
+ // Verify that the argument identifier has not already been mentioned.
+ if (!ParamsSoFar.insert(ParmII).second) {
+ Diag(Tok, diag::err_param_redefinition) << ParmII;
+ } else {
+ // Remember this identifier in ParamInfo.
+ ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII,
+ Tok.getLocation(),
+ nullptr));
+ }
+
+ // Eat the identifier.
+ ConsumeToken();
+ // The list continues if we see a comma.
+ } while (TryConsumeToken(tok::comma));
+}
+
+/// ParseParameterDeclarationClause - Parse a (possibly empty) parameter-list
+/// after the opening parenthesis. This function will not parse a K&R-style
+/// identifier list.
+///
+/// DeclContext is the context of the declarator being parsed. If FirstArgAttrs
+/// is non-null, then the caller parsed those attributes immediately after the
+/// open paren - they will be applied to the DeclSpec of the first parameter.
+///
+/// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will
+/// be the location of the ellipsis, if any was parsed.
+///
+/// parameter-type-list: [C99 6.7.5]
+/// parameter-list
+/// parameter-list ',' '...'
+/// [C++] parameter-list '...'
+///
+/// parameter-list: [C99 6.7.5]
+/// parameter-declaration
+/// parameter-list ',' parameter-declaration
+///
+/// parameter-declaration: [C99 6.7.5]
+/// declaration-specifiers declarator
+/// [C++] declaration-specifiers declarator '=' assignment-expression
+/// [C++11] initializer-clause
+/// [GNU] declaration-specifiers declarator attributes
+/// declaration-specifiers abstract-declarator[opt]
+/// [C++] declaration-specifiers abstract-declarator[opt]
+/// '=' assignment-expression
+/// [GNU] declaration-specifiers abstract-declarator[opt] attributes
+/// [C++11] attribute-specifier-seq parameter-declaration
+///
+void Parser::ParseParameterDeclarationClause(
+ DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs,
+ SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
+ SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration) {
+
+ // Avoid exceeding the maximum function scope depth.
+ // See https://bugs.llvm.org/show_bug.cgi?id=19607
+ // Note Sema::ActOnParamDeclarator calls ParmVarDecl::setScopeInfo with
+ // getFunctionPrototypeDepth() - 1.
+ if (getCurScope()->getFunctionPrototypeDepth() - 1 >
+ ParmVarDecl::getMaxFunctionScopeDepth()) {
+ Diag(Tok.getLocation(), diag::err_function_scope_depth_exceeded)
+ << ParmVarDecl::getMaxFunctionScopeDepth();
+ cutOffParsing();
+ return;
+ }
+
+ // C++2a [temp.res]p5
+ // A qualified-id is assumed to name a type if
+ // - [...]
+ // - it is a decl-specifier of the decl-specifier-seq of a
+ // - [...]
+ // - parameter-declaration in a member-declaration [...]
+ // - parameter-declaration in a declarator of a function or function
+ // template declaration whose declarator-id is qualified [...]
+ // - parameter-declaration in a lambda-declarator [...]
+ auto AllowImplicitTypename = ImplicitTypenameContext::No;
+ if (DeclaratorCtx == DeclaratorContext::Member ||
+ DeclaratorCtx == DeclaratorContext::LambdaExpr ||
+ DeclaratorCtx == DeclaratorContext::RequiresExpr ||
+ IsACXXFunctionDeclaration) {
+ AllowImplicitTypename = ImplicitTypenameContext::Yes;
+ }
+
+ do {
+ // FIXME: Issue a diagnostic if we parsed an attribute-specifier-seq
+ // before deciding this was a parameter-declaration-clause.
+ if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
+ break;
+
+ // Parse the declaration-specifiers.
+ // Just use the ParsingDeclaration "scope" of the declarator.
+ DeclSpec DS(AttrFactory);
+
+ ParsedAttributes ArgDeclAttrs(AttrFactory);
+ ParsedAttributes ArgDeclSpecAttrs(AttrFactory);
+
+ if (FirstArgAttrs.Range.isValid()) {
+ // If the caller parsed attributes for the first argument, add them now.
+ // Take them so that we only apply the attributes to the first parameter.
+ // We have already started parsing the decl-specifier sequence, so don't
+ // parse any parameter-declaration pieces that precede it.
+ ArgDeclSpecAttrs.takeAllFrom(FirstArgAttrs);
+ } else {
+ // Parse any C++11 attributes.
+ MaybeParseCXX11Attributes(ArgDeclAttrs);
+
+ // Skip any Microsoft attributes before a param.
+ MaybeParseMicrosoftAttributes(ArgDeclSpecAttrs);
+ }
+
+ SourceLocation DSStart = Tok.getLocation();
+
+ ParseDeclarationSpecifiers(DS, /*TemplateInfo=*/ParsedTemplateInfo(),
+ AS_none, DeclSpecContext::DSC_normal,
+ /*LateAttrs=*/nullptr, AllowImplicitTypename);
+ DS.takeAttributesFrom(ArgDeclSpecAttrs);
+
+ // Parse the declarator. This is "PrototypeContext" or
+ // "LambdaExprParameterContext", because we must accept either
+ // 'declarator' or 'abstract-declarator' here.
+ Declarator ParmDeclarator(DS, ArgDeclAttrs,
+ DeclaratorCtx == DeclaratorContext::RequiresExpr
+ ? DeclaratorContext::RequiresExpr
+ : DeclaratorCtx == DeclaratorContext::LambdaExpr
+ ? DeclaratorContext::LambdaExprParameter
+ : DeclaratorContext::Prototype);
+ ParseDeclarator(ParmDeclarator);
+
+ // Parse GNU attributes, if present.
+ MaybeParseGNUAttributes(ParmDeclarator);
+ if (getLangOpts().HLSL)
+ MaybeParseHLSLSemantics(DS.getAttributes());
+
+ if (Tok.is(tok::kw_requires)) {
+ // User tried to define a requires clause in a parameter declaration,
+ // which is surely not a function declaration.
+ // void f(int (*g)(int, int) requires true);
+ Diag(Tok,
+ diag::err_requires_clause_on_declarator_not_declaring_a_function);
+ ConsumeToken();
+ Actions.CorrectDelayedTyposInExpr(
+ ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true));
+ }
+
+ // Remember this parsed parameter in ParamInfo.
+ IdentifierInfo *ParmII = ParmDeclarator.getIdentifier();
+
+ // DefArgToks is used when the parsing of default arguments needs
+ // to be delayed.
+ std::unique_ptr<CachedTokens> DefArgToks;
+
+ // If no parameter was specified, verify that *something* was specified,
+ // otherwise we have a missing type and identifier.
+ if (DS.isEmpty() && ParmDeclarator.getIdentifier() == nullptr &&
+ ParmDeclarator.getNumTypeObjects() == 0) {
+ // Completely missing, emit error.
+ Diag(DSStart, diag::err_missing_param);
+ } else {
+ // Otherwise, we have something. Add it and let semantic analysis try
+ // to grok it and add the result to the ParamInfo we are building.
+
+ // Last chance to recover from a misplaced ellipsis in an attempted
+ // parameter pack declaration.
+ if (Tok.is(tok::ellipsis) &&
+ (NextToken().isNot(tok::r_paren) ||
+ (!ParmDeclarator.getEllipsisLoc().isValid() &&
+ !Actions.isUnexpandedParameterPackPermitted())) &&
+ Actions.containsUnexpandedParameterPacks(ParmDeclarator))
+ DiagnoseMisplacedEllipsisInDeclarator(ConsumeToken(), ParmDeclarator);
+
+ // Now we are at the point where declarator parsing is finished.
+ //
+ // Try to catch keywords in place of the identifier in a declarator, and
+ // in particular the common case where:
+ // 1 identifier comes at the end of the declarator
+ // 2 if the identifier is dropped, the declarator is valid but anonymous
+ // (no identifier)
+ // 3 declarator parsing succeeds, and then we have a trailing keyword,
+ // which is never valid in a param list (e.g. missing a ',')
+ // And we can't handle this in ParseDeclarator because in general keywords
+ // may be allowed to follow the declarator. (And in some cases there'd be
+ // better recovery like inserting punctuation). ParseDeclarator is just
+ // treating this as an anonymous parameter, and fortunately at this point
+ // we've already almost done that.
+ //
+ // We care about case 1) where the declarator type should be known, and
+ // the identifier should be null.
+ if (!ParmDeclarator.isInvalidType() && !ParmDeclarator.hasName() &&
+ Tok.isNot(tok::raw_identifier) && !Tok.isAnnotation() &&
+ Tok.getIdentifierInfo() &&
+ Tok.getIdentifierInfo()->isKeyword(getLangOpts())) {
+ Diag(Tok, diag::err_keyword_as_parameter) << PP.getSpelling(Tok);
+ // Consume the keyword.
+ ConsumeToken();
+ }
+ // Inform the actions module about the parameter declarator, so it gets
+ // added to the current scope.
+ Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator);
+ // Parse the default argument, if any. We parse the default
+ // arguments in all dialects; the semantic analysis in
+ // ActOnParamDefaultArgument will reject the default argument in
+ // C.
+ if (Tok.is(tok::equal)) {
+ SourceLocation EqualLoc = Tok.getLocation();
+
+ // Parse the default argument
+ if (DeclaratorCtx == DeclaratorContext::Member) {
+ // If we're inside a class definition, cache the tokens
+ // corresponding to the default argument. We'll actually parse
+ // them when we see the end of the class definition.
+ DefArgToks.reset(new CachedTokens);
+
+ SourceLocation ArgStartLoc = NextToken().getLocation();
+ if (!ConsumeAndStoreInitializer(*DefArgToks, CIK_DefaultArgument)) {
+ DefArgToks.reset();
+ Actions.ActOnParamDefaultArgumentError(Param, EqualLoc);
+ } else {
+ Actions.ActOnParamUnparsedDefaultArgument(Param, EqualLoc,
+ ArgStartLoc);
+ }
+ } else {
+ // Consume the '='.
+ ConsumeToken();
+
+ // The argument isn't actually potentially evaluated unless it is
+ // used.
+ EnterExpressionEvaluationContext Eval(
+ Actions,
+ Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed,
+ Param);
+
+ ExprResult DefArgResult;
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+ DefArgResult = ParseBraceInitializer();
+ } else {
+ if (Tok.is(tok::l_paren) && NextToken().is(tok::l_brace)) {
+ Diag(Tok, diag::err_stmt_expr_in_default_arg) << 0;
+ Actions.ActOnParamDefaultArgumentError(Param, EqualLoc);
+ // Skip the statement expression and continue parsing
+ SkipUntil(tok::comma, StopBeforeMatch);
+ continue;
+ }
+ DefArgResult = ParseAssignmentExpression();
+ }
+ DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult);
+ if (DefArgResult.isInvalid()) {
+ Actions.ActOnParamDefaultArgumentError(Param, EqualLoc);
+ SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
+ } else {
+ // Inform the actions module about the default argument
+ Actions.ActOnParamDefaultArgument(Param, EqualLoc,
+ DefArgResult.get());
+ }
+ }
+ }
+
+ ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII,
+ ParmDeclarator.getIdentifierLoc(),
+ Param, std::move(DefArgToks)));
+ }
+
+ if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) {
+ if (!getLangOpts().CPlusPlus) {
+ // We have ellipsis without a preceding ',', which is ill-formed
+ // in C. Complain and provide the fix.
+ Diag(EllipsisLoc, diag::err_missing_comma_before_ellipsis)
+ << FixItHint::CreateInsertion(EllipsisLoc, ", ");
+ } else if (ParmDeclarator.getEllipsisLoc().isValid() ||
+ Actions.containsUnexpandedParameterPacks(ParmDeclarator)) {
+ // It looks like this was supposed to be a parameter pack. Warn and
+ // point out where the ellipsis should have gone.
+ SourceLocation ParmEllipsis = ParmDeclarator.getEllipsisLoc();
+ Diag(EllipsisLoc, diag::warn_misplaced_ellipsis_vararg)
+ << ParmEllipsis.isValid() << ParmEllipsis;
+ if (ParmEllipsis.isValid()) {
+ Diag(ParmEllipsis,
+ diag::note_misplaced_ellipsis_vararg_existing_ellipsis);
+ } else {
+ Diag(ParmDeclarator.getIdentifierLoc(),
+ diag::note_misplaced_ellipsis_vararg_add_ellipsis)
+ << FixItHint::CreateInsertion(ParmDeclarator.getIdentifierLoc(),
+ "...")
+ << !ParmDeclarator.hasName();
+ }
+ Diag(EllipsisLoc, diag::note_misplaced_ellipsis_vararg_add_comma)
+ << FixItHint::CreateInsertion(EllipsisLoc, ", ");
+ }
+
+ // We can't have any more parameters after an ellipsis.
+ break;
+ }
+
+ // If the next token is a comma, consume it and keep reading arguments.
+ } while (TryConsumeToken(tok::comma));
+}
+
+/// [C90] direct-declarator '[' constant-expression[opt] ']'
+/// [C99] direct-declarator '[' type-qual-list[opt] assignment-expr[opt] ']'
+/// [C99] direct-declarator '[' 'static' type-qual-list[opt] assign-expr ']'
+/// [C99] direct-declarator '[' type-qual-list 'static' assignment-expr ']'
+/// [C99] direct-declarator '[' type-qual-list[opt] '*' ']'
+/// [C++11] direct-declarator '[' constant-expression[opt] ']'
+/// attribute-specifier-seq[opt]
+void Parser::ParseBracketDeclarator(Declarator &D) {
+ if (CheckProhibitedCXX11Attribute())
+ return;
+
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+
+ // C array syntax has many features, but by-far the most common is [] and [4].
+ // This code does a fast path to handle some of the most obvious cases.
+ if (Tok.getKind() == tok::r_square) {
+ T.consumeClose();
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseCXX11Attributes(attrs);
+
+ // Remember that we parsed the empty array type.
+ D.AddTypeInfo(DeclaratorChunk::getArray(0, false, false, nullptr,
+ T.getOpenLocation(),
+ T.getCloseLocation()),
+ std::move(attrs), T.getCloseLocation());
+ return;
+ } else if (Tok.getKind() == tok::numeric_constant &&
+ GetLookAheadToken(1).is(tok::r_square)) {
+ // [4] is very common. Parse the numeric constant expression.
+ ExprResult ExprRes(Actions.ActOnNumericConstant(Tok, getCurScope()));
+ ConsumeToken();
+
+ T.consumeClose();
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseCXX11Attributes(attrs);
+
+ // Remember that we parsed a array type, and remember its features.
+ D.AddTypeInfo(DeclaratorChunk::getArray(0, false, false, ExprRes.get(),
+ T.getOpenLocation(),
+ T.getCloseLocation()),
+ std::move(attrs), T.getCloseLocation());
+ return;
+ } else if (Tok.getKind() == tok::code_completion) {
+ cutOffParsing();
+ Actions.CodeCompleteBracketDeclarator(getCurScope());
+ return;
+ }
+
+ // If valid, this location is the position where we read the 'static' keyword.
+ SourceLocation StaticLoc;
+ TryConsumeToken(tok::kw_static, StaticLoc);
+
+ // If there is a type-qualifier-list, read it now.
+ // Type qualifiers in an array subscript are a C99 feature.
+ DeclSpec DS(AttrFactory);
+ ParseTypeQualifierListOpt(DS, AR_CXX11AttributesParsed);
+
+ // If we haven't already read 'static', check to see if there is one after the
+ // type-qualifier-list.
+ if (!StaticLoc.isValid())
+ TryConsumeToken(tok::kw_static, StaticLoc);
+
+ // Handle "direct-declarator [ type-qual-list[opt] * ]".
+ bool isStar = false;
+ ExprResult NumElements;
+
+ // Handle the case where we have '[*]' as the array size. However, a leading
+ // star could be the start of an expression, for example 'X[*p + 4]'. Verify
+ // the token after the star is a ']'. Since stars in arrays are
+ // infrequent, use of lookahead is not costly here.
+ if (Tok.is(tok::star) && GetLookAheadToken(1).is(tok::r_square)) {
+ ConsumeToken(); // Eat the '*'.
+
+ if (StaticLoc.isValid()) {
+ Diag(StaticLoc, diag::err_unspecified_vla_size_with_static);
+ StaticLoc = SourceLocation(); // Drop the static.
+ }
+ isStar = true;
+ } else if (Tok.isNot(tok::r_square)) {
+ // Note, in C89, this production uses the constant-expr production instead
+ // of assignment-expr. The only difference is that assignment-expr allows
+ // things like '=' and '*='. Sema rejects these in C89 mode because they
+ // are not i-c-e's, so we don't need to distinguish between the two here.
+
+ // Parse the constant-expression or assignment-expression now (depending
+ // on dialect).
+ if (getLangOpts().CPlusPlus) {
+ NumElements = ParseConstantExpression();
+ } else {
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ NumElements =
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+ }
+ } else {
+ if (StaticLoc.isValid()) {
+ Diag(StaticLoc, diag::err_unspecified_size_with_static);
+ StaticLoc = SourceLocation(); // Drop the static.
+ }
+ }
+
+ // If there was an error parsing the assignment-expression, recover.
+ if (NumElements.isInvalid()) {
+ D.setInvalidType(true);
+ // If the expression was invalid, skip it.
+ SkipUntil(tok::r_square, StopAtSemi);
+ return;
+ }
+
+ T.consumeClose();
+
+ MaybeParseCXX11Attributes(DS.getAttributes());
+
+ // Remember that we parsed a array type, and remember its features.
+ D.AddTypeInfo(
+ DeclaratorChunk::getArray(DS.getTypeQualifiers(), StaticLoc.isValid(),
+ isStar, NumElements.get(), T.getOpenLocation(),
+ T.getCloseLocation()),
+ std::move(DS.getAttributes()), T.getCloseLocation());
+}
+
+/// Diagnose brackets before an identifier.
+void Parser::ParseMisplacedBracketDeclarator(Declarator &D) {
+ assert(Tok.is(tok::l_square) && "Missing opening bracket");
+ assert(!D.mayOmitIdentifier() && "Declarator cannot omit identifier");
+
+ SourceLocation StartBracketLoc = Tok.getLocation();
+ Declarator TempDeclarator(D.getDeclSpec(), ParsedAttributesView::none(),
+ D.getContext());
+
+ while (Tok.is(tok::l_square)) {
+ ParseBracketDeclarator(TempDeclarator);
+ }
+
+ // Stuff the location of the start of the brackets into the Declarator.
+ // The diagnostics from ParseDirectDeclarator will make more sense if
+ // they use this location instead.
+ if (Tok.is(tok::semi))
+ D.getName().EndLocation = StartBracketLoc;
+
+ SourceLocation SuggestParenLoc = Tok.getLocation();
+
+ // Now that the brackets are removed, try parsing the declarator again.
+ ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator);
+
+ // Something went wrong parsing the brackets, in which case,
+ // ParseBracketDeclarator has emitted an error, and we don't need to emit
+ // one here.
+ if (TempDeclarator.getNumTypeObjects() == 0)
+ return;
+
+ // Determine if parens will need to be suggested in the diagnostic.
+ bool NeedParens = false;
+ if (D.getNumTypeObjects() != 0) {
+ switch (D.getTypeObject(D.getNumTypeObjects() - 1).Kind) {
+ case DeclaratorChunk::Pointer:
+ case DeclaratorChunk::Reference:
+ case DeclaratorChunk::BlockPointer:
+ case DeclaratorChunk::MemberPointer:
+ case DeclaratorChunk::Pipe:
+ NeedParens = true;
+ break;
+ case DeclaratorChunk::Array:
+ case DeclaratorChunk::Function:
+ case DeclaratorChunk::Paren:
+ break;
+ }
+ }
+
+ if (NeedParens) {
+ // Create a DeclaratorChunk for the inserted parens.
+ SourceLocation EndLoc = PP.getLocForEndOfToken(D.getEndLoc());
+ D.AddTypeInfo(DeclaratorChunk::getParen(SuggestParenLoc, EndLoc),
+ SourceLocation());
+ }
+
+ // Adding back the bracket info to the end of the Declarator.
+ for (unsigned i = 0, e = TempDeclarator.getNumTypeObjects(); i < e; ++i) {
+ const DeclaratorChunk &Chunk = TempDeclarator.getTypeObject(i);
+ D.AddTypeInfo(Chunk, SourceLocation());
+ }
+
+ // The missing identifier would have been diagnosed in ParseDirectDeclarator.
+ // If parentheses are required, always suggest them.
+ if (!D.getIdentifier() && !NeedParens)
+ return;
+
+ SourceLocation EndBracketLoc = TempDeclarator.getEndLoc();
+
+ // Generate the move bracket error message.
+ SourceRange BracketRange(StartBracketLoc, EndBracketLoc);
+ SourceLocation EndLoc = PP.getLocForEndOfToken(D.getEndLoc());
+
+ if (NeedParens) {
+ Diag(EndLoc, diag::err_brackets_go_after_unqualified_id)
+ << getLangOpts().CPlusPlus
+ << FixItHint::CreateInsertion(SuggestParenLoc, "(")
+ << FixItHint::CreateInsertion(EndLoc, ")")
+ << FixItHint::CreateInsertionFromRange(
+ EndLoc, CharSourceRange(BracketRange, true))
+ << FixItHint::CreateRemoval(BracketRange);
+ } else {
+ Diag(EndLoc, diag::err_brackets_go_after_unqualified_id)
+ << getLangOpts().CPlusPlus
+ << FixItHint::CreateInsertionFromRange(
+ EndLoc, CharSourceRange(BracketRange, true))
+ << FixItHint::CreateRemoval(BracketRange);
+ }
+}
+
+/// [GNU] typeof-specifier:
+/// typeof ( expressions )
+/// typeof ( type-name )
+/// [GNU/C++] typeof unary-expression
+/// [C2x] typeof-specifier:
+/// typeof '(' typeof-specifier-argument ')'
+/// typeof_unqual '(' typeof-specifier-argument ')'
+///
+/// typeof-specifier-argument:
+/// expression
+/// type-name
+///
+void Parser::ParseTypeofSpecifier(DeclSpec &DS) {
+ assert(Tok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual) &&
+ "Not a typeof specifier");
+
+ bool IsUnqual = Tok.is(tok::kw_typeof_unqual);
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (getLangOpts().C2x && !II->getName().startswith("__"))
+ Diag(Tok.getLocation(), diag::warn_c2x_compat_typeof_type_specifier)
+ << IsUnqual;
+
+ Token OpTok = Tok;
+ SourceLocation StartLoc = ConsumeToken();
+ bool HasParens = Tok.is(tok::l_paren);
+
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated,
+ Sema::ReuseLambdaContextDecl);
+
+ bool isCastExpr;
+ ParsedType CastTy;
+ SourceRange CastRange;
+ ExprResult Operand = Actions.CorrectDelayedTyposInExpr(
+ ParseExprAfterUnaryExprOrTypeTrait(OpTok, isCastExpr, CastTy, CastRange));
+ if (HasParens)
+ DS.setTypeArgumentRange(CastRange);
+
+ if (CastRange.getEnd().isInvalid())
+ // FIXME: Not accurate, the range gets one token more than it should.
+ DS.SetRangeEnd(Tok.getLocation());
+ else
+ DS.SetRangeEnd(CastRange.getEnd());
+
+ if (isCastExpr) {
+ if (!CastTy) {
+ DS.SetTypeSpecError();
+ return;
+ }
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ // Check for duplicate type specifiers (e.g. "int typeof(int)").
+ if (DS.SetTypeSpecType(IsUnqual ? DeclSpec::TST_typeof_unqualType
+ : DeclSpec::TST_typeofType,
+ StartLoc, PrevSpec,
+ DiagID, CastTy,
+ Actions.getASTContext().getPrintingPolicy()))
+ Diag(StartLoc, DiagID) << PrevSpec;
+ return;
+ }
+
+ // If we get here, the operand to the typeof was an expression.
+ if (Operand.isInvalid()) {
+ DS.SetTypeSpecError();
+ return;
+ }
+
+ // We might need to transform the operand if it is potentially evaluated.
+ Operand = Actions.HandleExprEvaluationContextForTypeof(Operand.get());
+ if (Operand.isInvalid()) {
+ DS.SetTypeSpecError();
+ return;
+ }
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ // Check for duplicate type specifiers (e.g. "int typeof(int)").
+ if (DS.SetTypeSpecType(IsUnqual ? DeclSpec::TST_typeof_unqualExpr
+ : DeclSpec::TST_typeofExpr,
+ StartLoc, PrevSpec,
+ DiagID, Operand.get(),
+ Actions.getASTContext().getPrintingPolicy()))
+ Diag(StartLoc, DiagID) << PrevSpec;
+}
+
+/// [C11] atomic-specifier:
+/// _Atomic ( type-name )
+///
+void Parser::ParseAtomicSpecifier(DeclSpec &DS) {
+ assert(Tok.is(tok::kw__Atomic) && NextToken().is(tok::l_paren) &&
+ "Not an atomic specifier");
+
+ SourceLocation StartLoc = ConsumeToken();
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen())
+ return;
+
+ TypeResult Result = ParseTypeName();
+ if (Result.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ // Match the ')'
+ T.consumeClose();
+
+ if (T.getCloseLocation().isInvalid())
+ return;
+
+ DS.setTypeArgumentRange(T.getRange());
+ DS.SetRangeEnd(T.getCloseLocation());
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ if (DS.SetTypeSpecType(DeclSpec::TST_atomic, StartLoc, PrevSpec,
+ DiagID, Result.get(),
+ Actions.getASTContext().getPrintingPolicy()))
+ Diag(StartLoc, DiagID) << PrevSpec;
+}
+
+/// TryAltiVecVectorTokenOutOfLine - Out of line body that should only be called
+/// from TryAltiVecVectorToken.
+bool Parser::TryAltiVecVectorTokenOutOfLine() {
+ Token Next = NextToken();
+ switch (Next.getKind()) {
+ default: return false;
+ case tok::kw_short:
+ case tok::kw_long:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_void:
+ case tok::kw_char:
+ case tok::kw_int:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw_bool:
+ case tok::kw__Bool:
+ case tok::kw___bool:
+ case tok::kw___pixel:
+ Tok.setKind(tok::kw___vector);
+ return true;
+ case tok::identifier:
+ if (Next.getIdentifierInfo() == Ident_pixel) {
+ Tok.setKind(tok::kw___vector);
+ return true;
+ }
+ if (Next.getIdentifierInfo() == Ident_bool ||
+ Next.getIdentifierInfo() == Ident_Bool) {
+ Tok.setKind(tok::kw___vector);
+ return true;
+ }
+ return false;
+ }
+}
+
+bool Parser::TryAltiVecTokenOutOfLine(DeclSpec &DS, SourceLocation Loc,
+ const char *&PrevSpec, unsigned &DiagID,
+ bool &isInvalid) {
+ const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
+ if (Tok.getIdentifierInfo() == Ident_vector) {
+ Token Next = NextToken();
+ switch (Next.getKind()) {
+ case tok::kw_short:
+ case tok::kw_long:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_void:
+ case tok::kw_char:
+ case tok::kw_int:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw_bool:
+ case tok::kw__Bool:
+ case tok::kw___bool:
+ case tok::kw___pixel:
+ isInvalid = DS.SetTypeAltiVecVector(true, Loc, PrevSpec, DiagID, Policy);
+ return true;
+ case tok::identifier:
+ if (Next.getIdentifierInfo() == Ident_pixel) {
+ isInvalid = DS.SetTypeAltiVecVector(true, Loc, PrevSpec, DiagID,Policy);
+ return true;
+ }
+ if (Next.getIdentifierInfo() == Ident_bool ||
+ Next.getIdentifierInfo() == Ident_Bool) {
+ isInvalid =
+ DS.SetTypeAltiVecVector(true, Loc, PrevSpec, DiagID, Policy);
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ } else if ((Tok.getIdentifierInfo() == Ident_pixel) &&
+ DS.isTypeAltiVecVector()) {
+ isInvalid = DS.SetTypeAltiVecPixel(true, Loc, PrevSpec, DiagID, Policy);
+ return true;
+ } else if ((Tok.getIdentifierInfo() == Ident_bool) &&
+ DS.isTypeAltiVecVector()) {
+ isInvalid = DS.SetTypeAltiVecBool(true, Loc, PrevSpec, DiagID, Policy);
+ return true;
+ }
+ return false;
+}
+
+void Parser::DiagnoseBitIntUse(const Token &Tok) {
+ // If the token is for _ExtInt, diagnose it as being deprecated. Otherwise,
+ // the token is about _BitInt and gets (potentially) diagnosed as use of an
+ // extension.
+ assert(Tok.isOneOf(tok::kw__ExtInt, tok::kw__BitInt) &&
+ "expected either an _ExtInt or _BitInt token!");
+
+ SourceLocation Loc = Tok.getLocation();
+ if (Tok.is(tok::kw__ExtInt)) {
+ Diag(Loc, diag::warn_ext_int_deprecated)
+ << FixItHint::CreateReplacement(Loc, "_BitInt");
+ } else {
+ // In C2x mode, diagnose that the use is not compatible with pre-C2x modes.
+ // Otherwise, diagnose that the use is a Clang extension.
+ if (getLangOpts().C2x)
+ Diag(Loc, diag::warn_c17_compat_bit_int);
+ else
+ Diag(Loc, diag::ext_bit_int) << getLangOpts().CPlusPlus;
+ }
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseDeclCXX.cpp b/contrib/libs/clang16/lib/Parse/ParseDeclCXX.cpp
new file mode 100644
index 0000000000..227c1df2bd
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseDeclCXX.cpp
@@ -0,0 +1,4850 @@
+//===--- ParseDeclCXX.cpp - C++ Declaration Parsing -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the C++ Declaration portions of the Parser interfaces.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/PrettyDeclStackTrace.h"
+#include "clang/Basic/AttributeCommonInfo.h"
+#include "clang/Basic/Attributes.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/ParsedTemplate.h"
+#include "clang/Sema/Scope.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/TimeProfiler.h"
+#include <optional>
+
+using namespace clang;
+
+/// ParseNamespace - We know that the current token is a namespace keyword. This
+/// may either be a top level namespace or a block-level namespace alias. If
+/// there was an inline keyword, it has already been parsed.
+///
+/// namespace-definition: [C++: namespace.def]
+/// named-namespace-definition
+/// unnamed-namespace-definition
+/// nested-namespace-definition
+///
+/// named-namespace-definition:
+/// 'inline'[opt] 'namespace' attributes[opt] identifier '{'
+/// namespace-body '}'
+///
+/// unnamed-namespace-definition:
+/// 'inline'[opt] 'namespace' attributes[opt] '{' namespace-body '}'
+///
+/// nested-namespace-definition:
+/// 'namespace' enclosing-namespace-specifier '::' 'inline'[opt]
+/// identifier '{' namespace-body '}'
+///
+/// enclosing-namespace-specifier:
+/// identifier
+/// enclosing-namespace-specifier '::' 'inline'[opt] identifier
+///
+/// namespace-alias-definition: [C++ 7.3.2: namespace.alias]
+/// 'namespace' identifier '=' qualified-namespace-specifier ';'
+///
+Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
+ SourceLocation &DeclEnd,
+ SourceLocation InlineLoc) {
+ assert(Tok.is(tok::kw_namespace) && "Not a namespace!");
+ SourceLocation NamespaceLoc = ConsumeToken(); // eat the 'namespace'.
+ ObjCDeclContextSwitch ObjCDC(*this);
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteNamespaceDecl(getCurScope());
+ return nullptr;
+ }
+
+ SourceLocation IdentLoc;
+ IdentifierInfo *Ident = nullptr;
+ InnerNamespaceInfoList ExtraNSs;
+ SourceLocation FirstNestedInlineLoc;
+
+ ParsedAttributes attrs(AttrFactory);
+
+ auto ReadAttributes = [&] {
+ bool MoreToParse;
+ do {
+ MoreToParse = false;
+ if (Tok.is(tok::kw___attribute)) {
+ ParseGNUAttributes(attrs);
+ MoreToParse = true;
+ }
+ if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
+ ? diag::warn_cxx14_compat_ns_enum_attribute
+ : diag::ext_ns_enum_attribute)
+ << 0 /*namespace*/;
+ ParseCXX11Attributes(attrs);
+ MoreToParse = true;
+ }
+ } while (MoreToParse);
+ };
+
+ ReadAttributes();
+
+ if (Tok.is(tok::identifier)) {
+ Ident = Tok.getIdentifierInfo();
+ IdentLoc = ConsumeToken(); // eat the identifier.
+ while (Tok.is(tok::coloncolon) &&
+ (NextToken().is(tok::identifier) ||
+ (NextToken().is(tok::kw_inline) &&
+ GetLookAheadToken(2).is(tok::identifier)))) {
+
+ InnerNamespaceInfo Info;
+ Info.NamespaceLoc = ConsumeToken();
+
+ if (Tok.is(tok::kw_inline)) {
+ Info.InlineLoc = ConsumeToken();
+ if (FirstNestedInlineLoc.isInvalid())
+ FirstNestedInlineLoc = Info.InlineLoc;
+ }
+
+ Info.Ident = Tok.getIdentifierInfo();
+ Info.IdentLoc = ConsumeToken();
+
+ ExtraNSs.push_back(Info);
+ }
+ }
+
+ ReadAttributes();
+
+ SourceLocation attrLoc = attrs.Range.getBegin();
+
+ // A nested namespace definition cannot have attributes.
+ if (!ExtraNSs.empty() && attrLoc.isValid())
+ Diag(attrLoc, diag::err_unexpected_nested_namespace_attribute);
+
+ if (Tok.is(tok::equal)) {
+ if (!Ident) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ // Skip to end of the definition and eat the ';'.
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+ if (attrLoc.isValid())
+ Diag(attrLoc, diag::err_unexpected_namespace_attributes_alias);
+ if (InlineLoc.isValid())
+ Diag(InlineLoc, diag::err_inline_namespace_alias)
+ << FixItHint::CreateRemoval(InlineLoc);
+ Decl *NSAlias = ParseNamespaceAlias(NamespaceLoc, IdentLoc, Ident, DeclEnd);
+ return Actions.ConvertDeclToDeclGroup(NSAlias);
+ }
+
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ if (T.consumeOpen()) {
+ if (Ident)
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ else
+ Diag(Tok, diag::err_expected_either) << tok::identifier << tok::l_brace;
+ return nullptr;
+ }
+
+ if (getCurScope()->isClassScope() || getCurScope()->isTemplateParamScope() ||
+ getCurScope()->isInObjcMethodScope() || getCurScope()->getBlockParent() ||
+ getCurScope()->getFnParent()) {
+ Diag(T.getOpenLocation(), diag::err_namespace_nonnamespace_scope);
+ SkipUntil(tok::r_brace);
+ return nullptr;
+ }
+
+ if (ExtraNSs.empty()) {
+ // Normal namespace definition, not a nested-namespace-definition.
+ } else if (InlineLoc.isValid()) {
+ Diag(InlineLoc, diag::err_inline_nested_namespace_definition);
+ } else if (getLangOpts().CPlusPlus20) {
+ Diag(ExtraNSs[0].NamespaceLoc,
+ diag::warn_cxx14_compat_nested_namespace_definition);
+ if (FirstNestedInlineLoc.isValid())
+ Diag(FirstNestedInlineLoc,
+ diag::warn_cxx17_compat_inline_nested_namespace_definition);
+ } else if (getLangOpts().CPlusPlus17) {
+ Diag(ExtraNSs[0].NamespaceLoc,
+ diag::warn_cxx14_compat_nested_namespace_definition);
+ if (FirstNestedInlineLoc.isValid())
+ Diag(FirstNestedInlineLoc, diag::ext_inline_nested_namespace_definition);
+ } else {
+ TentativeParsingAction TPA(*this);
+ SkipUntil(tok::r_brace, StopBeforeMatch);
+ Token rBraceToken = Tok;
+ TPA.Revert();
+
+ if (!rBraceToken.is(tok::r_brace)) {
+ Diag(ExtraNSs[0].NamespaceLoc, diag::ext_nested_namespace_definition)
+ << SourceRange(ExtraNSs.front().NamespaceLoc,
+ ExtraNSs.back().IdentLoc);
+ } else {
+ std::string NamespaceFix;
+ for (const auto &ExtraNS : ExtraNSs) {
+ NamespaceFix += " { ";
+ if (ExtraNS.InlineLoc.isValid())
+ NamespaceFix += "inline ";
+ NamespaceFix += "namespace ";
+ NamespaceFix += ExtraNS.Ident->getName();
+ }
+
+ std::string RBraces;
+ for (unsigned i = 0, e = ExtraNSs.size(); i != e; ++i)
+ RBraces += "} ";
+
+ Diag(ExtraNSs[0].NamespaceLoc, diag::ext_nested_namespace_definition)
+ << FixItHint::CreateReplacement(
+ SourceRange(ExtraNSs.front().NamespaceLoc,
+ ExtraNSs.back().IdentLoc),
+ NamespaceFix)
+ << FixItHint::CreateInsertion(rBraceToken.getLocation(), RBraces);
+ }
+
+ // Warn about nested inline namespaces.
+ if (FirstNestedInlineLoc.isValid())
+ Diag(FirstNestedInlineLoc, diag::ext_inline_nested_namespace_definition);
+ }
+
+ // If we're still good, complain about inline namespaces in non-C++0x now.
+ if (InlineLoc.isValid())
+ Diag(InlineLoc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_inline_namespace
+ : diag::ext_inline_namespace);
+
+ // Enter a scope for the namespace.
+ ParseScope NamespaceScope(this, Scope::DeclScope);
+
+ UsingDirectiveDecl *ImplicitUsingDirectiveDecl = nullptr;
+ Decl *NamespcDecl = Actions.ActOnStartNamespaceDef(
+ getCurScope(), InlineLoc, NamespaceLoc, IdentLoc, Ident,
+ T.getOpenLocation(), attrs, ImplicitUsingDirectiveDecl, false);
+
+ PrettyDeclStackTraceEntry CrashInfo(Actions.Context, NamespcDecl,
+ NamespaceLoc, "parsing namespace");
+
+ // Parse the contents of the namespace. This includes parsing recovery on
+ // any improperly nested namespaces.
+ ParseInnerNamespace(ExtraNSs, 0, InlineLoc, attrs, T);
+
+ // Leave the namespace scope.
+ NamespaceScope.Exit();
+
+ DeclEnd = T.getCloseLocation();
+ Actions.ActOnFinishNamespaceDef(NamespcDecl, DeclEnd);
+
+ return Actions.ConvertDeclToDeclGroup(NamespcDecl,
+ ImplicitUsingDirectiveDecl);
+}
+
+/// ParseInnerNamespace - Parse the contents of a namespace.
+void Parser::ParseInnerNamespace(const InnerNamespaceInfoList &InnerNSs,
+ unsigned int index, SourceLocation &InlineLoc,
+ ParsedAttributes &attrs,
+ BalancedDelimiterTracker &Tracker) {
+ if (index == InnerNSs.size()) {
+ while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
+ Tok.isNot(tok::eof)) {
+ ParsedAttributes DeclAttrs(AttrFactory);
+ MaybeParseCXX11Attributes(DeclAttrs);
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+ ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
+ }
+
+ // The caller is what called check -- we are simply calling
+ // the close for it.
+ Tracker.consumeClose();
+
+ return;
+ }
+
+ // Handle a nested namespace definition.
+ // FIXME: Preserve the source information through to the AST rather than
+ // desugaring it here.
+ ParseScope NamespaceScope(this, Scope::DeclScope);
+ UsingDirectiveDecl *ImplicitUsingDirectiveDecl = nullptr;
+ Decl *NamespcDecl = Actions.ActOnStartNamespaceDef(
+ getCurScope(), InnerNSs[index].InlineLoc, InnerNSs[index].NamespaceLoc,
+ InnerNSs[index].IdentLoc, InnerNSs[index].Ident,
+ Tracker.getOpenLocation(), attrs, ImplicitUsingDirectiveDecl, true);
+ assert(!ImplicitUsingDirectiveDecl &&
+ "nested namespace definition cannot define anonymous namespace");
+
+ ParseInnerNamespace(InnerNSs, ++index, InlineLoc, attrs, Tracker);
+
+ NamespaceScope.Exit();
+ Actions.ActOnFinishNamespaceDef(NamespcDecl, Tracker.getCloseLocation());
+}
+
+/// ParseNamespaceAlias - Parse the part after the '=' in a namespace
+/// alias definition.
+///
+Decl *Parser::ParseNamespaceAlias(SourceLocation NamespaceLoc,
+ SourceLocation AliasLoc,
+ IdentifierInfo *Alias,
+ SourceLocation &DeclEnd) {
+ assert(Tok.is(tok::equal) && "Not equal token");
+
+ ConsumeToken(); // eat the '='.
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteNamespaceAliasDecl(getCurScope());
+ return nullptr;
+ }
+
+ CXXScopeSpec SS;
+ // Parse (optional) nested-name-specifier.
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false,
+ /*MayBePseudoDestructor=*/nullptr,
+ /*IsTypename=*/false,
+ /*LastII=*/nullptr,
+ /*OnlyNamespace=*/true);
+
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected_namespace_name);
+ // Skip to end of the definition and eat the ';'.
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ if (SS.isInvalid()) {
+ // Diagnostics have been emitted in ParseOptionalCXXScopeSpecifier.
+ // Skip to end of the definition and eat the ';'.
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ // Parse identifier.
+ IdentifierInfo *Ident = Tok.getIdentifierInfo();
+ SourceLocation IdentLoc = ConsumeToken();
+
+ // Eat the ';'.
+ DeclEnd = Tok.getLocation();
+ if (ExpectAndConsume(tok::semi, diag::err_expected_semi_after_namespace_name))
+ SkipUntil(tok::semi);
+
+ return Actions.ActOnNamespaceAliasDef(getCurScope(), NamespaceLoc, AliasLoc,
+ Alias, SS, IdentLoc, Ident);
+}
+
+/// ParseLinkage - We know that the current token is a string_literal
+/// and just before that, that extern was seen.
+///
+/// linkage-specification: [C++ 7.5p2: dcl.link]
+/// 'extern' string-literal '{' declaration-seq[opt] '}'
+/// 'extern' string-literal declaration
+///
+Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
+ assert(isTokenStringLiteral() && "Not a string literal!");
+ ExprResult Lang = ParseStringLiteralExpression(false);
+
+ ParseScope LinkageScope(this, Scope::DeclScope);
+ Decl *LinkageSpec =
+ Lang.isInvalid()
+ ? nullptr
+ : Actions.ActOnStartLinkageSpecification(
+ getCurScope(), DS.getSourceRange().getBegin(), Lang.get(),
+ Tok.is(tok::l_brace) ? Tok.getLocation() : SourceLocation());
+
+ ParsedAttributes DeclAttrs(AttrFactory);
+ ParsedAttributes DeclSpecAttrs(AttrFactory);
+
+ while (MaybeParseCXX11Attributes(DeclAttrs) ||
+ MaybeParseGNUAttributes(DeclSpecAttrs))
+ ;
+
+ if (Tok.isNot(tok::l_brace)) {
+ // Reset the source range in DS, as the leading "extern"
+ // does not really belong to the inner declaration ...
+ DS.SetRangeStart(SourceLocation());
+ DS.SetRangeEnd(SourceLocation());
+ // ... but anyway remember that such an "extern" was seen.
+ DS.setExternInLinkageSpec(true);
+ ParseExternalDeclaration(DeclAttrs, DeclSpecAttrs, &DS);
+ return LinkageSpec ? Actions.ActOnFinishLinkageSpecification(
+ getCurScope(), LinkageSpec, SourceLocation())
+ : nullptr;
+ }
+
+ DS.abort();
+
+ ProhibitAttributes(DeclAttrs);
+
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+
+ unsigned NestedModules = 0;
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::annot_module_begin:
+ ++NestedModules;
+ ParseTopLevelDecl();
+ continue;
+
+ case tok::annot_module_end:
+ if (!NestedModules)
+ break;
+ --NestedModules;
+ ParseTopLevelDecl();
+ continue;
+
+ case tok::annot_module_include:
+ ParseTopLevelDecl();
+ continue;
+
+ case tok::eof:
+ break;
+
+ case tok::r_brace:
+ if (!NestedModules)
+ break;
+ [[fallthrough]];
+ default:
+ ParsedAttributes DeclAttrs(AttrFactory);
+ MaybeParseCXX11Attributes(DeclAttrs);
+ ParseExternalDeclaration(DeclAttrs, DeclSpecAttrs);
+ continue;
+ }
+
+ break;
+ }
+
+ T.consumeClose();
+ return LinkageSpec ? Actions.ActOnFinishLinkageSpecification(
+ getCurScope(), LinkageSpec, T.getCloseLocation())
+ : nullptr;
+}
+
+/// Parse a C++ Modules TS export-declaration.
+///
+/// export-declaration:
+/// 'export' declaration
+/// 'export' '{' declaration-seq[opt] '}'
+///
+Decl *Parser::ParseExportDeclaration() {
+ assert(Tok.is(tok::kw_export));
+ SourceLocation ExportLoc = ConsumeToken();
+
+ ParseScope ExportScope(this, Scope::DeclScope);
+ Decl *ExportDecl = Actions.ActOnStartExportDecl(
+ getCurScope(), ExportLoc,
+ Tok.is(tok::l_brace) ? Tok.getLocation() : SourceLocation());
+
+ if (Tok.isNot(tok::l_brace)) {
+ // FIXME: Factor out a ParseExternalDeclarationWithAttrs.
+ ParsedAttributes DeclAttrs(AttrFactory);
+ MaybeParseCXX11Attributes(DeclAttrs);
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+ ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
+ return Actions.ActOnFinishExportDecl(getCurScope(), ExportDecl,
+ SourceLocation());
+ }
+
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+
+ // The Modules TS draft says "An export-declaration shall declare at least one
+ // entity", but the intent is that it shall contain at least one declaration.
+ if (Tok.is(tok::r_brace) && getLangOpts().ModulesTS) {
+ Diag(ExportLoc, diag::err_export_empty)
+ << SourceRange(ExportLoc, Tok.getLocation());
+ }
+
+ while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
+ Tok.isNot(tok::eof)) {
+ ParsedAttributes DeclAttrs(AttrFactory);
+ MaybeParseCXX11Attributes(DeclAttrs);
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+ ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
+ }
+
+ T.consumeClose();
+ return Actions.ActOnFinishExportDecl(getCurScope(), ExportDecl,
+ T.getCloseLocation());
+}
+
+/// ParseUsingDirectiveOrDeclaration - Parse C++ using using-declaration or
+/// using-directive. Assumes that current token is 'using'.
+Parser::DeclGroupPtrTy Parser::ParseUsingDirectiveOrDeclaration(
+ DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
+ SourceLocation &DeclEnd, ParsedAttributes &Attrs) {
+ assert(Tok.is(tok::kw_using) && "Not using token");
+ ObjCDeclContextSwitch ObjCDC(*this);
+
+ // Eat 'using'.
+ SourceLocation UsingLoc = ConsumeToken();
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteUsing(getCurScope());
+ return nullptr;
+ }
+
+ // Consume unexpected 'template' keywords.
+ while (Tok.is(tok::kw_template)) {
+ SourceLocation TemplateLoc = ConsumeToken();
+ Diag(TemplateLoc, diag::err_unexpected_template_after_using)
+ << FixItHint::CreateRemoval(TemplateLoc);
+ }
+
+ // 'using namespace' means this is a using-directive.
+ if (Tok.is(tok::kw_namespace)) {
+ // Template parameters are always an error here.
+ if (TemplateInfo.Kind) {
+ SourceRange R = TemplateInfo.getSourceRange();
+ Diag(UsingLoc, diag::err_templated_using_directive_declaration)
+ << 0 /* directive */ << R << FixItHint::CreateRemoval(R);
+ }
+
+ Decl *UsingDir = ParseUsingDirective(Context, UsingLoc, DeclEnd, Attrs);
+ return Actions.ConvertDeclToDeclGroup(UsingDir);
+ }
+
+ // Otherwise, it must be a using-declaration or an alias-declaration.
+ return ParseUsingDeclaration(Context, TemplateInfo, UsingLoc, DeclEnd, Attrs,
+ AS_none);
+}
+
+/// ParseUsingDirective - Parse C++ using-directive, assumes
+/// that current token is 'namespace' and 'using' was already parsed.
+///
+/// using-directive: [C++ 7.3.p4: namespace.udir]
+/// 'using' 'namespace' ::[opt] nested-name-specifier[opt]
+/// namespace-name ;
+/// [GNU] using-directive:
+/// 'using' 'namespace' ::[opt] nested-name-specifier[opt]
+/// namespace-name attributes[opt] ;
+///
+Decl *Parser::ParseUsingDirective(DeclaratorContext Context,
+ SourceLocation UsingLoc,
+ SourceLocation &DeclEnd,
+ ParsedAttributes &attrs) {
+ assert(Tok.is(tok::kw_namespace) && "Not 'namespace' token");
+
+ // Eat 'namespace'.
+ SourceLocation NamespcLoc = ConsumeToken();
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteUsingDirective(getCurScope());
+ return nullptr;
+ }
+
+ CXXScopeSpec SS;
+ // Parse (optional) nested-name-specifier.
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false,
+ /*MayBePseudoDestructor=*/nullptr,
+ /*IsTypename=*/false,
+ /*LastII=*/nullptr,
+ /*OnlyNamespace=*/true);
+
+ IdentifierInfo *NamespcName = nullptr;
+ SourceLocation IdentLoc = SourceLocation();
+
+ // Parse namespace-name.
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected_namespace_name);
+ // If there was invalid namespace name, skip to end of decl, and eat ';'.
+ SkipUntil(tok::semi);
+ // FIXME: Are there cases, when we would like to call ActOnUsingDirective?
+ return nullptr;
+ }
+
+ if (SS.isInvalid()) {
+ // Diagnostics have been emitted in ParseOptionalCXXScopeSpecifier.
+ // Skip to end of the definition and eat the ';'.
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ // Parse identifier.
+ NamespcName = Tok.getIdentifierInfo();
+ IdentLoc = ConsumeToken();
+
+ // Parse (optional) attributes (most likely GNU strong-using extension).
+ bool GNUAttr = false;
+ if (Tok.is(tok::kw___attribute)) {
+ GNUAttr = true;
+ ParseGNUAttributes(attrs);
+ }
+
+ // Eat ';'.
+ DeclEnd = Tok.getLocation();
+ if (ExpectAndConsume(tok::semi,
+ GNUAttr ? diag::err_expected_semi_after_attribute_list
+ : diag::err_expected_semi_after_namespace_name))
+ SkipUntil(tok::semi);
+
+ return Actions.ActOnUsingDirective(getCurScope(), UsingLoc, NamespcLoc, SS,
+ IdentLoc, NamespcName, attrs);
+}
+
+/// Parse a using-declarator (or the identifier in a C++11 alias-declaration).
+///
+/// using-declarator:
+/// 'typename'[opt] nested-name-specifier unqualified-id
+///
+bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
+ UsingDeclarator &D) {
+ D.clear();
+
+ // Ignore optional 'typename'.
+ // FIXME: This is wrong; we should parse this as a typename-specifier.
+ TryConsumeToken(tok::kw_typename, D.TypenameLoc);
+
+ if (Tok.is(tok::kw___super)) {
+ Diag(Tok.getLocation(), diag::err_super_in_using_declaration);
+ return true;
+ }
+
+ // Parse nested-name-specifier.
+ IdentifierInfo *LastII = nullptr;
+ if (ParseOptionalCXXScopeSpecifier(D.SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false,
+ /*MayBePseudoDtor=*/nullptr,
+ /*IsTypename=*/false,
+ /*LastII=*/&LastII,
+ /*OnlyNamespace=*/false,
+ /*InUsingDeclaration=*/true))
+
+ return true;
+ if (D.SS.isInvalid())
+ return true;
+
+ // Parse the unqualified-id. We allow parsing of both constructor and
+ // destructor names and allow the action module to diagnose any semantic
+ // errors.
+ //
+ // C++11 [class.qual]p2:
+ // [...] in a using-declaration that is a member-declaration, if the name
+ // specified after the nested-name-specifier is the same as the identifier
+ // or the simple-template-id's template-name in the last component of the
+ // nested-name-specifier, the name is [...] considered to name the
+ // constructor.
+ if (getLangOpts().CPlusPlus11 && Context == DeclaratorContext::Member &&
+ Tok.is(tok::identifier) &&
+ (NextToken().is(tok::semi) || NextToken().is(tok::comma) ||
+ NextToken().is(tok::ellipsis) || NextToken().is(tok::l_square) ||
+ NextToken().is(tok::kw___attribute)) &&
+ D.SS.isNotEmpty() && LastII == Tok.getIdentifierInfo() &&
+ !D.SS.getScopeRep()->getAsNamespace() &&
+ !D.SS.getScopeRep()->getAsNamespaceAlias()) {
+ SourceLocation IdLoc = ConsumeToken();
+ ParsedType Type =
+ Actions.getInheritingConstructorName(D.SS, IdLoc, *LastII);
+ D.Name.setConstructorName(Type, IdLoc, IdLoc);
+ } else {
+ if (ParseUnqualifiedId(
+ D.SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext=*/false,
+ /*AllowDestructorName=*/true,
+ /*AllowConstructorName=*/
+ !(Tok.is(tok::identifier) && NextToken().is(tok::equal)),
+ /*AllowDeductionGuide=*/false, nullptr, D.Name))
+ return true;
+ }
+
+ if (TryConsumeToken(tok::ellipsis, D.EllipsisLoc))
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
+ ? diag::warn_cxx17_compat_using_declaration_pack
+ : diag::ext_using_declaration_pack);
+
+ return false;
+}
+
+/// ParseUsingDeclaration - Parse C++ using-declaration or alias-declaration.
+/// Assumes that 'using' was already seen.
+///
+/// using-declaration: [C++ 7.3.p3: namespace.udecl]
+/// 'using' using-declarator-list[opt] ;
+///
+/// using-declarator-list: [C++1z]
+/// using-declarator '...'[opt]
+/// using-declarator-list ',' using-declarator '...'[opt]
+///
+/// using-declarator-list: [C++98-14]
+/// using-declarator
+///
+/// alias-declaration: C++11 [dcl.dcl]p1
+/// 'using' identifier attribute-specifier-seq[opt] = type-id ;
+///
+/// using-enum-declaration: [C++20, dcl.enum]
+/// 'using' elaborated-enum-specifier ;
+/// The terminal name of the elaborated-enum-specifier undergoes
+/// ordinary lookup
+///
+/// elaborated-enum-specifier:
+/// 'enum' nested-name-specifier[opt] identifier
+Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
+ DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
+ SourceLocation UsingLoc, SourceLocation &DeclEnd,
+ ParsedAttributes &PrefixAttrs, AccessSpecifier AS) {
+ SourceLocation UELoc;
+ bool InInitStatement = Context == DeclaratorContext::SelectionInit ||
+ Context == DeclaratorContext::ForInit;
+
+ if (TryConsumeToken(tok::kw_enum, UELoc) && !InInitStatement) {
+ // C++20 using-enum
+ Diag(UELoc, getLangOpts().CPlusPlus20
+ ? diag::warn_cxx17_compat_using_enum_declaration
+ : diag::ext_using_enum_declaration);
+
+ DiagnoseCXX11AttributeExtension(PrefixAttrs);
+
+ if (TemplateInfo.Kind) {
+ SourceRange R = TemplateInfo.getSourceRange();
+ Diag(UsingLoc, diag::err_templated_using_directive_declaration)
+ << 1 /* declaration */ << R << FixItHint::CreateRemoval(R);
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+ CXXScopeSpec SS;
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ParsedType=*/nullptr,
+ /*ObectHasErrors=*/false,
+ /*EnteringConttext=*/false,
+ /*MayBePseudoDestructor=*/nullptr,
+ /*IsTypename=*/false,
+ /*IdentifierInfo=*/nullptr,
+ /*OnlyNamespace=*/false,
+ /*InUsingDeclaration=*/true)) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteUsing(getCurScope());
+ return nullptr;
+ }
+
+ if (!Tok.is(tok::identifier)) {
+ Diag(Tok.getLocation(), diag::err_using_enum_expect_identifier)
+ << Tok.is(tok::kw_enum);
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+ IdentifierInfo *IdentInfo = Tok.getIdentifierInfo();
+ SourceLocation IdentLoc = ConsumeToken();
+ Decl *UED = Actions.ActOnUsingEnumDeclaration(
+ getCurScope(), AS, UsingLoc, UELoc, IdentLoc, *IdentInfo, &SS);
+ if (!UED) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ DeclEnd = Tok.getLocation();
+ if (ExpectAndConsume(tok::semi, diag::err_expected_after,
+ "using-enum declaration"))
+ SkipUntil(tok::semi);
+
+ return Actions.ConvertDeclToDeclGroup(UED);
+ }
+
+ // Check for misplaced attributes before the identifier in an
+ // alias-declaration.
+ ParsedAttributes MisplacedAttrs(AttrFactory);
+ MaybeParseCXX11Attributes(MisplacedAttrs);
+
+ if (InInitStatement && Tok.isNot(tok::identifier))
+ return nullptr;
+
+ UsingDeclarator D;
+ bool InvalidDeclarator = ParseUsingDeclarator(Context, D);
+
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, Attrs);
+
+ // If we had any misplaced attributes from earlier, this is where they
+ // should have been written.
+ if (MisplacedAttrs.Range.isValid()) {
+ Diag(MisplacedAttrs.Range.getBegin(), diag::err_attributes_not_allowed)
+ << FixItHint::CreateInsertionFromRange(
+ Tok.getLocation(),
+ CharSourceRange::getTokenRange(MisplacedAttrs.Range))
+ << FixItHint::CreateRemoval(MisplacedAttrs.Range);
+ Attrs.takeAllFrom(MisplacedAttrs);
+ }
+
+ // Maybe this is an alias-declaration.
+ if (Tok.is(tok::equal) || InInitStatement) {
+ if (InvalidDeclarator) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ ProhibitAttributes(PrefixAttrs);
+
+ Decl *DeclFromDeclSpec = nullptr;
+ Decl *AD = ParseAliasDeclarationAfterDeclarator(
+ TemplateInfo, UsingLoc, D, DeclEnd, AS, Attrs, &DeclFromDeclSpec);
+ return Actions.ConvertDeclToDeclGroup(AD, DeclFromDeclSpec);
+ }
+
+ DiagnoseCXX11AttributeExtension(PrefixAttrs);
+
+ // Diagnose an attempt to declare a templated using-declaration.
+ // In C++11, alias-declarations can be templates:
+ // template <...> using id = type;
+ if (TemplateInfo.Kind) {
+ SourceRange R = TemplateInfo.getSourceRange();
+ Diag(UsingLoc, diag::err_templated_using_directive_declaration)
+ << 1 /* declaration */ << R << FixItHint::CreateRemoval(R);
+
+ // Unfortunately, we have to bail out instead of recovering by
+ // ignoring the parameters, just in case the nested name specifier
+ // depends on the parameters.
+ return nullptr;
+ }
+
+ SmallVector<Decl *, 8> DeclsInGroup;
+ while (true) {
+ // Parse (optional) attributes.
+ MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, Attrs);
+ DiagnoseCXX11AttributeExtension(Attrs);
+ Attrs.addAll(PrefixAttrs.begin(), PrefixAttrs.end());
+
+ if (InvalidDeclarator)
+ SkipUntil(tok::comma, tok::semi, StopBeforeMatch);
+ else {
+ // "typename" keyword is allowed for identifiers only,
+ // because it may be a type definition.
+ if (D.TypenameLoc.isValid() &&
+ D.Name.getKind() != UnqualifiedIdKind::IK_Identifier) {
+ Diag(D.Name.getSourceRange().getBegin(),
+ diag::err_typename_identifiers_only)
+ << FixItHint::CreateRemoval(SourceRange(D.TypenameLoc));
+ // Proceed parsing, but discard the typename keyword.
+ D.TypenameLoc = SourceLocation();
+ }
+
+ Decl *UD = Actions.ActOnUsingDeclaration(getCurScope(), AS, UsingLoc,
+ D.TypenameLoc, D.SS, D.Name,
+ D.EllipsisLoc, Attrs);
+ if (UD)
+ DeclsInGroup.push_back(UD);
+ }
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+
+ // Parse another using-declarator.
+ Attrs.clear();
+ InvalidDeclarator = ParseUsingDeclarator(Context, D);
+ }
+
+ if (DeclsInGroup.size() > 1)
+ Diag(Tok.getLocation(),
+ getLangOpts().CPlusPlus17
+ ? diag::warn_cxx17_compat_multi_using_declaration
+ : diag::ext_multi_using_declaration);
+
+ // Eat ';'.
+ DeclEnd = Tok.getLocation();
+ if (ExpectAndConsume(tok::semi, diag::err_expected_after,
+ !Attrs.empty() ? "attributes list"
+ : UELoc.isValid() ? "using-enum declaration"
+ : "using declaration"))
+ SkipUntil(tok::semi);
+
+ return Actions.BuildDeclaratorGroup(DeclsInGroup);
+}
+
+Decl *Parser::ParseAliasDeclarationAfterDeclarator(
+ const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc,
+ UsingDeclarator &D, SourceLocation &DeclEnd, AccessSpecifier AS,
+ ParsedAttributes &Attrs, Decl **OwnedType) {
+ if (ExpectAndConsume(tok::equal)) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_alias_declaration
+ : diag::ext_alias_declaration);
+
+ // Type alias templates cannot be specialized.
+ int SpecKind = -1;
+ if (TemplateInfo.Kind == ParsedTemplateInfo::Template &&
+ D.Name.getKind() == UnqualifiedIdKind::IK_TemplateId)
+ SpecKind = 0;
+ if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization)
+ SpecKind = 1;
+ if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation)
+ SpecKind = 2;
+ if (SpecKind != -1) {
+ SourceRange Range;
+ if (SpecKind == 0)
+ Range = SourceRange(D.Name.TemplateId->LAngleLoc,
+ D.Name.TemplateId->RAngleLoc);
+ else
+ Range = TemplateInfo.getSourceRange();
+ Diag(Range.getBegin(), diag::err_alias_declaration_specialization)
+ << SpecKind << Range;
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ // Name must be an identifier.
+ if (D.Name.getKind() != UnqualifiedIdKind::IK_Identifier) {
+ Diag(D.Name.StartLocation, diag::err_alias_declaration_not_identifier);
+ // No removal fixit: can't recover from this.
+ SkipUntil(tok::semi);
+ return nullptr;
+ } else if (D.TypenameLoc.isValid())
+ Diag(D.TypenameLoc, diag::err_alias_declaration_not_identifier)
+ << FixItHint::CreateRemoval(
+ SourceRange(D.TypenameLoc, D.SS.isNotEmpty() ? D.SS.getEndLoc()
+ : D.TypenameLoc));
+ else if (D.SS.isNotEmpty())
+ Diag(D.SS.getBeginLoc(), diag::err_alias_declaration_not_identifier)
+ << FixItHint::CreateRemoval(D.SS.getRange());
+ if (D.EllipsisLoc.isValid())
+ Diag(D.EllipsisLoc, diag::err_alias_declaration_pack_expansion)
+ << FixItHint::CreateRemoval(SourceRange(D.EllipsisLoc));
+
+ Decl *DeclFromDeclSpec = nullptr;
+ TypeResult TypeAlias =
+ ParseTypeName(nullptr,
+ TemplateInfo.Kind ? DeclaratorContext::AliasTemplate
+ : DeclaratorContext::AliasDecl,
+ AS, &DeclFromDeclSpec, &Attrs);
+ if (OwnedType)
+ *OwnedType = DeclFromDeclSpec;
+
+ // Eat ';'.
+ DeclEnd = Tok.getLocation();
+ if (ExpectAndConsume(tok::semi, diag::err_expected_after,
+ !Attrs.empty() ? "attributes list"
+ : "alias declaration"))
+ SkipUntil(tok::semi);
+
+ TemplateParameterLists *TemplateParams = TemplateInfo.TemplateParams;
+ MultiTemplateParamsArg TemplateParamsArg(
+ TemplateParams ? TemplateParams->data() : nullptr,
+ TemplateParams ? TemplateParams->size() : 0);
+ return Actions.ActOnAliasDeclaration(getCurScope(), AS, TemplateParamsArg,
+ UsingLoc, D.Name, Attrs, TypeAlias,
+ DeclFromDeclSpec);
+}
+
+static FixItHint getStaticAssertNoMessageFixIt(const Expr *AssertExpr,
+ SourceLocation EndExprLoc) {
+ if (const auto *BO = dyn_cast_or_null<BinaryOperator>(AssertExpr)) {
+ if (BO->getOpcode() == BO_LAnd &&
+ isa<StringLiteral>(BO->getRHS()->IgnoreImpCasts()))
+ return FixItHint::CreateReplacement(BO->getOperatorLoc(), ",");
+ }
+ return FixItHint::CreateInsertion(EndExprLoc, ", \"\"");
+}
+
+/// ParseStaticAssertDeclaration - Parse C++0x or C11 static_assert-declaration.
+///
+/// [C++0x] static_assert-declaration:
+/// static_assert ( constant-expression , string-literal ) ;
+///
+/// [C11] static_assert-declaration:
+/// _Static_assert ( constant-expression , string-literal ) ;
+///
+Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd) {
+ assert(Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert) &&
+ "Not a static_assert declaration");
+
+ // Save the token used for static assertion.
+ Token SavedTok = Tok;
+
+ if (Tok.is(tok::kw__Static_assert) && !getLangOpts().C11)
+ Diag(Tok, diag::ext_c11_feature) << Tok.getName();
+ if (Tok.is(tok::kw_static_assert)) {
+ if (!getLangOpts().CPlusPlus) {
+ if (!getLangOpts().C2x)
+ Diag(Tok, diag::ext_ms_static_assert) << FixItHint::CreateReplacement(
+ Tok.getLocation(), "_Static_assert");
+ } else
+ Diag(Tok, diag::warn_cxx98_compat_static_assert);
+ }
+
+ SourceLocation StaticAssertLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ SkipMalformedDecl();
+ return nullptr;
+ }
+
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ ExprResult AssertExpr(ParseConstantExpressionInExprEvalContext());
+ if (AssertExpr.isInvalid()) {
+ SkipMalformedDecl();
+ return nullptr;
+ }
+
+ ExprResult AssertMessage;
+ if (Tok.is(tok::r_paren)) {
+ unsigned DiagVal;
+ if (getLangOpts().CPlusPlus17)
+ DiagVal = diag::warn_cxx14_compat_static_assert_no_message;
+ else if (getLangOpts().CPlusPlus)
+ DiagVal = diag::ext_cxx_static_assert_no_message;
+ else if (getLangOpts().C2x)
+ DiagVal = diag::warn_c17_compat_static_assert_no_message;
+ else
+ DiagVal = diag::ext_c_static_assert_no_message;
+ Diag(Tok, DiagVal) << getStaticAssertNoMessageFixIt(AssertExpr.get(),
+ Tok.getLocation());
+ } else {
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ if (!isTokenStringLiteral()) {
+ Diag(Tok, diag::err_expected_string_literal)
+ << /*Source='static_assert'*/ 1;
+ SkipMalformedDecl();
+ return nullptr;
+ }
+
+ AssertMessage = ParseStringLiteralExpression();
+ if (AssertMessage.isInvalid()) {
+ SkipMalformedDecl();
+ return nullptr;
+ }
+ }
+
+ T.consumeClose();
+
+ DeclEnd = Tok.getLocation();
+ // Passing the token used to the error message.
+ ExpectAndConsumeSemi(diag::err_expected_semi_after_static_assert,
+ SavedTok.getName());
+
+ return Actions.ActOnStaticAssertDeclaration(StaticAssertLoc, AssertExpr.get(),
+ AssertMessage.get(),
+ T.getCloseLocation());
+}
+
+/// ParseDecltypeSpecifier - Parse a C++11 decltype specifier.
+///
+/// 'decltype' ( expression )
+/// 'decltype' ( 'auto' ) [C++1y]
+///
+SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
+ assert(Tok.isOneOf(tok::kw_decltype, tok::annot_decltype) &&
+ "Not a decltype specifier");
+
+ ExprResult Result;
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc;
+
+ if (Tok.is(tok::annot_decltype)) {
+ Result = getExprAnnotation(Tok);
+ EndLoc = Tok.getAnnotationEndLoc();
+ // Unfortunately, we don't know the LParen source location as the annotated
+ // token doesn't have it.
+ DS.setTypeArgumentRange(SourceRange(SourceLocation(), EndLoc));
+ ConsumeAnnotationToken();
+ if (Result.isInvalid()) {
+ DS.SetTypeSpecError();
+ return EndLoc;
+ }
+ } else {
+ if (Tok.getIdentifierInfo()->isStr("decltype"))
+ Diag(Tok, diag::warn_cxx98_compat_decltype);
+
+ ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "decltype",
+ tok::r_paren)) {
+ DS.SetTypeSpecError();
+ return T.getOpenLocation() == Tok.getLocation() ? StartLoc
+ : T.getOpenLocation();
+ }
+
+ // Check for C++1y 'decltype(auto)'.
+ if (Tok.is(tok::kw_auto) && NextToken().is(tok::r_paren)) {
+ // the typename-specifier in a function-style cast expression may
+ // be 'auto' since C++2b.
+ Diag(Tok.getLocation(),
+ getLangOpts().CPlusPlus14
+ ? diag::warn_cxx11_compat_decltype_auto_type_specifier
+ : diag::ext_decltype_auto_type_specifier);
+ ConsumeToken();
+ } else {
+ // Parse the expression
+
+ // C++11 [dcl.type.simple]p4:
+ // The operand of the decltype specifier is an unevaluated operand.
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated, nullptr,
+ Sema::ExpressionEvaluationContextRecord::EK_Decltype);
+ Result = Actions.CorrectDelayedTyposInExpr(
+ ParseExpression(), /*InitDecl=*/nullptr,
+ /*RecoverUncorrectedTypos=*/false,
+ [](Expr *E) { return E->hasPlaceholderType() ? ExprError() : E; });
+ if (Result.isInvalid()) {
+ DS.SetTypeSpecError();
+ if (SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch)) {
+ EndLoc = ConsumeParen();
+ } else {
+ if (PP.isBacktrackEnabled() && Tok.is(tok::semi)) {
+ // Backtrack to get the location of the last token before the semi.
+ PP.RevertCachedTokens(2);
+ ConsumeToken(); // the semi.
+ EndLoc = ConsumeAnyToken();
+ assert(Tok.is(tok::semi));
+ } else {
+ EndLoc = Tok.getLocation();
+ }
+ }
+ return EndLoc;
+ }
+
+ Result = Actions.ActOnDecltypeExpression(Result.get());
+ }
+
+ // Match the ')'
+ T.consumeClose();
+ DS.setTypeArgumentRange(T.getRange());
+ if (T.getCloseLocation().isInvalid()) {
+ DS.SetTypeSpecError();
+ // FIXME: this should return the location of the last token
+ // that was consumed (by "consumeClose()")
+ return T.getCloseLocation();
+ }
+
+ if (Result.isInvalid()) {
+ DS.SetTypeSpecError();
+ return T.getCloseLocation();
+ }
+
+ EndLoc = T.getCloseLocation();
+ }
+ assert(!Result.isInvalid());
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
+ // Check for duplicate type specifiers (e.g. "int decltype(a)").
+ if (Result.get() ? DS.SetTypeSpecType(DeclSpec::TST_decltype, StartLoc,
+ PrevSpec, DiagID, Result.get(), Policy)
+ : DS.SetTypeSpecType(DeclSpec::TST_decltype_auto, StartLoc,
+ PrevSpec, DiagID, Policy)) {
+ Diag(StartLoc, DiagID) << PrevSpec;
+ DS.SetTypeSpecError();
+ }
+ return EndLoc;
+}
+
+void Parser::AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,
+ SourceLocation StartLoc,
+ SourceLocation EndLoc) {
+ // make sure we have a token we can turn into an annotation token
+ if (PP.isBacktrackEnabled()) {
+ PP.RevertCachedTokens(1);
+ if (DS.getTypeSpecType() == TST_error) {
+ // We encountered an error in parsing 'decltype(...)' so lets annotate all
+ // the tokens in the backtracking cache - that we likely had to skip over
+ // to get to a token that allows us to resume parsing, such as a
+ // semi-colon.
+ EndLoc = PP.getLastCachedTokenLocation();
+ }
+ } else
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+
+ Tok.setKind(tok::annot_decltype);
+ setExprAnnotation(Tok,
+ DS.getTypeSpecType() == TST_decltype ? DS.getRepAsExpr()
+ : DS.getTypeSpecType() == TST_decltype_auto ? ExprResult()
+ : ExprError());
+ Tok.setAnnotationEndLoc(EndLoc);
+ Tok.setLocation(StartLoc);
+ PP.AnnotateCachedTokens(Tok);
+}
+
+DeclSpec::TST Parser::TypeTransformTokToDeclSpec() {
+ switch (Tok.getKind()) {
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \
+ case tok::kw___##Trait: \
+ return DeclSpec::TST_##Trait;
+#include "clang/Basic/TransformTypeTraits.def"
+ default:
+ llvm_unreachable("passed in an unhandled type transformation built-in");
+ }
+}
+
+bool Parser::MaybeParseTypeTransformTypeSpecifier(DeclSpec &DS) {
+ if (!NextToken().is(tok::l_paren)) {
+ Tok.setKind(tok::identifier);
+ return false;
+ }
+ DeclSpec::TST TypeTransformTST = TypeTransformTokToDeclSpec();
+ SourceLocation StartLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, Tok.getName(),
+ tok::r_paren))
+ return true;
+
+ TypeResult Result = ParseTypeName();
+ if (Result.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return true;
+ }
+
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid())
+ return true;
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ if (DS.SetTypeSpecType(TypeTransformTST, StartLoc, PrevSpec, DiagID,
+ Result.get(),
+ Actions.getASTContext().getPrintingPolicy()))
+ Diag(StartLoc, DiagID) << PrevSpec;
+ DS.setTypeArgumentRange(T.getRange());
+ return true;
+}
+
+/// ParseBaseTypeSpecifier - Parse a C++ base-type-specifier which is either a
+/// class name or decltype-specifier. Note that we only check that the result
+/// names a type; semantic analysis will need to verify that the type names a
+/// class. The result is either a type or null, depending on whether a type
+/// name was found.
+///
+/// base-type-specifier: [C++11 class.derived]
+/// class-or-decltype
+/// class-or-decltype: [C++11 class.derived]
+/// nested-name-specifier[opt] class-name
+/// decltype-specifier
+/// class-name: [C++ class.name]
+/// identifier
+/// simple-template-id
+///
+/// In C++98, instead of base-type-specifier, we have:
+///
+/// ::[opt] nested-name-specifier[opt] class-name
+TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
+ SourceLocation &EndLocation) {
+ // Ignore attempts to use typename
+ if (Tok.is(tok::kw_typename)) {
+ Diag(Tok, diag::err_expected_class_name_not_template)
+ << FixItHint::CreateRemoval(Tok.getLocation());
+ ConsumeToken();
+ }
+
+ // Parse optional nested-name-specifier
+ CXXScopeSpec SS;
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false))
+ return true;
+
+ BaseLoc = Tok.getLocation();
+
+ // Parse decltype-specifier
+ // tok == kw_decltype is just error recovery, it can only happen when SS
+ // isn't empty
+ if (Tok.isOneOf(tok::kw_decltype, tok::annot_decltype)) {
+ if (SS.isNotEmpty())
+ Diag(SS.getBeginLoc(), diag::err_unexpected_scope_on_base_decltype)
+ << FixItHint::CreateRemoval(SS.getRange());
+ // Fake up a Declarator to use with ActOnTypeName.
+ DeclSpec DS(AttrFactory);
+
+ EndLocation = ParseDecltypeSpecifier(DS);
+
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ }
+
+ // Check whether we have a template-id that names a type.
+ if (Tok.is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ if (TemplateId->mightBeType()) {
+ AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No,
+ /*IsClassName=*/true);
+
+ assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
+ TypeResult Type = getTypeAnnotation(Tok);
+ EndLocation = Tok.getAnnotationEndLoc();
+ ConsumeAnnotationToken();
+ return Type;
+ }
+
+ // Fall through to produce an error below.
+ }
+
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected_class_name);
+ return true;
+ }
+
+ IdentifierInfo *Id = Tok.getIdentifierInfo();
+ SourceLocation IdLoc = ConsumeToken();
+
+ if (Tok.is(tok::less)) {
+ // It looks the user intended to write a template-id here, but the
+ // template-name was wrong. Try to fix that.
+ // FIXME: Invoke ParseOptionalCXXScopeSpecifier in a "'template' is neither
+ // required nor permitted" mode, and do this there.
+ TemplateNameKind TNK = TNK_Non_template;
+ TemplateTy Template;
+ if (!Actions.DiagnoseUnknownTemplateName(*Id, IdLoc, getCurScope(), &SS,
+ Template, TNK)) {
+ Diag(IdLoc, diag::err_unknown_template_name) << Id;
+ }
+
+ // Form the template name
+ UnqualifiedId TemplateName;
+ TemplateName.setIdentifier(Id, IdLoc);
+
+ // Parse the full template-id, then turn it into a type.
+ if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
+ TemplateName))
+ return true;
+ if (Tok.is(tok::annot_template_id) &&
+ takeTemplateIdAnnotation(Tok)->mightBeType())
+ AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No,
+ /*IsClassName=*/true);
+
+ // If we didn't end up with a typename token, there's nothing more we
+ // can do.
+ if (Tok.isNot(tok::annot_typename))
+ return true;
+
+ // Retrieve the type from the annotation token, consume that token, and
+ // return.
+ EndLocation = Tok.getAnnotationEndLoc();
+ TypeResult Type = getTypeAnnotation(Tok);
+ ConsumeAnnotationToken();
+ return Type;
+ }
+
+ // We have an identifier; check whether it is actually a type.
+ IdentifierInfo *CorrectedII = nullptr;
+ ParsedType Type = Actions.getTypeName(
+ *Id, IdLoc, getCurScope(), &SS, /*isClassName=*/true, false, nullptr,
+ /*IsCtorOrDtorName=*/false,
+ /*WantNontrivialTypeSourceInfo=*/true,
+ /*IsClassTemplateDeductionContext=*/false, ImplicitTypenameContext::No,
+ &CorrectedII);
+ if (!Type) {
+ Diag(IdLoc, diag::err_expected_class_name);
+ return true;
+ }
+
+ // Consume the identifier.
+ EndLocation = IdLoc;
+
+ // Fake up a Declarator to use with ActOnTypeName.
+ DeclSpec DS(AttrFactory);
+ DS.SetRangeStart(IdLoc);
+ DS.SetRangeEnd(EndLocation);
+ DS.getTypeSpecScope() = SS;
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ DS.SetTypeSpecType(TST_typename, IdLoc, PrevSpec, DiagID, Type,
+ Actions.getASTContext().getPrintingPolicy());
+
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+}
+
+void Parser::ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs) {
+ while (Tok.isOneOf(tok::kw___single_inheritance,
+ tok::kw___multiple_inheritance,
+ tok::kw___virtual_inheritance)) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+ }
+}
+
+/// Determine whether the following tokens are valid after a type-specifier
+/// which could be a standalone declaration. This will conservatively return
+/// true if there's any doubt, and is appropriate for insert-';' fixits.
+bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) {
+ // This switch enumerates the valid "follow" set for type-specifiers.
+ switch (Tok.getKind()) {
+ default:
+ break;
+ case tok::semi: // struct foo {...} ;
+ case tok::star: // struct foo {...} * P;
+ case tok::amp: // struct foo {...} & R = ...
+ case tok::ampamp: // struct foo {...} && R = ...
+ case tok::identifier: // struct foo {...} V ;
+ case tok::r_paren: //(struct foo {...} ) {4}
+ case tok::coloncolon: // struct foo {...} :: a::b;
+ case tok::annot_cxxscope: // struct foo {...} a:: b;
+ case tok::annot_typename: // struct foo {...} a ::b;
+ case tok::annot_template_id: // struct foo {...} a<int> ::b;
+ case tok::kw_decltype: // struct foo {...} decltype (a)::b;
+ case tok::l_paren: // struct foo {...} ( x);
+ case tok::comma: // __builtin_offsetof(struct foo{...} ,
+ case tok::kw_operator: // struct foo operator ++() {...}
+ case tok::kw___declspec: // struct foo {...} __declspec(...)
+ case tok::l_square: // void f(struct f [ 3])
+ case tok::ellipsis: // void f(struct f ... [Ns])
+ // FIXME: we should emit semantic diagnostic when declaration
+ // attribute is in type attribute position.
+ case tok::kw___attribute: // struct foo __attribute__((used)) x;
+ case tok::annot_pragma_pack: // struct foo {...} _Pragma(pack(pop));
+ // struct foo {...} _Pragma(section(...));
+ case tok::annot_pragma_ms_pragma:
+ // struct foo {...} _Pragma(vtordisp(pop));
+ case tok::annot_pragma_ms_vtordisp:
+ // struct foo {...} _Pragma(pointers_to_members(...));
+ case tok::annot_pragma_ms_pointers_to_members:
+ return true;
+ case tok::colon:
+ return CouldBeBitfield || // enum E { ... } : 2;
+ ColonIsSacred; // _Generic(..., enum E : 2);
+ // Microsoft compatibility
+ case tok::kw___cdecl: // struct foo {...} __cdecl x;
+ case tok::kw___fastcall: // struct foo {...} __fastcall x;
+ case tok::kw___stdcall: // struct foo {...} __stdcall x;
+ case tok::kw___thiscall: // struct foo {...} __thiscall x;
+ case tok::kw___vectorcall: // struct foo {...} __vectorcall x;
+ // We will diagnose these calling-convention specifiers on non-function
+ // declarations later, so claim they are valid after a type specifier.
+ return getLangOpts().MicrosoftExt;
+ // Type qualifiers
+ case tok::kw_const: // struct foo {...} const x;
+ case tok::kw_volatile: // struct foo {...} volatile x;
+ case tok::kw_restrict: // struct foo {...} restrict x;
+ case tok::kw__Atomic: // struct foo {...} _Atomic x;
+ case tok::kw___unaligned: // struct foo {...} __unaligned *x;
+ // Function specifiers
+ // Note, no 'explicit'. An explicit function must be either a conversion
+ // operator or a constructor. Either way, it can't have a return type.
+ case tok::kw_inline: // struct foo inline f();
+ case tok::kw_virtual: // struct foo virtual f();
+ case tok::kw_friend: // struct foo friend f();
+ // Storage-class specifiers
+ case tok::kw_static: // struct foo {...} static x;
+ case tok::kw_extern: // struct foo {...} extern x;
+ case tok::kw_typedef: // struct foo {...} typedef x;
+ case tok::kw_register: // struct foo {...} register x;
+ case tok::kw_auto: // struct foo {...} auto x;
+ case tok::kw_mutable: // struct foo {...} mutable x;
+ case tok::kw_thread_local: // struct foo {...} thread_local x;
+ case tok::kw_constexpr: // struct foo {...} constexpr x;
+ case tok::kw_consteval: // struct foo {...} consteval x;
+ case tok::kw_constinit: // struct foo {...} constinit x;
+ // As shown above, type qualifiers and storage class specifiers absolutely
+ // can occur after class specifiers according to the grammar. However,
+ // almost no one actually writes code like this. If we see one of these,
+ // it is much more likely that someone missed a semi colon and the
+ // type/storage class specifier we're seeing is part of the *next*
+ // intended declaration, as in:
+ //
+ // struct foo { ... }
+ // typedef int X;
+ //
+ // We'd really like to emit a missing semicolon error instead of emitting
+ // an error on the 'int' saying that you can't have two type specifiers in
+ // the same declaration of X. Because of this, we look ahead past this
+ // token to see if it's a type specifier. If so, we know the code is
+ // otherwise invalid, so we can produce the expected semi error.
+ if (!isKnownToBeTypeSpecifier(NextToken()))
+ return true;
+ break;
+ case tok::r_brace: // struct bar { struct foo {...} }
+ // Missing ';' at end of struct is accepted as an extension in C mode.
+ if (!getLangOpts().CPlusPlus)
+ return true;
+ break;
+ case tok::greater:
+ // template<class T = class X>
+ return getLangOpts().CPlusPlus;
+ }
+ return false;
+}
+
+/// ParseClassSpecifier - Parse a C++ class-specifier [C++ class] or
+/// elaborated-type-specifier [C++ dcl.type.elab]; we can't tell which
+/// until we reach the start of a definition or see a token that
+/// cannot start a definition.
+///
+/// class-specifier: [C++ class]
+/// class-head '{' member-specification[opt] '}'
+/// class-head '{' member-specification[opt] '}' attributes[opt]
+/// class-head:
+/// class-key identifier[opt] base-clause[opt]
+/// class-key nested-name-specifier identifier base-clause[opt]
+/// class-key nested-name-specifier[opt] simple-template-id
+/// base-clause[opt]
+/// [GNU] class-key attributes[opt] identifier[opt] base-clause[opt]
+/// [GNU] class-key attributes[opt] nested-name-specifier
+/// identifier base-clause[opt]
+/// [GNU] class-key attributes[opt] nested-name-specifier[opt]
+/// simple-template-id base-clause[opt]
+/// class-key:
+/// 'class'
+/// 'struct'
+/// 'union'
+///
+/// elaborated-type-specifier: [C++ dcl.type.elab]
+/// class-key ::[opt] nested-name-specifier[opt] identifier
+/// class-key ::[opt] nested-name-specifier[opt] 'template'[opt]
+/// simple-template-id
+///
+/// Note that the C++ class-specifier and elaborated-type-specifier,
+/// together, subsume the C99 struct-or-union-specifier:
+///
+/// struct-or-union-specifier: [C99 6.7.2.1]
+/// struct-or-union identifier[opt] '{' struct-contents '}'
+/// struct-or-union identifier
+/// [GNU] struct-or-union attributes[opt] identifier[opt] '{' struct-contents
+/// '}' attributes[opt]
+/// [GNU] struct-or-union attributes[opt] identifier
+/// struct-or-union:
+/// 'struct'
+/// 'union'
+void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
+ SourceLocation StartLoc, DeclSpec &DS,
+ const ParsedTemplateInfo &TemplateInfo,
+ AccessSpecifier AS, bool EnteringContext,
+ DeclSpecContext DSC,
+ ParsedAttributes &Attributes) {
+ DeclSpec::TST TagType;
+ if (TagTokKind == tok::kw_struct)
+ TagType = DeclSpec::TST_struct;
+ else if (TagTokKind == tok::kw___interface)
+ TagType = DeclSpec::TST_interface;
+ else if (TagTokKind == tok::kw_class)
+ TagType = DeclSpec::TST_class;
+ else {
+ assert(TagTokKind == tok::kw_union && "Not a class specifier");
+ TagType = DeclSpec::TST_union;
+ }
+
+ if (Tok.is(tok::code_completion)) {
+ // Code completion for a struct, class, or union name.
+ cutOffParsing();
+ Actions.CodeCompleteTag(getCurScope(), TagType);
+ return;
+ }
+
+ // C++20 [temp.class.spec] 13.7.5/10
+ // The usual access checking rules do not apply to non-dependent names
+ // used to specify template arguments of the simple-template-id of the
+ // partial specialization.
+ // C++20 [temp.spec] 13.9/6:
+ // The usual access checking rules do not apply to names in a declaration
+ // of an explicit instantiation or explicit specialization...
+ const bool shouldDelayDiagsInTag =
+ (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate);
+ SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
+
+ ParsedAttributes attrs(AttrFactory);
+ // If attributes exist after tag, parse them.
+ MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+
+ // Parse inheritance specifiers.
+ if (Tok.isOneOf(tok::kw___single_inheritance, tok::kw___multiple_inheritance,
+ tok::kw___virtual_inheritance))
+ ParseMicrosoftInheritanceClassAttributes(attrs);
+
+ // Allow attributes to precede or succeed the inheritance specifiers.
+ MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+
+ // Source location used by FIXIT to insert misplaced
+ // C++11 attributes
+ SourceLocation AttrFixitLoc = Tok.getLocation();
+
+ if (TagType == DeclSpec::TST_struct && Tok.isNot(tok::identifier) &&
+ !Tok.isAnnotation() && Tok.getIdentifierInfo() &&
+ Tok.isOneOf(
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) tok::kw___##Trait,
+#include "clang/Basic/TransformTypeTraits.def"
+ tok::kw___is_abstract,
+ tok::kw___is_aggregate,
+ tok::kw___is_arithmetic,
+ tok::kw___is_array,
+ tok::kw___is_assignable,
+ tok::kw___is_base_of,
+ tok::kw___is_bounded_array,
+ tok::kw___is_class,
+ tok::kw___is_complete_type,
+ tok::kw___is_compound,
+ tok::kw___is_const,
+ tok::kw___is_constructible,
+ tok::kw___is_convertible,
+ tok::kw___is_convertible_to,
+ tok::kw___is_destructible,
+ tok::kw___is_empty,
+ tok::kw___is_enum,
+ tok::kw___is_floating_point,
+ tok::kw___is_final,
+ tok::kw___is_function,
+ tok::kw___is_fundamental,
+ tok::kw___is_integral,
+ tok::kw___is_interface_class,
+ tok::kw___is_literal,
+ tok::kw___is_lvalue_expr,
+ tok::kw___is_lvalue_reference,
+ tok::kw___is_member_function_pointer,
+ tok::kw___is_member_object_pointer,
+ tok::kw___is_member_pointer,
+ tok::kw___is_nothrow_assignable,
+ tok::kw___is_nothrow_constructible,
+ tok::kw___is_nothrow_destructible,
+ tok::kw___is_nullptr,
+ tok::kw___is_object,
+ tok::kw___is_pod,
+ tok::kw___is_pointer,
+ tok::kw___is_polymorphic,
+ tok::kw___is_reference,
+ tok::kw___is_referenceable,
+ tok::kw___is_rvalue_expr,
+ tok::kw___is_rvalue_reference,
+ tok::kw___is_same,
+ tok::kw___is_scalar,
+ tok::kw___is_scoped_enum,
+ tok::kw___is_sealed,
+ tok::kw___is_signed,
+ tok::kw___is_standard_layout,
+ tok::kw___is_trivial,
+ tok::kw___is_trivially_assignable,
+ tok::kw___is_trivially_constructible,
+ tok::kw___is_trivially_copyable,
+ tok::kw___is_unbounded_array,
+ tok::kw___is_union,
+ tok::kw___is_unsigned,
+ tok::kw___is_void,
+ tok::kw___is_volatile))
+ // GNU libstdc++ 4.2 and libc++ use certain intrinsic names as the
+ // name of struct templates, but some are keywords in GCC >= 4.3
+ // and Clang. Therefore, when we see the token sequence "struct
+ // X", make X into a normal identifier rather than a keyword, to
+ // allow libstdc++ 4.2 and libc++ to work properly.
+ TryKeywordIdentFallback(true);
+
+ struct PreserveAtomicIdentifierInfoRAII {
+ PreserveAtomicIdentifierInfoRAII(Token &Tok, bool Enabled)
+ : AtomicII(nullptr) {
+ if (!Enabled)
+ return;
+ assert(Tok.is(tok::kw__Atomic));
+ AtomicII = Tok.getIdentifierInfo();
+ AtomicII->revertTokenIDToIdentifier();
+ Tok.setKind(tok::identifier);
+ }
+ ~PreserveAtomicIdentifierInfoRAII() {
+ if (!AtomicII)
+ return;
+ AtomicII->revertIdentifierToTokenID(tok::kw__Atomic);
+ }
+ IdentifierInfo *AtomicII;
+ };
+
+ // 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>. When we are parsing 'struct _Atomic', don't consider
+ // '_Atomic' to be a keyword. We are careful to undo this so that clang can
+ // use '_Atomic' in its own header files.
+ bool ShouldChangeAtomicToIdentifier = getLangOpts().MSVCCompat &&
+ Tok.is(tok::kw__Atomic) &&
+ TagType == DeclSpec::TST_struct;
+ PreserveAtomicIdentifierInfoRAII AtomicTokenGuard(
+ Tok, ShouldChangeAtomicToIdentifier);
+
+ // Parse the (optional) nested-name-specifier.
+ CXXScopeSpec &SS = DS.getTypeSpecScope();
+ if (getLangOpts().CPlusPlus) {
+ // "FOO : BAR" is not a potential typo for "FOO::BAR". In this context it
+ // is a base-specifier-list.
+ ColonProtectionRAIIObject X(*this);
+
+ CXXScopeSpec Spec;
+ bool HasValidSpec = true;
+ if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ EnteringContext)) {
+ DS.SetTypeSpecError();
+ HasValidSpec = false;
+ }
+ if (Spec.isSet())
+ if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ HasValidSpec = false;
+ }
+ if (HasValidSpec)
+ SS = Spec;
+ }
+
+ TemplateParameterLists *TemplateParams = TemplateInfo.TemplateParams;
+
+ auto RecoverFromUndeclaredTemplateName = [&](IdentifierInfo *Name,
+ SourceLocation NameLoc,
+ SourceRange TemplateArgRange,
+ bool KnownUndeclared) {
+ Diag(NameLoc, diag::err_explicit_spec_non_template)
+ << (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation)
+ << TagTokKind << Name << TemplateArgRange << KnownUndeclared;
+
+ // Strip off the last template parameter list if it was empty, since
+ // we've removed its template argument list.
+ if (TemplateParams && TemplateInfo.LastParameterListWasEmpty) {
+ if (TemplateParams->size() > 1) {
+ TemplateParams->pop_back();
+ } else {
+ TemplateParams = nullptr;
+ const_cast<ParsedTemplateInfo &>(TemplateInfo).Kind =
+ ParsedTemplateInfo::NonTemplate;
+ }
+ } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) {
+ // Pretend this is just a forward declaration.
+ TemplateParams = nullptr;
+ const_cast<ParsedTemplateInfo &>(TemplateInfo).Kind =
+ ParsedTemplateInfo::NonTemplate;
+ const_cast<ParsedTemplateInfo &>(TemplateInfo).TemplateLoc =
+ SourceLocation();
+ const_cast<ParsedTemplateInfo &>(TemplateInfo).ExternLoc =
+ SourceLocation();
+ }
+ };
+
+ // Parse the (optional) class name or simple-template-id.
+ IdentifierInfo *Name = nullptr;
+ SourceLocation NameLoc;
+ TemplateIdAnnotation *TemplateId = nullptr;
+ if (Tok.is(tok::identifier)) {
+ Name = Tok.getIdentifierInfo();
+ NameLoc = ConsumeToken();
+
+ if (Tok.is(tok::less) && getLangOpts().CPlusPlus) {
+ // The name was supposed to refer to a template, but didn't.
+ // Eat the template argument list and try to continue parsing this as
+ // a class (or template thereof).
+ TemplateArgList TemplateArgs;
+ SourceLocation LAngleLoc, RAngleLoc;
+ if (ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs,
+ RAngleLoc)) {
+ // We couldn't parse the template argument list at all, so don't
+ // try to give any location information for the list.
+ LAngleLoc = RAngleLoc = SourceLocation();
+ }
+ RecoverFromUndeclaredTemplateName(
+ Name, NameLoc, SourceRange(LAngleLoc, RAngleLoc), false);
+ }
+ } else if (Tok.is(tok::annot_template_id)) {
+ TemplateId = takeTemplateIdAnnotation(Tok);
+ NameLoc = ConsumeAnnotationToken();
+
+ if (TemplateId->Kind == TNK_Undeclared_template) {
+ // Try to resolve the template name to a type template. May update Kind.
+ Actions.ActOnUndeclaredTypeTemplateName(
+ getCurScope(), TemplateId->Template, TemplateId->Kind, NameLoc, Name);
+ if (TemplateId->Kind == TNK_Undeclared_template) {
+ RecoverFromUndeclaredTemplateName(
+ Name, NameLoc,
+ SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc), true);
+ TemplateId = nullptr;
+ }
+ }
+
+ if (TemplateId && !TemplateId->mightBeType()) {
+ // The template-name in the simple-template-id refers to
+ // something other than a type template. Give an appropriate
+ // error message and skip to the ';'.
+ SourceRange Range(NameLoc);
+ if (SS.isNotEmpty())
+ Range.setBegin(SS.getBeginLoc());
+
+ // FIXME: Name may be null here.
+ Diag(TemplateId->LAngleLoc, diag::err_template_spec_syntax_non_template)
+ << TemplateId->Name << static_cast<int>(TemplateId->Kind) << Range;
+
+ DS.SetTypeSpecError();
+ SkipUntil(tok::semi, StopBeforeMatch);
+ return;
+ }
+ }
+
+ // There are four options here.
+ // - If we are in a trailing return type, this is always just a reference,
+ // and we must not try to parse a definition. For instance,
+ // [] () -> struct S { };
+ // does not define a type.
+ // - If we have 'struct foo {...', 'struct foo :...',
+ // 'struct foo final :' or 'struct foo final {', then this is a definition.
+ // - If we have 'struct foo;', then this is either a forward declaration
+ // or a friend declaration, which have to be treated differently.
+ // - Otherwise we have something like 'struct foo xyz', a reference.
+ //
+ // We also detect these erroneous cases to provide better diagnostic for
+ // C++11 attributes parsing.
+ // - attributes follow class name:
+ // struct foo [[]] {};
+ // - attributes appear before or after 'final':
+ // struct foo [[]] final [[]] {};
+ //
+ // However, in type-specifier-seq's, things look like declarations but are
+ // just references, e.g.
+ // new struct s;
+ // or
+ // &T::operator struct s;
+ // For these, DSC is DeclSpecContext::DSC_type_specifier or
+ // DeclSpecContext::DSC_alias_declaration.
+
+ // If there are attributes after class name, parse them.
+ MaybeParseCXX11Attributes(Attributes);
+
+ const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
+ Sema::TagUseKind TUK;
+ if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) ==
+ AllowDefiningTypeSpec::No ||
+ (getLangOpts().OpenMP && OpenMPDirectiveParsing))
+ TUK = Sema::TUK_Reference;
+ else if (Tok.is(tok::l_brace) ||
+ (DSC != DeclSpecContext::DSC_association &&
+ getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
+ (isClassCompatibleKeyword() &&
+ (NextToken().is(tok::l_brace) || NextToken().is(tok::colon)))) {
+ if (DS.isFriendSpecified()) {
+ // C++ [class.friend]p2:
+ // A class shall not be defined in a friend declaration.
+ Diag(Tok.getLocation(), diag::err_friend_decl_defines_type)
+ << SourceRange(DS.getFriendSpecLoc());
+
+ // Skip everything up to the semicolon, so that this looks like a proper
+ // friend class (or template thereof) declaration.
+ SkipUntil(tok::semi, StopBeforeMatch);
+ TUK = Sema::TUK_Friend;
+ } else {
+ // Okay, this is a class definition.
+ TUK = Sema::TUK_Definition;
+ }
+ } else if (isClassCompatibleKeyword() &&
+ (NextToken().is(tok::l_square) ||
+ NextToken().is(tok::kw_alignas) ||
+ isCXX11VirtSpecifier(NextToken()) != VirtSpecifiers::VS_None)) {
+ // We can't tell if this is a definition or reference
+ // until we skipped the 'final' and C++11 attribute specifiers.
+ TentativeParsingAction PA(*this);
+
+ // Skip the 'final', abstract'... keywords.
+ while (isClassCompatibleKeyword()) {
+ ConsumeToken();
+ }
+
+ // Skip C++11 attribute specifiers.
+ while (true) {
+ if (Tok.is(tok::l_square) && NextToken().is(tok::l_square)) {
+ ConsumeBracket();
+ if (!SkipUntil(tok::r_square, StopAtSemi))
+ break;
+ } else if (Tok.is(tok::kw_alignas) && NextToken().is(tok::l_paren)) {
+ ConsumeToken();
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren, StopAtSemi))
+ break;
+ } else {
+ break;
+ }
+ }
+
+ if (Tok.isOneOf(tok::l_brace, tok::colon))
+ TUK = Sema::TUK_Definition;
+ else
+ TUK = Sema::TUK_Reference;
+
+ PA.Revert();
+ } else if (!isTypeSpecifier(DSC) &&
+ (Tok.is(tok::semi) ||
+ (Tok.isAtStartOfLine() && !isValidAfterTypeSpecifier(false)))) {
+ TUK = DS.isFriendSpecified() ? Sema::TUK_Friend : Sema::TUK_Declaration;
+ if (Tok.isNot(tok::semi)) {
+ const PrintingPolicy &PPol = Actions.getASTContext().getPrintingPolicy();
+ // A semicolon was missing after this declaration. Diagnose and recover.
+ ExpectAndConsume(tok::semi, diag::err_expected_after,
+ DeclSpec::getSpecifierName(TagType, PPol));
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+ Tok.setKind(tok::semi);
+ }
+ } else
+ TUK = Sema::TUK_Reference;
+
+ // Forbid misplaced attributes. In cases of a reference, we pass attributes
+ // to caller to handle.
+ if (TUK != Sema::TUK_Reference) {
+ // If this is not a reference, then the only possible
+ // valid place for C++11 attributes to appear here
+ // is between class-key and class-name. If there are
+ // any attributes after class-name, we try a fixit to move
+ // them to the right place.
+ SourceRange AttrRange = Attributes.Range;
+ if (AttrRange.isValid()) {
+ Diag(AttrRange.getBegin(), diag::err_attributes_not_allowed)
+ << AttrRange
+ << FixItHint::CreateInsertionFromRange(
+ AttrFixitLoc, CharSourceRange(AttrRange, true))
+ << FixItHint::CreateRemoval(AttrRange);
+
+ // Recover by adding misplaced attributes to the attribute list
+ // of the class so they can be applied on the class later.
+ attrs.takeAllFrom(Attributes);
+ }
+ }
+
+ if (!Name && !TemplateId &&
+ (DS.getTypeSpecType() == DeclSpec::TST_error ||
+ TUK != Sema::TUK_Definition)) {
+ if (DS.getTypeSpecType() != DeclSpec::TST_error) {
+ // We have a declaration or reference to an anonymous class.
+ Diag(StartLoc, diag::err_anon_type_definition)
+ << DeclSpec::getSpecifierName(TagType, Policy);
+ }
+
+ // If we are parsing a definition and stop at a base-clause, continue on
+ // until the semicolon. Continuing from the comma will just trick us into
+ // thinking we are seeing a variable declaration.
+ if (TUK == Sema::TUK_Definition && Tok.is(tok::colon))
+ SkipUntil(tok::semi, StopBeforeMatch);
+ else
+ SkipUntil(tok::comma, StopAtSemi);
+ return;
+ }
+
+ // Create the tag portion of the class or class template.
+ DeclResult TagOrTempResult = true; // invalid
+ TypeResult TypeResult = true; // invalid
+
+ bool Owned = false;
+ Sema::SkipBodyInfo SkipBody;
+ if (TemplateId) {
+ // Explicit specialization, class template partial specialization,
+ // or explicit instantiation.
+ ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
+ TemplateId->NumArgs);
+ if (TemplateId->isInvalid()) {
+ // Can't build the declaration.
+ } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
+ TUK == Sema::TUK_Declaration) {
+ // This is an explicit instantiation of a class template.
+ ProhibitCXX11Attributes(attrs, diag::err_attributes_not_allowed,
+ /*DiagnoseEmptyAttrs=*/true);
+
+ TagOrTempResult = Actions.ActOnExplicitInstantiation(
+ getCurScope(), TemplateInfo.ExternLoc, TemplateInfo.TemplateLoc,
+ TagType, StartLoc, SS, TemplateId->Template,
+ TemplateId->TemplateNameLoc, TemplateId->LAngleLoc, TemplateArgsPtr,
+ TemplateId->RAngleLoc, attrs);
+
+ // Friend template-ids are treated as references unless
+ // they have template headers, in which case they're ill-formed
+ // (FIXME: "template <class T> friend class A<T>::B<int>;").
+ // We diagnose this error in ActOnClassTemplateSpecialization.
+ } else if (TUK == Sema::TUK_Reference ||
+ (TUK == Sema::TUK_Friend &&
+ TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate)) {
+ ProhibitCXX11Attributes(attrs, diag::err_attributes_not_allowed,
+ /*DiagnoseEmptyAttrs=*/true);
+ TypeResult = Actions.ActOnTagTemplateIdType(
+ TUK, TagType, StartLoc, SS, TemplateId->TemplateKWLoc,
+ TemplateId->Template, TemplateId->TemplateNameLoc,
+ TemplateId->LAngleLoc, TemplateArgsPtr, TemplateId->RAngleLoc);
+ } else {
+ // This is an explicit specialization or a class template
+ // partial specialization.
+ TemplateParameterLists FakedParamLists;
+ if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) {
+ // This looks like an explicit instantiation, because we have
+ // something like
+ //
+ // template class Foo<X>
+ //
+ // but it actually has a definition. Most likely, this was
+ // meant to be an explicit specialization, but the user forgot
+ // the '<>' after 'template'.
+ // It this is friend declaration however, since it cannot have a
+ // template header, it is most likely that the user meant to
+ // remove the 'template' keyword.
+ assert((TUK == Sema::TUK_Definition || TUK == Sema::TUK_Friend) &&
+ "Expected a definition here");
+
+ if (TUK == Sema::TUK_Friend) {
+ Diag(DS.getFriendSpecLoc(), diag::err_friend_explicit_instantiation);
+ TemplateParams = nullptr;
+ } else {
+ SourceLocation LAngleLoc =
+ PP.getLocForEndOfToken(TemplateInfo.TemplateLoc);
+ Diag(TemplateId->TemplateNameLoc,
+ diag::err_explicit_instantiation_with_definition)
+ << SourceRange(TemplateInfo.TemplateLoc)
+ << FixItHint::CreateInsertion(LAngleLoc, "<>");
+
+ // Create a fake template parameter list that contains only
+ // "template<>", so that we treat this construct as a class
+ // template specialization.
+ FakedParamLists.push_back(Actions.ActOnTemplateParameterList(
+ 0, SourceLocation(), TemplateInfo.TemplateLoc, LAngleLoc,
+ std::nullopt, LAngleLoc, nullptr));
+ TemplateParams = &FakedParamLists;
+ }
+ }
+
+ // Build the class template specialization.
+ TagOrTempResult = Actions.ActOnClassTemplateSpecialization(
+ getCurScope(), TagType, TUK, StartLoc, DS.getModulePrivateSpecLoc(),
+ SS, *TemplateId, attrs,
+ MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0]
+ : nullptr,
+ TemplateParams ? TemplateParams->size() : 0),
+ &SkipBody);
+ }
+ } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
+ TUK == Sema::TUK_Declaration) {
+ // Explicit instantiation of a member of a class template
+ // specialization, e.g.,
+ //
+ // template struct Outer<int>::Inner;
+ //
+ ProhibitAttributes(attrs);
+
+ TagOrTempResult = Actions.ActOnExplicitInstantiation(
+ getCurScope(), TemplateInfo.ExternLoc, TemplateInfo.TemplateLoc,
+ TagType, StartLoc, SS, Name, NameLoc, attrs);
+ } else if (TUK == Sema::TUK_Friend &&
+ TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) {
+ ProhibitCXX11Attributes(attrs, diag::err_attributes_not_allowed,
+ /*DiagnoseEmptyAttrs=*/true);
+
+ TagOrTempResult = Actions.ActOnTemplatedFriendTag(
+ getCurScope(), DS.getFriendSpecLoc(), TagType, StartLoc, SS, Name,
+ NameLoc, attrs,
+ MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] : nullptr,
+ TemplateParams ? TemplateParams->size() : 0));
+ } else {
+ if (TUK != Sema::TUK_Declaration && TUK != Sema::TUK_Definition)
+ ProhibitCXX11Attributes(attrs, diag::err_attributes_not_allowed,
+ /* DiagnoseEmptyAttrs=*/true);
+
+ if (TUK == Sema::TUK_Definition &&
+ TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) {
+ // If the declarator-id is not a template-id, issue a diagnostic and
+ // recover by ignoring the 'template' keyword.
+ Diag(Tok, diag::err_template_defn_explicit_instantiation)
+ << 1 << FixItHint::CreateRemoval(TemplateInfo.TemplateLoc);
+ TemplateParams = nullptr;
+ }
+
+ bool IsDependent = false;
+
+ // Don't pass down template parameter lists if this is just a tag
+ // reference. For example, we don't need the template parameters here:
+ // template <class T> class A *makeA(T t);
+ MultiTemplateParamsArg TParams;
+ if (TUK != Sema::TUK_Reference && TemplateParams)
+ TParams =
+ MultiTemplateParamsArg(&(*TemplateParams)[0], TemplateParams->size());
+
+ stripTypeAttributesOffDeclSpec(attrs, DS, TUK);
+
+ // Declaration or definition of a class type
+ TagOrTempResult = Actions.ActOnTag(
+ getCurScope(), TagType, TUK, StartLoc, SS, Name, NameLoc, attrs, AS,
+ DS.getModulePrivateSpecLoc(), TParams, Owned, IsDependent,
+ SourceLocation(), false, clang::TypeResult(),
+ DSC == DeclSpecContext::DSC_type_specifier,
+ DSC == DeclSpecContext::DSC_template_param ||
+ DSC == DeclSpecContext::DSC_template_type_arg,
+ OffsetOfState, &SkipBody);
+
+ // If ActOnTag said the type was dependent, try again with the
+ // less common call.
+ if (IsDependent) {
+ assert(TUK == Sema::TUK_Reference || TUK == Sema::TUK_Friend);
+ TypeResult = Actions.ActOnDependentTag(getCurScope(), TagType, TUK, SS,
+ Name, StartLoc, NameLoc);
+ }
+ }
+
+ // If this is an elaborated type specifier in function template,
+ // and we delayed diagnostics before,
+ // just merge them into the current pool.
+ if (shouldDelayDiagsInTag) {
+ diagsFromTag.done();
+ if (TUK == Sema::TUK_Reference &&
+ TemplateInfo.Kind == ParsedTemplateInfo::Template)
+ diagsFromTag.redelay();
+ }
+
+ // If there is a body, parse it and inform the actions module.
+ if (TUK == Sema::TUK_Definition) {
+ assert(Tok.is(tok::l_brace) ||
+ (getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
+ isClassCompatibleKeyword());
+ if (SkipBody.ShouldSkip)
+ SkipCXXMemberSpecification(StartLoc, AttrFixitLoc, TagType,
+ TagOrTempResult.get());
+ else if (getLangOpts().CPlusPlus)
+ ParseCXXMemberSpecification(StartLoc, AttrFixitLoc, attrs, TagType,
+ TagOrTempResult.get());
+ else {
+ Decl *D =
+ SkipBody.CheckSameAsPrevious ? SkipBody.New : TagOrTempResult.get();
+ // Parse the definition body.
+ ParseStructUnionBody(StartLoc, TagType, cast<RecordDecl>(D));
+ if (SkipBody.CheckSameAsPrevious &&
+ !Actions.ActOnDuplicateDefinition(TagOrTempResult.get(), SkipBody)) {
+ DS.SetTypeSpecError();
+ return;
+ }
+ }
+ }
+
+ if (!TagOrTempResult.isInvalid())
+ // Delayed processing of attributes.
+ Actions.ProcessDeclAttributeDelayed(TagOrTempResult.get(), attrs);
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ bool Result;
+ if (!TypeResult.isInvalid()) {
+ Result = DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc,
+ NameLoc.isValid() ? NameLoc : StartLoc,
+ PrevSpec, DiagID, TypeResult.get(), Policy);
+ } else if (!TagOrTempResult.isInvalid()) {
+ Result = DS.SetTypeSpecType(
+ TagType, StartLoc, NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec,
+ DiagID, TagOrTempResult.get(), Owned, Policy);
+ } else {
+ DS.SetTypeSpecError();
+ return;
+ }
+
+ if (Result)
+ Diag(StartLoc, DiagID) << PrevSpec;
+
+ // At this point, we've successfully parsed a class-specifier in 'definition'
+ // form (e.g. "struct foo { int x; }". While we could just return here, we're
+ // going to look at what comes after it to improve error recovery. If an
+ // impossible token occurs next, we assume that the programmer forgot a ; at
+ // the end of the declaration and recover that way.
+ //
+ // Also enforce C++ [temp]p3:
+ // In a template-declaration which defines a class, no declarator
+ // is permitted.
+ //
+ // After a type-specifier, we don't expect a semicolon. This only happens in
+ // C, since definitions are not permitted in this context in C++.
+ if (TUK == Sema::TUK_Definition &&
+ (getLangOpts().CPlusPlus || !isTypeSpecifier(DSC)) &&
+ (TemplateInfo.Kind || !isValidAfterTypeSpecifier(false))) {
+ if (Tok.isNot(tok::semi)) {
+ const PrintingPolicy &PPol = Actions.getASTContext().getPrintingPolicy();
+ ExpectAndConsume(tok::semi, diag::err_expected_after,
+ DeclSpec::getSpecifierName(TagType, PPol));
+ // Push this token back into the preprocessor and change our current token
+ // to ';' so that the rest of the code recovers as though there were an
+ // ';' after the definition.
+ PP.EnterToken(Tok, /*IsReinject=*/true);
+ Tok.setKind(tok::semi);
+ }
+ }
+}
+
+/// ParseBaseClause - Parse the base-clause of a C++ class [C++ class.derived].
+///
+/// base-clause : [C++ class.derived]
+/// ':' base-specifier-list
+/// base-specifier-list:
+/// base-specifier '...'[opt]
+/// base-specifier-list ',' base-specifier '...'[opt]
+void Parser::ParseBaseClause(Decl *ClassDecl) {
+ assert(Tok.is(tok::colon) && "Not a base clause");
+ ConsumeToken();
+
+ // Build up an array of parsed base specifiers.
+ SmallVector<CXXBaseSpecifier *, 8> BaseInfo;
+
+ while (true) {
+ // Parse a base-specifier.
+ BaseResult Result = ParseBaseSpecifier(ClassDecl);
+ if (Result.isInvalid()) {
+ // Skip the rest of this base specifier, up until the comma or
+ // opening brace.
+ SkipUntil(tok::comma, tok::l_brace, StopAtSemi | StopBeforeMatch);
+ } else {
+ // Add this to our array of base specifiers.
+ BaseInfo.push_back(Result.get());
+ }
+
+ // If the next token is a comma, consume it and keep reading
+ // base-specifiers.
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ // Attach the base specifiers
+ Actions.ActOnBaseSpecifiers(ClassDecl, BaseInfo);
+}
+
+/// ParseBaseSpecifier - Parse a C++ base-specifier. A base-specifier is
+/// one entry in the base class list of a class specifier, for example:
+/// class foo : public bar, virtual private baz {
+/// 'public bar' and 'virtual private baz' are each base-specifiers.
+///
+/// base-specifier: [C++ class.derived]
+/// attribute-specifier-seq[opt] base-type-specifier
+/// attribute-specifier-seq[opt] 'virtual' access-specifier[opt]
+/// base-type-specifier
+/// attribute-specifier-seq[opt] access-specifier 'virtual'[opt]
+/// base-type-specifier
+BaseResult Parser::ParseBaseSpecifier(Decl *ClassDecl) {
+ bool IsVirtual = false;
+ SourceLocation StartLoc = Tok.getLocation();
+
+ ParsedAttributes Attributes(AttrFactory);
+ MaybeParseCXX11Attributes(Attributes);
+
+ // Parse the 'virtual' keyword.
+ if (TryConsumeToken(tok::kw_virtual))
+ IsVirtual = true;
+
+ CheckMisplacedCXX11Attribute(Attributes, StartLoc);
+
+ // Parse an (optional) access specifier.
+ AccessSpecifier Access = getAccessSpecifierIfPresent();
+ if (Access != AS_none) {
+ ConsumeToken();
+ if (getLangOpts().HLSL)
+ Diag(Tok.getLocation(), diag::ext_hlsl_access_specifiers);
+ }
+
+ CheckMisplacedCXX11Attribute(Attributes, StartLoc);
+
+ // Parse the 'virtual' keyword (again!), in case it came after the
+ // access specifier.
+ if (Tok.is(tok::kw_virtual)) {
+ SourceLocation VirtualLoc = ConsumeToken();
+ if (IsVirtual) {
+ // Complain about duplicate 'virtual'
+ Diag(VirtualLoc, diag::err_dup_virtual)
+ << FixItHint::CreateRemoval(VirtualLoc);
+ }
+
+ IsVirtual = true;
+ }
+
+ CheckMisplacedCXX11Attribute(Attributes, StartLoc);
+
+ // Parse the class-name.
+
+ // 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>. Treat '_Atomic' to be an identifier when we are
+ // parsing the class-name for a base specifier.
+ if (getLangOpts().MSVCCompat && Tok.is(tok::kw__Atomic) &&
+ NextToken().is(tok::less))
+ Tok.setKind(tok::identifier);
+
+ SourceLocation EndLocation;
+ SourceLocation BaseLoc;
+ TypeResult BaseType = ParseBaseTypeSpecifier(BaseLoc, EndLocation);
+ if (BaseType.isInvalid())
+ return true;
+
+ // Parse the optional ellipsis (for a pack expansion). The ellipsis is
+ // actually part of the base-specifier-list grammar productions, but we
+ // parse it here for convenience.
+ SourceLocation EllipsisLoc;
+ TryConsumeToken(tok::ellipsis, EllipsisLoc);
+
+ // Find the complete source range for the base-specifier.
+ SourceRange Range(StartLoc, EndLocation);
+
+ // Notify semantic analysis that we have parsed a complete
+ // base-specifier.
+ return Actions.ActOnBaseSpecifier(ClassDecl, Range, Attributes, IsVirtual,
+ Access, BaseType.get(), BaseLoc,
+ EllipsisLoc);
+}
+
+/// getAccessSpecifierIfPresent - Determine whether the next token is
+/// a C++ access-specifier.
+///
+/// access-specifier: [C++ class.derived]
+/// 'private'
+/// 'protected'
+/// 'public'
+AccessSpecifier Parser::getAccessSpecifierIfPresent() const {
+ switch (Tok.getKind()) {
+ default:
+ return AS_none;
+ case tok::kw_private:
+ return AS_private;
+ case tok::kw_protected:
+ return AS_protected;
+ case tok::kw_public:
+ return AS_public;
+ }
+}
+
+/// If the given declarator has any parts for which parsing has to be
+/// delayed, e.g., default arguments or an exception-specification, create a
+/// late-parsed method declaration record to handle the parsing at the end of
+/// the class definition.
+void Parser::HandleMemberFunctionDeclDelays(Declarator &DeclaratorInfo,
+ Decl *ThisDecl) {
+ DeclaratorChunk::FunctionTypeInfo &FTI = DeclaratorInfo.getFunctionTypeInfo();
+ // If there was a late-parsed exception-specification, we'll need a
+ // late parse
+ bool NeedLateParse = FTI.getExceptionSpecType() == EST_Unparsed;
+
+ if (!NeedLateParse) {
+ // Look ahead to see if there are any default args
+ for (unsigned ParamIdx = 0; ParamIdx < FTI.NumParams; ++ParamIdx) {
+ auto Param = cast<ParmVarDecl>(FTI.Params[ParamIdx].Param);
+ if (Param->hasUnparsedDefaultArg()) {
+ NeedLateParse = true;
+ break;
+ }
+ }
+ }
+
+ if (NeedLateParse) {
+ // Push this method onto the stack of late-parsed method
+ // declarations.
+ auto LateMethod = new LateParsedMethodDeclaration(this, ThisDecl);
+ getCurrentClass().LateParsedDeclarations.push_back(LateMethod);
+
+ // Push tokens for each parameter. Those that do not have defaults will be
+ // NULL. We need to track all the parameters so that we can push them into
+ // scope for later parameters and perhaps for the exception specification.
+ LateMethod->DefaultArgs.reserve(FTI.NumParams);
+ for (unsigned ParamIdx = 0; ParamIdx < FTI.NumParams; ++ParamIdx)
+ LateMethod->DefaultArgs.push_back(LateParsedDefaultArgument(
+ FTI.Params[ParamIdx].Param,
+ std::move(FTI.Params[ParamIdx].DefaultArgTokens)));
+
+ // Stash the exception-specification tokens in the late-pased method.
+ if (FTI.getExceptionSpecType() == EST_Unparsed) {
+ LateMethod->ExceptionSpecTokens = FTI.ExceptionSpecTokens;
+ FTI.ExceptionSpecTokens = nullptr;
+ }
+ }
+}
+
+/// isCXX11VirtSpecifier - Determine whether the given token is a C++11
+/// virt-specifier.
+///
+/// virt-specifier:
+/// override
+/// final
+/// __final
+VirtSpecifiers::Specifier Parser::isCXX11VirtSpecifier(const Token &Tok) const {
+ if (!getLangOpts().CPlusPlus || Tok.isNot(tok::identifier))
+ return VirtSpecifiers::VS_None;
+
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+
+ // Initialize the contextual keywords.
+ if (!Ident_final) {
+ Ident_final = &PP.getIdentifierTable().get("final");
+ if (getLangOpts().GNUKeywords)
+ Ident_GNU_final = &PP.getIdentifierTable().get("__final");
+ if (getLangOpts().MicrosoftExt) {
+ Ident_sealed = &PP.getIdentifierTable().get("sealed");
+ Ident_abstract = &PP.getIdentifierTable().get("abstract");
+ }
+ Ident_override = &PP.getIdentifierTable().get("override");
+ }
+
+ if (II == Ident_override)
+ return VirtSpecifiers::VS_Override;
+
+ if (II == Ident_sealed)
+ return VirtSpecifiers::VS_Sealed;
+
+ if (II == Ident_abstract)
+ return VirtSpecifiers::VS_Abstract;
+
+ if (II == Ident_final)
+ return VirtSpecifiers::VS_Final;
+
+ if (II == Ident_GNU_final)
+ return VirtSpecifiers::VS_GNU_Final;
+
+ return VirtSpecifiers::VS_None;
+}
+
+/// ParseOptionalCXX11VirtSpecifierSeq - Parse a virt-specifier-seq.
+///
+/// virt-specifier-seq:
+/// virt-specifier
+/// virt-specifier-seq virt-specifier
+void Parser::ParseOptionalCXX11VirtSpecifierSeq(VirtSpecifiers &VS,
+ bool IsInterface,
+ SourceLocation FriendLoc) {
+ while (true) {
+ VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier();
+ if (Specifier == VirtSpecifiers::VS_None)
+ return;
+
+ if (FriendLoc.isValid()) {
+ Diag(Tok.getLocation(), diag::err_friend_decl_spec)
+ << VirtSpecifiers::getSpecifierName(Specifier)
+ << FixItHint::CreateRemoval(Tok.getLocation())
+ << SourceRange(FriendLoc, FriendLoc);
+ ConsumeToken();
+ continue;
+ }
+
+ // C++ [class.mem]p8:
+ // A virt-specifier-seq shall contain at most one of each virt-specifier.
+ const char *PrevSpec = nullptr;
+ if (VS.SetSpecifier(Specifier, Tok.getLocation(), PrevSpec))
+ Diag(Tok.getLocation(), diag::err_duplicate_virt_specifier)
+ << PrevSpec << FixItHint::CreateRemoval(Tok.getLocation());
+
+ if (IsInterface && (Specifier == VirtSpecifiers::VS_Final ||
+ Specifier == VirtSpecifiers::VS_Sealed)) {
+ Diag(Tok.getLocation(), diag::err_override_control_interface)
+ << VirtSpecifiers::getSpecifierName(Specifier);
+ } else if (Specifier == VirtSpecifiers::VS_Sealed) {
+ Diag(Tok.getLocation(), diag::ext_ms_sealed_keyword);
+ } else if (Specifier == VirtSpecifiers::VS_Abstract) {
+ Diag(Tok.getLocation(), diag::ext_ms_abstract_keyword);
+ } else if (Specifier == VirtSpecifiers::VS_GNU_Final) {
+ Diag(Tok.getLocation(), diag::ext_warn_gnu_final);
+ } else {
+ Diag(Tok.getLocation(),
+ getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_override_control_keyword
+ : diag::ext_override_control_keyword)
+ << VirtSpecifiers::getSpecifierName(Specifier);
+ }
+ ConsumeToken();
+ }
+}
+
+/// isCXX11FinalKeyword - Determine whether the next token is a C++11
+/// 'final' or Microsoft 'sealed' contextual keyword.
+bool Parser::isCXX11FinalKeyword() const {
+ VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier();
+ return Specifier == VirtSpecifiers::VS_Final ||
+ Specifier == VirtSpecifiers::VS_GNU_Final ||
+ Specifier == VirtSpecifiers::VS_Sealed;
+}
+
+/// isClassCompatibleKeyword - Determine whether the next token is a C++11
+/// 'final' or Microsoft 'sealed' or 'abstract' contextual keywords.
+bool Parser::isClassCompatibleKeyword() const {
+ VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier();
+ return Specifier == VirtSpecifiers::VS_Final ||
+ Specifier == VirtSpecifiers::VS_GNU_Final ||
+ Specifier == VirtSpecifiers::VS_Sealed ||
+ Specifier == VirtSpecifiers::VS_Abstract;
+}
+
+/// Parse a C++ member-declarator up to, but not including, the optional
+/// brace-or-equal-initializer or pure-specifier.
+bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
+ Declarator &DeclaratorInfo, VirtSpecifiers &VS, ExprResult &BitfieldSize,
+ LateParsedAttrList &LateParsedAttrs) {
+ // member-declarator:
+ // declarator virt-specifier-seq[opt] pure-specifier[opt]
+ // declarator requires-clause
+ // declarator brace-or-equal-initializer[opt]
+ // identifier attribute-specifier-seq[opt] ':' constant-expression
+ // brace-or-equal-initializer[opt]
+ // ':' constant-expression
+ //
+ // NOTE: the latter two productions are a proposed bugfix rather than the
+ // current grammar rules as of C++20.
+ if (Tok.isNot(tok::colon))
+ ParseDeclarator(DeclaratorInfo);
+ else
+ DeclaratorInfo.SetIdentifier(nullptr, Tok.getLocation());
+
+ if (!DeclaratorInfo.isFunctionDeclarator() && TryConsumeToken(tok::colon)) {
+ assert(DeclaratorInfo.isPastIdentifier() &&
+ "don't know where identifier would go yet?");
+ BitfieldSize = ParseConstantExpression();
+ if (BitfieldSize.isInvalid())
+ SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
+ } else if (Tok.is(tok::kw_requires)) {
+ ParseTrailingRequiresClause(DeclaratorInfo);
+ } else {
+ ParseOptionalCXX11VirtSpecifierSeq(
+ VS, getCurrentClass().IsInterface,
+ DeclaratorInfo.getDeclSpec().getFriendSpecLoc());
+ if (!VS.isUnset())
+ MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(DeclaratorInfo,
+ VS);
+ }
+
+ // If a simple-asm-expr is present, parse it.
+ if (Tok.is(tok::kw_asm)) {
+ SourceLocation Loc;
+ ExprResult AsmLabel(ParseSimpleAsm(/*ForAsmLabel*/ true, &Loc));
+ if (AsmLabel.isInvalid())
+ SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
+
+ DeclaratorInfo.setAsmLabel(AsmLabel.get());
+ DeclaratorInfo.SetRangeEnd(Loc);
+ }
+
+ // If attributes exist after the declarator, but before an '{', parse them.
+ // However, this does not apply for [[]] attributes (which could show up
+ // before or after the __attribute__ attributes).
+ DiagnoseAndSkipCXX11Attributes();
+ MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs);
+ DiagnoseAndSkipCXX11Attributes();
+
+ // For compatibility with code written to older Clang, also accept a
+ // virt-specifier *after* the GNU attributes.
+ if (BitfieldSize.isUnset() && VS.isUnset()) {
+ ParseOptionalCXX11VirtSpecifierSeq(
+ VS, getCurrentClass().IsInterface,
+ DeclaratorInfo.getDeclSpec().getFriendSpecLoc());
+ if (!VS.isUnset()) {
+ // If we saw any GNU-style attributes that are known to GCC followed by a
+ // virt-specifier, issue a GCC-compat warning.
+ for (const ParsedAttr &AL : DeclaratorInfo.getAttributes())
+ if (AL.isKnownToGCC() && !AL.isCXX11Attribute())
+ Diag(AL.getLoc(), diag::warn_gcc_attribute_location);
+
+ MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(DeclaratorInfo,
+ VS);
+ }
+ }
+
+ // If this has neither a name nor a bit width, something has gone seriously
+ // wrong. Skip until the semi-colon or }.
+ if (!DeclaratorInfo.hasName() && BitfieldSize.isUnset()) {
+ // If so, skip until the semi-colon or a }.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ return true;
+ }
+ return false;
+}
+
+/// Look for declaration specifiers possibly occurring after C++11
+/// virt-specifier-seq and diagnose them.
+void Parser::MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(
+ Declarator &D, VirtSpecifiers &VS) {
+ DeclSpec DS(AttrFactory);
+
+ // GNU-style and C++11 attributes are not allowed here, but they will be
+ // handled by the caller. Diagnose everything else.
+ ParseTypeQualifierListOpt(
+ DS, AR_NoAttributesParsed, false,
+ /*IdentifierRequired=*/false, llvm::function_ref<void()>([&]() {
+ Actions.CodeCompleteFunctionQualifiers(DS, D, &VS);
+ }));
+ D.ExtendWithDeclSpec(DS);
+
+ if (D.isFunctionDeclarator()) {
+ auto &Function = D.getFunctionTypeInfo();
+ if (DS.getTypeQualifiers() != DeclSpec::TQ_unspecified) {
+ auto DeclSpecCheck = [&](DeclSpec::TQ TypeQual, StringRef FixItName,
+ SourceLocation SpecLoc) {
+ FixItHint Insertion;
+ auto &MQ = Function.getOrCreateMethodQualifiers();
+ if (!(MQ.getTypeQualifiers() & TypeQual)) {
+ std::string Name(FixItName.data());
+ Name += " ";
+ Insertion = FixItHint::CreateInsertion(VS.getFirstLocation(), Name);
+ MQ.SetTypeQual(TypeQual, SpecLoc);
+ }
+ Diag(SpecLoc, diag::err_declspec_after_virtspec)
+ << FixItName
+ << VirtSpecifiers::getSpecifierName(VS.getLastSpecifier())
+ << FixItHint::CreateRemoval(SpecLoc) << Insertion;
+ };
+ DS.forEachQualifier(DeclSpecCheck);
+ }
+
+ // Parse ref-qualifiers.
+ bool RefQualifierIsLValueRef = true;
+ SourceLocation RefQualifierLoc;
+ if (ParseRefQualifier(RefQualifierIsLValueRef, RefQualifierLoc)) {
+ const char *Name = (RefQualifierIsLValueRef ? "& " : "&& ");
+ FixItHint Insertion =
+ FixItHint::CreateInsertion(VS.getFirstLocation(), Name);
+ Function.RefQualifierIsLValueRef = RefQualifierIsLValueRef;
+ Function.RefQualifierLoc = RefQualifierLoc;
+
+ Diag(RefQualifierLoc, diag::err_declspec_after_virtspec)
+ << (RefQualifierIsLValueRef ? "&" : "&&")
+ << VirtSpecifiers::getSpecifierName(VS.getLastSpecifier())
+ << FixItHint::CreateRemoval(RefQualifierLoc) << Insertion;
+ D.SetRangeEnd(RefQualifierLoc);
+ }
+ }
+}
+
+/// ParseCXXClassMemberDeclaration - Parse a C++ class member declaration.
+///
+/// member-declaration:
+/// decl-specifier-seq[opt] member-declarator-list[opt] ';'
+/// function-definition ';'[opt]
+/// ::[opt] nested-name-specifier template[opt] unqualified-id ';'[TODO]
+/// using-declaration [TODO]
+/// [C++0x] static_assert-declaration
+/// template-declaration
+/// [GNU] '__extension__' member-declaration
+///
+/// member-declarator-list:
+/// member-declarator
+/// member-declarator-list ',' member-declarator
+///
+/// member-declarator:
+/// declarator virt-specifier-seq[opt] pure-specifier[opt]
+/// [C++2a] declarator requires-clause
+/// declarator constant-initializer[opt]
+/// [C++11] declarator brace-or-equal-initializer[opt]
+/// identifier[opt] ':' constant-expression
+///
+/// virt-specifier-seq:
+/// virt-specifier
+/// virt-specifier-seq virt-specifier
+///
+/// virt-specifier:
+/// override
+/// final
+/// [MS] sealed
+///
+/// pure-specifier:
+/// '= 0'
+///
+/// constant-initializer:
+/// '=' constant-expression
+///
+Parser::DeclGroupPtrTy
+Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
+ ParsedAttributes &AccessAttrs,
+ const ParsedTemplateInfo &TemplateInfo,
+ ParsingDeclRAIIObject *TemplateDiags) {
+ if (Tok.is(tok::at)) {
+ if (getLangOpts().ObjC && NextToken().isObjCAtKeyword(tok::objc_defs))
+ Diag(Tok, diag::err_at_defs_cxx);
+ else
+ Diag(Tok, diag::err_at_in_class);
+
+ ConsumeToken();
+ SkipUntil(tok::r_brace, StopAtSemi);
+ return nullptr;
+ }
+
+ // Turn on colon protection early, while parsing declspec, although there is
+ // nothing to protect there. It prevents from false errors if error recovery
+ // incorrectly determines where the declspec ends, as in the example:
+ // struct A { enum class B { C }; };
+ // const int C = 4;
+ // struct D { A::B : C; };
+ ColonProtectionRAIIObject X(*this);
+
+ // Access declarations.
+ bool MalformedTypeSpec = false;
+ if (!TemplateInfo.Kind &&
+ Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw___super)) {
+ if (TryAnnotateCXXScopeToken())
+ MalformedTypeSpec = true;
+
+ bool isAccessDecl;
+ if (Tok.isNot(tok::annot_cxxscope))
+ isAccessDecl = false;
+ else if (NextToken().is(tok::identifier))
+ isAccessDecl = GetLookAheadToken(2).is(tok::semi);
+ else
+ isAccessDecl = NextToken().is(tok::kw_operator);
+
+ if (isAccessDecl) {
+ // Collect the scope specifier token we annotated earlier.
+ CXXScopeSpec SS;
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false);
+
+ if (SS.isInvalid()) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ // Try to parse an unqualified-id.
+ SourceLocation TemplateKWLoc;
+ UnqualifiedId Name;
+ if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, false, true, true,
+ false, &TemplateKWLoc, Name)) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ // TODO: recover from mistakenly-qualified operator declarations.
+ if (ExpectAndConsume(tok::semi, diag::err_expected_after,
+ "access declaration")) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ // FIXME: We should do something with the 'template' keyword here.
+ return DeclGroupPtrTy::make(DeclGroupRef(Actions.ActOnUsingDeclaration(
+ getCurScope(), AS, /*UsingLoc*/ SourceLocation(),
+ /*TypenameLoc*/ SourceLocation(), SS, Name,
+ /*EllipsisLoc*/ SourceLocation(),
+ /*AttrList*/ ParsedAttributesView())));
+ }
+ }
+
+ // static_assert-declaration. A templated static_assert declaration is
+ // diagnosed in Parser::ParseSingleDeclarationAfterTemplate.
+ if (!TemplateInfo.Kind &&
+ Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
+ SourceLocation DeclEnd;
+ return DeclGroupPtrTy::make(
+ DeclGroupRef(ParseStaticAssertDeclaration(DeclEnd)));
+ }
+
+ if (Tok.is(tok::kw_template)) {
+ assert(!TemplateInfo.TemplateParams &&
+ "Nested template improperly parsed?");
+ ObjCDeclContextSwitch ObjCDC(*this);
+ SourceLocation DeclEnd;
+ return DeclGroupPtrTy::make(
+ DeclGroupRef(ParseTemplateDeclarationOrSpecialization(
+ DeclaratorContext::Member, DeclEnd, AccessAttrs, AS)));
+ }
+
+ // Handle: member-declaration ::= '__extension__' member-declaration
+ if (Tok.is(tok::kw___extension__)) {
+ // __extension__ silences extension warnings in the subexpression.
+ ExtensionRAIIObject O(Diags); // Use RAII to do this.
+ ConsumeToken();
+ return ParseCXXClassMemberDeclaration(AS, AccessAttrs, TemplateInfo,
+ TemplateDiags);
+ }
+
+ ParsedAttributes DeclAttrs(AttrFactory);
+ // Optional C++11 attribute-specifier
+ MaybeParseCXX11Attributes(DeclAttrs);
+
+ // The next token may be an OpenMP pragma annotation token. That would
+ // normally be handled from ParseCXXClassMemberDeclarationWithPragmas, but in
+ // this case, it came from an *attribute* rather than a pragma. Handle it now.
+ if (Tok.is(tok::annot_attr_openmp))
+ return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, DeclAttrs);
+
+ if (Tok.is(tok::kw_using)) {
+ // Eat 'using'.
+ SourceLocation UsingLoc = ConsumeToken();
+
+ // Consume unexpected 'template' keywords.
+ while (Tok.is(tok::kw_template)) {
+ SourceLocation TemplateLoc = ConsumeToken();
+ Diag(TemplateLoc, diag::err_unexpected_template_after_using)
+ << FixItHint::CreateRemoval(TemplateLoc);
+ }
+
+ if (Tok.is(tok::kw_namespace)) {
+ Diag(UsingLoc, diag::err_using_namespace_in_class);
+ SkipUntil(tok::semi, StopBeforeMatch);
+ return nullptr;
+ }
+ SourceLocation DeclEnd;
+ // Otherwise, it must be a using-declaration or an alias-declaration.
+ return ParseUsingDeclaration(DeclaratorContext::Member, TemplateInfo,
+ UsingLoc, DeclEnd, DeclAttrs, AS);
+ }
+
+ ParsedAttributes DeclSpecAttrs(AttrFactory);
+ MaybeParseMicrosoftAttributes(DeclSpecAttrs);
+
+ // Hold late-parsed attributes so we can attach a Decl to them later.
+ LateParsedAttrList CommonLateParsedAttrs;
+
+ // decl-specifier-seq:
+ // Parse the common declaration-specifiers piece.
+ ParsingDeclSpec DS(*this, TemplateDiags);
+ DS.takeAttributesFrom(DeclSpecAttrs);
+
+ if (MalformedTypeSpec)
+ DS.SetTypeSpecError();
+
+ // Turn off usual access checking for templates explicit specialization
+ // and instantiation.
+ // C++20 [temp.spec] 13.9/6.
+ // This disables the access checking rules for member function template
+ // explicit instantiation and explicit specialization.
+ bool IsTemplateSpecOrInst =
+ (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
+ TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+ SuppressAccessChecks diagsFromTag(*this, IsTemplateSpecOrInst);
+
+ ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DeclSpecContext::DSC_class,
+ &CommonLateParsedAttrs);
+
+ if (IsTemplateSpecOrInst)
+ diagsFromTag.done();
+
+ // Turn off colon protection that was set for declspec.
+ X.restore();
+
+ // If we had a free-standing type definition with a missing semicolon, we
+ // may get this far before the problem becomes obvious.
+ if (DS.hasTagDefinition() &&
+ TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate &&
+ DiagnoseMissingSemiAfterTagDefinition(DS, AS, DeclSpecContext::DSC_class,
+ &CommonLateParsedAttrs))
+ return nullptr;
+
+ MultiTemplateParamsArg TemplateParams(
+ TemplateInfo.TemplateParams ? TemplateInfo.TemplateParams->data()
+ : nullptr,
+ TemplateInfo.TemplateParams ? TemplateInfo.TemplateParams->size() : 0);
+
+ if (TryConsumeToken(tok::semi)) {
+ if (DS.isFriendSpecified())
+ ProhibitAttributes(DeclAttrs);
+
+ RecordDecl *AnonRecord = nullptr;
+ Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(
+ getCurScope(), AS, DS, DeclAttrs, TemplateParams, false, AnonRecord);
+ DS.complete(TheDecl);
+ if (AnonRecord) {
+ Decl *decls[] = {AnonRecord, TheDecl};
+ return Actions.BuildDeclaratorGroup(decls);
+ }
+ return Actions.ConvertDeclToDeclGroup(TheDecl);
+ }
+
+ ParsingDeclarator DeclaratorInfo(*this, DS, DeclAttrs,
+ DeclaratorContext::Member);
+ if (TemplateInfo.TemplateParams)
+ DeclaratorInfo.setTemplateParameterLists(TemplateParams);
+ VirtSpecifiers VS;
+
+ // Hold late-parsed attributes so we can attach a Decl to them later.
+ LateParsedAttrList LateParsedAttrs;
+
+ SourceLocation EqualLoc;
+ SourceLocation PureSpecLoc;
+
+ auto TryConsumePureSpecifier = [&](bool AllowDefinition) {
+ if (Tok.isNot(tok::equal))
+ return false;
+
+ auto &Zero = NextToken();
+ SmallString<8> Buffer;
+ if (Zero.isNot(tok::numeric_constant) ||
+ PP.getSpelling(Zero, Buffer) != "0")
+ return false;
+
+ auto &After = GetLookAheadToken(2);
+ if (!After.isOneOf(tok::semi, tok::comma) &&
+ !(AllowDefinition &&
+ After.isOneOf(tok::l_brace, tok::colon, tok::kw_try)))
+ return false;
+
+ EqualLoc = ConsumeToken();
+ PureSpecLoc = ConsumeToken();
+ return true;
+ };
+
+ SmallVector<Decl *, 8> DeclsInGroup;
+ ExprResult BitfieldSize;
+ ExprResult TrailingRequiresClause;
+ bool ExpectSemi = true;
+
+ // C++20 [temp.spec] 13.9/6.
+ // This disables the access checking rules for member function template
+ // explicit instantiation and explicit specialization.
+ SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
+
+ // Parse the first declarator.
+ if (ParseCXXMemberDeclaratorBeforeInitializer(
+ DeclaratorInfo, VS, BitfieldSize, LateParsedAttrs)) {
+ TryConsumeToken(tok::semi);
+ return nullptr;
+ }
+
+ if (IsTemplateSpecOrInst)
+ SAC.done();
+
+ // Check for a member function definition.
+ if (BitfieldSize.isUnset()) {
+ // MSVC permits pure specifier on inline functions defined at class scope.
+ // Hence check for =0 before checking for function definition.
+ if (getLangOpts().MicrosoftExt && DeclaratorInfo.isDeclarationOfFunction())
+ TryConsumePureSpecifier(/*AllowDefinition*/ true);
+
+ FunctionDefinitionKind DefinitionKind = FunctionDefinitionKind::Declaration;
+ // function-definition:
+ //
+ // In C++11, a non-function declarator followed by an open brace is a
+ // braced-init-list for an in-class member initialization, not an
+ // erroneous function definition.
+ if (Tok.is(tok::l_brace) && !getLangOpts().CPlusPlus11) {
+ DefinitionKind = FunctionDefinitionKind::Definition;
+ } else if (DeclaratorInfo.isFunctionDeclarator()) {
+ if (Tok.isOneOf(tok::l_brace, tok::colon, tok::kw_try)) {
+ DefinitionKind = FunctionDefinitionKind::Definition;
+ } else if (Tok.is(tok::equal)) {
+ const Token &KW = NextToken();
+ if (KW.is(tok::kw_default))
+ DefinitionKind = FunctionDefinitionKind::Defaulted;
+ else if (KW.is(tok::kw_delete))
+ DefinitionKind = FunctionDefinitionKind::Deleted;
+ else if (KW.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteAfterFunctionEquals(DeclaratorInfo);
+ return nullptr;
+ }
+ }
+ }
+ DeclaratorInfo.setFunctionDefinitionKind(DefinitionKind);
+
+ // C++11 [dcl.attr.grammar] p4: If an attribute-specifier-seq appertains
+ // to a friend declaration, that declaration shall be a definition.
+ if (DeclaratorInfo.isFunctionDeclarator() &&
+ DefinitionKind == FunctionDefinitionKind::Declaration &&
+ DS.isFriendSpecified()) {
+ // Diagnose attributes that appear before decl specifier:
+ // [[]] friend int foo();
+ ProhibitAttributes(DeclAttrs);
+ }
+
+ if (DefinitionKind != FunctionDefinitionKind::Declaration) {
+ if (!DeclaratorInfo.isFunctionDeclarator()) {
+ Diag(DeclaratorInfo.getIdentifierLoc(), diag::err_func_def_no_params);
+ ConsumeBrace();
+ SkipUntil(tok::r_brace);
+
+ // Consume the optional ';'
+ TryConsumeToken(tok::semi);
+
+ return nullptr;
+ }
+
+ if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
+ Diag(DeclaratorInfo.getIdentifierLoc(),
+ diag::err_function_declared_typedef);
+
+ // Recover by treating the 'typedef' as spurious.
+ DS.ClearStorageClassSpecs();
+ }
+
+ Decl *FunDecl = ParseCXXInlineMethodDef(AS, AccessAttrs, DeclaratorInfo,
+ TemplateInfo, VS, PureSpecLoc);
+
+ if (FunDecl) {
+ for (unsigned i = 0, ni = CommonLateParsedAttrs.size(); i < ni; ++i) {
+ CommonLateParsedAttrs[i]->addDecl(FunDecl);
+ }
+ for (unsigned i = 0, ni = LateParsedAttrs.size(); i < ni; ++i) {
+ LateParsedAttrs[i]->addDecl(FunDecl);
+ }
+ }
+ LateParsedAttrs.clear();
+
+ // Consume the ';' - it's optional unless we have a delete or default
+ if (Tok.is(tok::semi))
+ ConsumeExtraSemi(AfterMemberFunctionDefinition);
+
+ return DeclGroupPtrTy::make(DeclGroupRef(FunDecl));
+ }
+ }
+
+ // member-declarator-list:
+ // member-declarator
+ // member-declarator-list ',' member-declarator
+
+ while (true) {
+ InClassInitStyle HasInClassInit = ICIS_NoInit;
+ bool HasStaticInitializer = false;
+ if (Tok.isOneOf(tok::equal, tok::l_brace) && PureSpecLoc.isInvalid()) {
+ // DRXXXX: Anonymous bit-fields cannot have a brace-or-equal-initializer.
+ if (BitfieldSize.isUsable() && !DeclaratorInfo.hasName()) {
+ // Diagnose the error and pretend there is no in-class initializer.
+ Diag(Tok, diag::err_anon_bitfield_member_init);
+ SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
+ } else if (DeclaratorInfo.isDeclarationOfFunction()) {
+ // It's a pure-specifier.
+ if (!TryConsumePureSpecifier(/*AllowFunctionDefinition*/ false))
+ // Parse it as an expression so that Sema can diagnose it.
+ HasStaticInitializer = true;
+ } else if (DeclaratorInfo.getDeclSpec().getStorageClassSpec() !=
+ DeclSpec::SCS_static &&
+ DeclaratorInfo.getDeclSpec().getStorageClassSpec() !=
+ DeclSpec::SCS_typedef &&
+ !DS.isFriendSpecified()) {
+ // It's a default member initializer.
+ if (BitfieldSize.get())
+ Diag(Tok, getLangOpts().CPlusPlus20
+ ? diag::warn_cxx17_compat_bitfield_member_init
+ : diag::ext_bitfield_member_init);
+ HasInClassInit = Tok.is(tok::equal) ? ICIS_CopyInit : ICIS_ListInit;
+ } else {
+ HasStaticInitializer = true;
+ }
+ }
+
+ // NOTE: If Sema is the Action module and declarator is an instance field,
+ // this call will *not* return the created decl; It will return null.
+ // See Sema::ActOnCXXMemberDeclarator for details.
+
+ NamedDecl *ThisDecl = nullptr;
+ if (DS.isFriendSpecified()) {
+ // C++11 [dcl.attr.grammar] p4: If an attribute-specifier-seq appertains
+ // to a friend declaration, that declaration shall be a definition.
+ //
+ // Diagnose attributes that appear in a friend member function declarator:
+ // friend int foo [[]] ();
+ SmallVector<SourceRange, 4> Ranges;
+ DeclaratorInfo.getCXX11AttributeRanges(Ranges);
+ for (SmallVectorImpl<SourceRange>::iterator I = Ranges.begin(),
+ E = Ranges.end();
+ I != E; ++I)
+ Diag((*I).getBegin(), diag::err_attributes_not_allowed) << *I;
+
+ ThisDecl = Actions.ActOnFriendFunctionDecl(getCurScope(), DeclaratorInfo,
+ TemplateParams);
+ } else {
+ ThisDecl = Actions.ActOnCXXMemberDeclarator(
+ getCurScope(), AS, DeclaratorInfo, TemplateParams, BitfieldSize.get(),
+ VS, HasInClassInit);
+
+ if (VarTemplateDecl *VT =
+ ThisDecl ? dyn_cast<VarTemplateDecl>(ThisDecl) : nullptr)
+ // Re-direct this decl to refer to the templated decl so that we can
+ // initialize it.
+ ThisDecl = VT->getTemplatedDecl();
+
+ if (ThisDecl)
+ Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs);
+ }
+
+ // Error recovery might have converted a non-static member into a static
+ // member.
+ if (HasInClassInit != ICIS_NoInit &&
+ DeclaratorInfo.getDeclSpec().getStorageClassSpec() ==
+ DeclSpec::SCS_static) {
+ HasInClassInit = ICIS_NoInit;
+ HasStaticInitializer = true;
+ }
+
+ if (PureSpecLoc.isValid() && VS.getAbstractLoc().isValid()) {
+ Diag(PureSpecLoc, diag::err_duplicate_virt_specifier) << "abstract";
+ }
+ if (ThisDecl && PureSpecLoc.isValid())
+ Actions.ActOnPureSpecifier(ThisDecl, PureSpecLoc);
+ else if (ThisDecl && VS.getAbstractLoc().isValid())
+ Actions.ActOnPureSpecifier(ThisDecl, VS.getAbstractLoc());
+
+ // Handle the initializer.
+ if (HasInClassInit != ICIS_NoInit) {
+ // The initializer was deferred; parse it and cache the tokens.
+ Diag(Tok, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_nonstatic_member_init
+ : diag::ext_nonstatic_member_init);
+
+ if (DeclaratorInfo.isArrayOfUnknownBound()) {
+ // C++11 [dcl.array]p3: An array bound may also be omitted when the
+ // declarator is followed by an initializer.
+ //
+ // A brace-or-equal-initializer for a member-declarator is not an
+ // initializer in the grammar, so this is ill-formed.
+ Diag(Tok, diag::err_incomplete_array_member_init);
+ SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
+
+ // Avoid later warnings about a class member of incomplete type.
+ if (ThisDecl)
+ ThisDecl->setInvalidDecl();
+ } else
+ ParseCXXNonStaticMemberInitializer(ThisDecl);
+ } else if (HasStaticInitializer) {
+ // Normal initializer.
+ ExprResult Init = ParseCXXMemberInitializer(
+ ThisDecl, DeclaratorInfo.isDeclarationOfFunction(), EqualLoc);
+
+ if (Init.isInvalid()) {
+ if (ThisDecl)
+ Actions.ActOnUninitializedDecl(ThisDecl);
+ SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
+ } else if (ThisDecl)
+ Actions.AddInitializerToDecl(ThisDecl, Init.get(),
+ EqualLoc.isInvalid());
+ } else if (ThisDecl && DS.getStorageClassSpec() == DeclSpec::SCS_static)
+ // No initializer.
+ Actions.ActOnUninitializedDecl(ThisDecl);
+
+ if (ThisDecl) {
+ if (!ThisDecl->isInvalidDecl()) {
+ // Set the Decl for any late parsed attributes
+ for (unsigned i = 0, ni = CommonLateParsedAttrs.size(); i < ni; ++i)
+ CommonLateParsedAttrs[i]->addDecl(ThisDecl);
+
+ for (unsigned i = 0, ni = LateParsedAttrs.size(); i < ni; ++i)
+ LateParsedAttrs[i]->addDecl(ThisDecl);
+ }
+ Actions.FinalizeDeclaration(ThisDecl);
+ DeclsInGroup.push_back(ThisDecl);
+
+ if (DeclaratorInfo.isFunctionDeclarator() &&
+ DeclaratorInfo.getDeclSpec().getStorageClassSpec() !=
+ DeclSpec::SCS_typedef)
+ HandleMemberFunctionDeclDelays(DeclaratorInfo, ThisDecl);
+ }
+ LateParsedAttrs.clear();
+
+ DeclaratorInfo.complete(ThisDecl);
+
+ // If we don't have a comma, it is either the end of the list (a ';')
+ // or an error, bail out.
+ SourceLocation CommaLoc;
+ if (!TryConsumeToken(tok::comma, CommaLoc))
+ break;
+
+ if (Tok.isAtStartOfLine() &&
+ !MightBeDeclarator(DeclaratorContext::Member)) {
+ // This comma was followed by a line-break and something which can't be
+ // the start of a declarator. The comma was probably a typo for a
+ // semicolon.
+ Diag(CommaLoc, diag::err_expected_semi_declaration)
+ << FixItHint::CreateReplacement(CommaLoc, ";");
+ ExpectSemi = false;
+ break;
+ }
+
+ // Parse the next declarator.
+ DeclaratorInfo.clear();
+ VS.clear();
+ BitfieldSize = ExprResult(/*Invalid=*/false);
+ EqualLoc = PureSpecLoc = SourceLocation();
+ DeclaratorInfo.setCommaLoc(CommaLoc);
+
+ // GNU attributes are allowed before the second and subsequent declarator.
+ // However, this does not apply for [[]] attributes (which could show up
+ // before or after the __attribute__ attributes).
+ DiagnoseAndSkipCXX11Attributes();
+ MaybeParseGNUAttributes(DeclaratorInfo);
+ DiagnoseAndSkipCXX11Attributes();
+
+ if (ParseCXXMemberDeclaratorBeforeInitializer(
+ DeclaratorInfo, VS, BitfieldSize, LateParsedAttrs))
+ break;
+ }
+
+ if (ExpectSemi &&
+ ExpectAndConsume(tok::semi, diag::err_expected_semi_decl_list)) {
+ // Skip to end of block or statement.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ // If we stopped at a ';', eat it.
+ TryConsumeToken(tok::semi);
+ return nullptr;
+ }
+
+ return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, DeclsInGroup);
+}
+
+/// ParseCXXMemberInitializer - Parse the brace-or-equal-initializer.
+/// Also detect and reject any attempted defaulted/deleted function definition.
+/// The location of the '=', if any, will be placed in EqualLoc.
+///
+/// This does not check for a pure-specifier; that's handled elsewhere.
+///
+/// brace-or-equal-initializer:
+/// '=' initializer-expression
+/// braced-init-list
+///
+/// initializer-clause:
+/// assignment-expression
+/// braced-init-list
+///
+/// defaulted/deleted function-definition:
+/// '=' 'default'
+/// '=' 'delete'
+///
+/// Prior to C++0x, the assignment-expression in an initializer-clause must
+/// be a constant-expression.
+ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction,
+ SourceLocation &EqualLoc) {
+ assert(Tok.isOneOf(tok::equal, tok::l_brace) &&
+ "Data member initializer not starting with '=' or '{'");
+
+ EnterExpressionEvaluationContext Context(
+ Actions,
+ isa_and_present<FieldDecl>(D)
+ ? Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed
+ : Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+ D);
+ if (TryConsumeToken(tok::equal, EqualLoc)) {
+ if (Tok.is(tok::kw_delete)) {
+ // In principle, an initializer of '= delete p;' is legal, but it will
+ // never type-check. It's better to diagnose it as an ill-formed
+ // expression than as an ill-formed deleted non-function member. An
+ // initializer of '= delete p, foo' will never be parsed, because a
+ // top-level comma always ends the initializer expression.
+ const Token &Next = NextToken();
+ if (IsFunction || Next.isOneOf(tok::semi, tok::comma, tok::eof)) {
+ if (IsFunction)
+ Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
+ << 1 /* delete */;
+ else
+ Diag(ConsumeToken(), diag::err_deleted_non_function);
+ return ExprError();
+ }
+ } else if (Tok.is(tok::kw_default)) {
+ if (IsFunction)
+ Diag(Tok, diag::err_default_delete_in_multiple_declaration)
+ << 0 /* default */;
+ else
+ Diag(ConsumeToken(), diag::err_default_special_members)
+ << getLangOpts().CPlusPlus20;
+ return ExprError();
+ }
+ }
+ if (const auto *PD = dyn_cast_or_null<MSPropertyDecl>(D)) {
+ Diag(Tok, diag::err_ms_property_initializer) << PD;
+ return ExprError();
+ }
+ return ParseInitializer();
+}
+
+void Parser::SkipCXXMemberSpecification(SourceLocation RecordLoc,
+ SourceLocation AttrFixitLoc,
+ unsigned TagType, Decl *TagDecl) {
+ // Skip the optional 'final' keyword.
+ if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) {
+ assert(isCXX11FinalKeyword() && "not a class definition");
+ ConsumeToken();
+
+ // Diagnose any C++11 attributes after 'final' keyword.
+ // We deliberately discard these attributes.
+ ParsedAttributes Attrs(AttrFactory);
+ CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc);
+
+ // This can only happen if we had malformed misplaced attributes;
+ // we only get called if there is a colon or left-brace after the
+ // attributes.
+ if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_brace))
+ return;
+ }
+
+ // Skip the base clauses. This requires actually parsing them, because
+ // otherwise we can't be sure where they end (a left brace may appear
+ // within a template argument).
+ if (Tok.is(tok::colon)) {
+ // Enter the scope of the class so that we can correctly parse its bases.
+ ParseScope ClassScope(this, Scope::ClassScope | Scope::DeclScope);
+ ParsingClassDefinition ParsingDef(*this, TagDecl, /*NonNestedClass*/ true,
+ TagType == DeclSpec::TST_interface);
+ auto OldContext =
+ Actions.ActOnTagStartSkippedDefinition(getCurScope(), TagDecl);
+
+ // Parse the bases but don't attach them to the class.
+ ParseBaseClause(nullptr);
+
+ Actions.ActOnTagFinishSkippedDefinition(OldContext);
+
+ if (!Tok.is(tok::l_brace)) {
+ Diag(PP.getLocForEndOfToken(PrevTokLocation),
+ diag::err_expected_lbrace_after_base_specifiers);
+ return;
+ }
+ }
+
+ // Skip the body.
+ assert(Tok.is(tok::l_brace));
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+ T.skipToEnd();
+
+ // Parse and discard any trailing attributes.
+ if (Tok.is(tok::kw___attribute)) {
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseGNUAttributes(Attrs);
+ }
+}
+
+Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclarationWithPragmas(
+ AccessSpecifier &AS, ParsedAttributes &AccessAttrs, DeclSpec::TST TagType,
+ Decl *TagDecl) {
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ switch (Tok.getKind()) {
+ case tok::kw___if_exists:
+ case tok::kw___if_not_exists:
+ ParseMicrosoftIfExistsClassDeclaration(TagType, AccessAttrs, AS);
+ return nullptr;
+
+ case tok::semi:
+ // Check for extraneous top-level semicolon.
+ ConsumeExtraSemi(InsideStruct, TagType);
+ return nullptr;
+
+ // Handle pragmas that can appear as member declarations.
+ case tok::annot_pragma_vis:
+ HandlePragmaVisibility();
+ return nullptr;
+ case tok::annot_pragma_pack:
+ HandlePragmaPack();
+ return nullptr;
+ case tok::annot_pragma_align:
+ HandlePragmaAlign();
+ return nullptr;
+ case tok::annot_pragma_ms_pointers_to_members:
+ HandlePragmaMSPointersToMembers();
+ return nullptr;
+ case tok::annot_pragma_ms_pragma:
+ HandlePragmaMSPragma();
+ return nullptr;
+ case tok::annot_pragma_ms_vtordisp:
+ HandlePragmaMSVtorDisp();
+ return nullptr;
+ case tok::annot_pragma_dump:
+ HandlePragmaDump();
+ return nullptr;
+
+ case tok::kw_namespace:
+ // If we see a namespace here, a close brace was missing somewhere.
+ DiagnoseUnexpectedNamespace(cast<NamedDecl>(TagDecl));
+ return nullptr;
+
+ case tok::kw_private:
+ // FIXME: We don't accept GNU attributes on access specifiers in OpenCL mode
+ // yet.
+ if (getLangOpts().OpenCL && !NextToken().is(tok::colon))
+ return ParseCXXClassMemberDeclaration(AS, AccessAttrs);
+ [[fallthrough]];
+ case tok::kw_public:
+ case tok::kw_protected: {
+ if (getLangOpts().HLSL)
+ Diag(Tok.getLocation(), diag::ext_hlsl_access_specifiers);
+ AccessSpecifier NewAS = getAccessSpecifierIfPresent();
+ assert(NewAS != AS_none);
+ // Current token is a C++ access specifier.
+ AS = NewAS;
+ SourceLocation ASLoc = Tok.getLocation();
+ unsigned TokLength = Tok.getLength();
+ ConsumeToken();
+ AccessAttrs.clear();
+ MaybeParseGNUAttributes(AccessAttrs);
+
+ SourceLocation EndLoc;
+ if (TryConsumeToken(tok::colon, EndLoc)) {
+ } else if (TryConsumeToken(tok::semi, EndLoc)) {
+ Diag(EndLoc, diag::err_expected)
+ << tok::colon << FixItHint::CreateReplacement(EndLoc, ":");
+ } else {
+ EndLoc = ASLoc.getLocWithOffset(TokLength);
+ Diag(EndLoc, diag::err_expected)
+ << tok::colon << FixItHint::CreateInsertion(EndLoc, ":");
+ }
+
+ // The Microsoft extension __interface does not permit non-public
+ // access specifiers.
+ if (TagType == DeclSpec::TST_interface && AS != AS_public) {
+ Diag(ASLoc, diag::err_access_specifier_interface) << (AS == AS_protected);
+ }
+
+ if (Actions.ActOnAccessSpecifier(NewAS, ASLoc, EndLoc, AccessAttrs)) {
+ // found another attribute than only annotations
+ AccessAttrs.clear();
+ }
+
+ return nullptr;
+ }
+
+ case tok::annot_attr_openmp:
+ case tok::annot_pragma_openmp:
+ return ParseOpenMPDeclarativeDirectiveWithExtDecl(
+ AS, AccessAttrs, /*Delayed=*/true, TagType, TagDecl);
+
+ default:
+ if (tok::isPragmaAnnotation(Tok.getKind())) {
+ Diag(Tok.getLocation(), diag::err_pragma_misplaced_in_decl)
+ << DeclSpec::getSpecifierName(
+ TagType, Actions.getASTContext().getPrintingPolicy());
+ ConsumeAnnotationToken();
+ return nullptr;
+ }
+ return ParseCXXClassMemberDeclaration(AS, AccessAttrs);
+ }
+}
+
+/// ParseCXXMemberSpecification - Parse the class definition.
+///
+/// member-specification:
+/// member-declaration member-specification[opt]
+/// access-specifier ':' member-specification[opt]
+///
+void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
+ SourceLocation AttrFixitLoc,
+ ParsedAttributes &Attrs,
+ unsigned TagType, Decl *TagDecl) {
+ assert((TagType == DeclSpec::TST_struct ||
+ TagType == DeclSpec::TST_interface ||
+ TagType == DeclSpec::TST_union || TagType == DeclSpec::TST_class) &&
+ "Invalid TagType!");
+
+ llvm::TimeTraceScope TimeScope("ParseClass", [&]() {
+ if (auto *TD = dyn_cast_or_null<NamedDecl>(TagDecl))
+ return TD->getQualifiedNameAsString();
+ return std::string("<anonymous>");
+ });
+
+ PrettyDeclStackTraceEntry CrashInfo(Actions.Context, TagDecl, RecordLoc,
+ "parsing struct/union/class body");
+
+ // Determine whether this is a non-nested class. Note that local
+ // classes are *not* considered to be nested classes.
+ bool NonNestedClass = true;
+ if (!ClassStack.empty()) {
+ for (const Scope *S = getCurScope(); S; S = S->getParent()) {
+ if (S->isClassScope()) {
+ // We're inside a class scope, so this is a nested class.
+ NonNestedClass = false;
+
+ // The Microsoft extension __interface does not permit nested classes.
+ if (getCurrentClass().IsInterface) {
+ Diag(RecordLoc, diag::err_invalid_member_in_interface)
+ << /*ErrorType=*/6
+ << (isa<NamedDecl>(TagDecl)
+ ? cast<NamedDecl>(TagDecl)->getQualifiedNameAsString()
+ : "(anonymous)");
+ }
+ break;
+ }
+
+ if (S->isFunctionScope())
+ // If we're in a function or function template then this is a local
+ // class rather than a nested class.
+ break;
+ }
+ }
+
+ // Enter a scope for the class.
+ ParseScope ClassScope(this, Scope::ClassScope | Scope::DeclScope);
+
+ // Note that we are parsing a new (potentially-nested) class definition.
+ ParsingClassDefinition ParsingDef(*this, TagDecl, NonNestedClass,
+ TagType == DeclSpec::TST_interface);
+
+ if (TagDecl)
+ Actions.ActOnTagStartDefinition(getCurScope(), TagDecl);
+
+ SourceLocation FinalLoc;
+ SourceLocation AbstractLoc;
+ bool IsFinalSpelledSealed = false;
+ bool IsAbstract = false;
+
+ // Parse the optional 'final' keyword.
+ if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) {
+ while (true) {
+ VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok);
+ if (Specifier == VirtSpecifiers::VS_None)
+ break;
+ if (isCXX11FinalKeyword()) {
+ if (FinalLoc.isValid()) {
+ auto Skipped = ConsumeToken();
+ Diag(Skipped, diag::err_duplicate_class_virt_specifier)
+ << VirtSpecifiers::getSpecifierName(Specifier);
+ } else {
+ FinalLoc = ConsumeToken();
+ if (Specifier == VirtSpecifiers::VS_Sealed)
+ IsFinalSpelledSealed = true;
+ }
+ } else {
+ if (AbstractLoc.isValid()) {
+ auto Skipped = ConsumeToken();
+ Diag(Skipped, diag::err_duplicate_class_virt_specifier)
+ << VirtSpecifiers::getSpecifierName(Specifier);
+ } else {
+ AbstractLoc = ConsumeToken();
+ IsAbstract = true;
+ }
+ }
+ if (TagType == DeclSpec::TST_interface)
+ Diag(FinalLoc, diag::err_override_control_interface)
+ << VirtSpecifiers::getSpecifierName(Specifier);
+ else if (Specifier == VirtSpecifiers::VS_Final)
+ Diag(FinalLoc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_override_control_keyword
+ : diag::ext_override_control_keyword)
+ << VirtSpecifiers::getSpecifierName(Specifier);
+ else if (Specifier == VirtSpecifiers::VS_Sealed)
+ Diag(FinalLoc, diag::ext_ms_sealed_keyword);
+ else if (Specifier == VirtSpecifiers::VS_Abstract)
+ Diag(AbstractLoc, diag::ext_ms_abstract_keyword);
+ else if (Specifier == VirtSpecifiers::VS_GNU_Final)
+ Diag(FinalLoc, diag::ext_warn_gnu_final);
+ }
+ assert((FinalLoc.isValid() || AbstractLoc.isValid()) &&
+ "not a class definition");
+
+ // Parse any C++11 attributes after 'final' keyword.
+ // These attributes are not allowed to appear here,
+ // and the only possible place for them to appertain
+ // to the class would be between class-key and class-name.
+ CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc);
+
+ // ParseClassSpecifier() does only a superficial check for attributes before
+ // deciding to call this method. For example, for
+ // `class C final alignas ([l) {` it will decide that this looks like a
+ // misplaced attribute since it sees `alignas '(' ')'`. But the actual
+ // attribute parsing code will try to parse the '[' as a constexpr lambda
+ // and consume enough tokens that the alignas parsing code will eat the
+ // opening '{'. So bail out if the next token isn't one we expect.
+ if (!Tok.is(tok::colon) && !Tok.is(tok::l_brace)) {
+ if (TagDecl)
+ Actions.ActOnTagDefinitionError(getCurScope(), TagDecl);
+ return;
+ }
+ }
+
+ if (Tok.is(tok::colon)) {
+ ParseScope InheritanceScope(this, getCurScope()->getFlags() |
+ Scope::ClassInheritanceScope);
+
+ ParseBaseClause(TagDecl);
+ if (!Tok.is(tok::l_brace)) {
+ bool SuggestFixIt = false;
+ SourceLocation BraceLoc = PP.getLocForEndOfToken(PrevTokLocation);
+ if (Tok.isAtStartOfLine()) {
+ switch (Tok.getKind()) {
+ case tok::kw_private:
+ case tok::kw_protected:
+ case tok::kw_public:
+ SuggestFixIt = NextToken().getKind() == tok::colon;
+ break;
+ case tok::kw_static_assert:
+ case tok::r_brace:
+ case tok::kw_using:
+ // base-clause can have simple-template-id; 'template' can't be there
+ case tok::kw_template:
+ SuggestFixIt = true;
+ break;
+ case tok::identifier:
+ SuggestFixIt = isConstructorDeclarator(true);
+ break;
+ default:
+ SuggestFixIt = isCXXSimpleDeclaration(/*AllowForRangeDecl=*/false);
+ break;
+ }
+ }
+ DiagnosticBuilder LBraceDiag =
+ Diag(BraceLoc, diag::err_expected_lbrace_after_base_specifiers);
+ if (SuggestFixIt) {
+ LBraceDiag << FixItHint::CreateInsertion(BraceLoc, " {");
+ // Try recovering from missing { after base-clause.
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+ Tok.setKind(tok::l_brace);
+ } else {
+ if (TagDecl)
+ Actions.ActOnTagDefinitionError(getCurScope(), TagDecl);
+ return;
+ }
+ }
+ }
+
+ assert(Tok.is(tok::l_brace));
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+
+ if (TagDecl)
+ Actions.ActOnStartCXXMemberDeclarations(getCurScope(), TagDecl, FinalLoc,
+ IsFinalSpelledSealed, IsAbstract,
+ T.getOpenLocation());
+
+ // C++ 11p3: Members of a class defined with the keyword class are private
+ // by default. Members of a class defined with the keywords struct or union
+ // are public by default.
+ // HLSL: In HLSL members of a class are public by default.
+ AccessSpecifier CurAS;
+ if (TagType == DeclSpec::TST_class && !getLangOpts().HLSL)
+ CurAS = AS_private;
+ else
+ CurAS = AS_public;
+ ParsedAttributes AccessAttrs(AttrFactory);
+
+ if (TagDecl) {
+ // While we still have something to read, read the member-declarations.
+ while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
+ Tok.isNot(tok::eof)) {
+ // Each iteration of this loop reads one member-declaration.
+ ParseCXXClassMemberDeclarationWithPragmas(
+ CurAS, AccessAttrs, static_cast<DeclSpec::TST>(TagType), TagDecl);
+ MaybeDestroyTemplateIds();
+ }
+ T.consumeClose();
+ } else {
+ SkipUntil(tok::r_brace);
+ }
+
+ // If attributes exist after class contents, parse them.
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseGNUAttributes(attrs);
+
+ if (TagDecl)
+ Actions.ActOnFinishCXXMemberSpecification(getCurScope(), RecordLoc, TagDecl,
+ T.getOpenLocation(),
+ T.getCloseLocation(), attrs);
+
+ // C++11 [class.mem]p2:
+ // Within the class member-specification, the class is regarded as complete
+ // within function bodies, default arguments, exception-specifications, and
+ // brace-or-equal-initializers for non-static data members (including such
+ // things in nested classes).
+ if (TagDecl && NonNestedClass) {
+ // We are not inside a nested class. This class and its nested classes
+ // are complete and we can parse the delayed portions of method
+ // declarations and the lexed inline method definitions, along with any
+ // delayed attributes.
+
+ SourceLocation SavedPrevTokLocation = PrevTokLocation;
+ ParseLexedPragmas(getCurrentClass());
+ ParseLexedAttributes(getCurrentClass());
+ ParseLexedMethodDeclarations(getCurrentClass());
+
+ // We've finished with all pending member declarations.
+ Actions.ActOnFinishCXXMemberDecls();
+
+ ParseLexedMemberInitializers(getCurrentClass());
+ ParseLexedMethodDefs(getCurrentClass());
+ PrevTokLocation = SavedPrevTokLocation;
+
+ // We've finished parsing everything, including default argument
+ // initializers.
+ Actions.ActOnFinishCXXNonNestedClass();
+ }
+
+ if (TagDecl)
+ Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange());
+
+ // Leave the class scope.
+ ParsingDef.Pop();
+ ClassScope.Exit();
+}
+
+void Parser::DiagnoseUnexpectedNamespace(NamedDecl *D) {
+ assert(Tok.is(tok::kw_namespace));
+
+ // FIXME: Suggest where the close brace should have gone by looking
+ // at indentation changes within the definition body.
+ Diag(D->getLocation(), diag::err_missing_end_of_definition) << D;
+ Diag(Tok.getLocation(), diag::note_missing_end_of_definition_before) << D;
+
+ // Push '};' onto the token stream to recover.
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+
+ Tok.startToken();
+ Tok.setLocation(PP.getLocForEndOfToken(PrevTokLocation));
+ Tok.setKind(tok::semi);
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+
+ Tok.setKind(tok::r_brace);
+}
+
+/// ParseConstructorInitializer - Parse a C++ constructor initializer,
+/// which explicitly initializes the members or base classes of a
+/// class (C++ [class.base.init]). For example, the three initializers
+/// after the ':' in the Derived constructor below:
+///
+/// @code
+/// class Base { };
+/// class Derived : Base {
+/// int x;
+/// float f;
+/// public:
+/// Derived(float f) : Base(), x(17), f(f) { }
+/// };
+/// @endcode
+///
+/// [C++] ctor-initializer:
+/// ':' mem-initializer-list
+///
+/// [C++] mem-initializer-list:
+/// mem-initializer ...[opt]
+/// mem-initializer ...[opt] , mem-initializer-list
+void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) {
+ assert(Tok.is(tok::colon) &&
+ "Constructor initializer always starts with ':'");
+
+ // Poison the SEH identifiers so they are flagged as illegal in constructor
+ // initializers.
+ PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true);
+ SourceLocation ColonLoc = ConsumeToken();
+
+ SmallVector<CXXCtorInitializer *, 4> MemInitializers;
+ bool AnyErrors = false;
+
+ do {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteConstructorInitializer(ConstructorDecl,
+ MemInitializers);
+ return;
+ }
+
+ MemInitResult MemInit = ParseMemInitializer(ConstructorDecl);
+ if (!MemInit.isInvalid())
+ MemInitializers.push_back(MemInit.get());
+ else
+ AnyErrors = true;
+
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ else if (Tok.is(tok::l_brace))
+ break;
+ // If the previous initializer was valid and the next token looks like a
+ // base or member initializer, assume that we're just missing a comma.
+ else if (!MemInit.isInvalid() &&
+ Tok.isOneOf(tok::identifier, tok::coloncolon)) {
+ SourceLocation Loc = PP.getLocForEndOfToken(PrevTokLocation);
+ Diag(Loc, diag::err_ctor_init_missing_comma)
+ << FixItHint::CreateInsertion(Loc, ", ");
+ } else {
+ // Skip over garbage, until we get to '{'. Don't eat the '{'.
+ if (!MemInit.isInvalid())
+ Diag(Tok.getLocation(), diag::err_expected_either)
+ << tok::l_brace << tok::comma;
+ SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch);
+ break;
+ }
+ } while (true);
+
+ Actions.ActOnMemInitializers(ConstructorDecl, ColonLoc, MemInitializers,
+ AnyErrors);
+}
+
+/// ParseMemInitializer - Parse a C++ member initializer, which is
+/// part of a constructor initializer that explicitly initializes one
+/// member or base class (C++ [class.base.init]). See
+/// ParseConstructorInitializer for an example.
+///
+/// [C++] mem-initializer:
+/// mem-initializer-id '(' expression-list[opt] ')'
+/// [C++0x] mem-initializer-id braced-init-list
+///
+/// [C++] mem-initializer-id:
+/// '::'[opt] nested-name-specifier[opt] class-name
+/// identifier
+MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
+ // parse '::'[opt] nested-name-specifier[opt]
+ CXXScopeSpec SS;
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false))
+ return true;
+
+ // : identifier
+ IdentifierInfo *II = nullptr;
+ SourceLocation IdLoc = Tok.getLocation();
+ // : declype(...)
+ DeclSpec DS(AttrFactory);
+ // : template_name<...>
+ TypeResult TemplateTypeTy;
+
+ if (Tok.is(tok::identifier)) {
+ // Get the identifier. This may be a member name or a class name,
+ // but we'll let the semantic analysis determine which it is.
+ II = Tok.getIdentifierInfo();
+ ConsumeToken();
+ } else if (Tok.is(tok::annot_decltype)) {
+ // Get the decltype expression, if there is one.
+ // Uses of decltype will already have been converted to annot_decltype by
+ // ParseOptionalCXXScopeSpecifier at this point.
+ // FIXME: Can we get here with a scope specifier?
+ ParseDecltypeSpecifier(DS);
+ } else {
+ TemplateIdAnnotation *TemplateId = Tok.is(tok::annot_template_id)
+ ? takeTemplateIdAnnotation(Tok)
+ : nullptr;
+ if (TemplateId && TemplateId->mightBeType()) {
+ AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No,
+ /*IsClassName=*/true);
+ assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
+ TemplateTypeTy = getTypeAnnotation(Tok);
+ ConsumeAnnotationToken();
+ } else {
+ Diag(Tok, diag::err_expected_member_or_base_name);
+ return true;
+ }
+ }
+
+ // Parse the '('.
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+
+ // FIXME: Add support for signature help inside initializer lists.
+ ExprResult InitList = ParseBraceInitializer();
+ if (InitList.isInvalid())
+ return true;
+
+ SourceLocation EllipsisLoc;
+ TryConsumeToken(tok::ellipsis, EllipsisLoc);
+
+ if (TemplateTypeTy.isInvalid())
+ return true;
+ return Actions.ActOnMemInitializer(ConstructorDecl, getCurScope(), SS, II,
+ TemplateTypeTy.get(), DS, IdLoc,
+ InitList.get(), EllipsisLoc);
+ } else if (Tok.is(tok::l_paren)) {
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ // Parse the optional expression-list.
+ ExprVector ArgExprs;
+ auto RunSignatureHelp = [&] {
+ if (TemplateTypeTy.isInvalid())
+ return QualType();
+ QualType PreferredType = Actions.ProduceCtorInitMemberSignatureHelp(
+ ConstructorDecl, SS, TemplateTypeTy.get(), ArgExprs, II,
+ T.getOpenLocation(), /*Braced=*/false);
+ CalledSignatureHelp = true;
+ return PreferredType;
+ };
+ if (Tok.isNot(tok::r_paren) && ParseExpressionList(ArgExprs, [&] {
+ PreferredType.enterFunctionArgument(Tok.getLocation(),
+ RunSignatureHelp);
+ })) {
+ if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
+ RunSignatureHelp();
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return true;
+ }
+
+ T.consumeClose();
+
+ SourceLocation EllipsisLoc;
+ TryConsumeToken(tok::ellipsis, EllipsisLoc);
+
+ if (TemplateTypeTy.isInvalid())
+ return true;
+ return Actions.ActOnMemInitializer(
+ ConstructorDecl, getCurScope(), SS, II, TemplateTypeTy.get(), DS, IdLoc,
+ T.getOpenLocation(), ArgExprs, T.getCloseLocation(), EllipsisLoc);
+ }
+
+ if (TemplateTypeTy.isInvalid())
+ return true;
+
+ if (getLangOpts().CPlusPlus11)
+ return Diag(Tok, diag::err_expected_either) << tok::l_paren << tok::l_brace;
+ else
+ return Diag(Tok, diag::err_expected) << tok::l_paren;
+}
+
+/// Parse a C++ exception-specification if present (C++0x [except.spec]).
+///
+/// exception-specification:
+/// dynamic-exception-specification
+/// noexcept-specification
+///
+/// noexcept-specification:
+/// 'noexcept'
+/// 'noexcept' '(' constant-expression ')'
+ExceptionSpecificationType Parser::tryParseExceptionSpecification(
+ bool Delayed, SourceRange &SpecificationRange,
+ SmallVectorImpl<ParsedType> &DynamicExceptions,
+ SmallVectorImpl<SourceRange> &DynamicExceptionRanges,
+ ExprResult &NoexceptExpr, CachedTokens *&ExceptionSpecTokens) {
+ ExceptionSpecificationType Result = EST_None;
+ ExceptionSpecTokens = nullptr;
+
+ // Handle delayed parsing of exception-specifications.
+ if (Delayed) {
+ if (Tok.isNot(tok::kw_throw) && Tok.isNot(tok::kw_noexcept))
+ return EST_None;
+
+ // Consume and cache the starting token.
+ bool IsNoexcept = Tok.is(tok::kw_noexcept);
+ Token StartTok = Tok;
+ SpecificationRange = SourceRange(ConsumeToken());
+
+ // Check for a '('.
+ if (!Tok.is(tok::l_paren)) {
+ // If this is a bare 'noexcept', we're done.
+ if (IsNoexcept) {
+ Diag(Tok, diag::warn_cxx98_compat_noexcept_decl);
+ NoexceptExpr = nullptr;
+ return EST_BasicNoexcept;
+ }
+
+ Diag(Tok, diag::err_expected_lparen_after) << "throw";
+ return EST_DynamicNone;
+ }
+
+ // Cache the tokens for the exception-specification.
+ ExceptionSpecTokens = new CachedTokens;
+ ExceptionSpecTokens->push_back(StartTok); // 'throw' or 'noexcept'
+ ExceptionSpecTokens->push_back(Tok); // '('
+ SpecificationRange.setEnd(ConsumeParen()); // '('
+
+ ConsumeAndStoreUntil(tok::r_paren, *ExceptionSpecTokens,
+ /*StopAtSemi=*/true,
+ /*ConsumeFinalToken=*/true);
+ SpecificationRange.setEnd(ExceptionSpecTokens->back().getLocation());
+
+ return EST_Unparsed;
+ }
+
+ // See if there's a dynamic specification.
+ if (Tok.is(tok::kw_throw)) {
+ Result = ParseDynamicExceptionSpecification(
+ SpecificationRange, DynamicExceptions, DynamicExceptionRanges);
+ assert(DynamicExceptions.size() == DynamicExceptionRanges.size() &&
+ "Produced different number of exception types and ranges.");
+ }
+
+ // If there's no noexcept specification, we're done.
+ if (Tok.isNot(tok::kw_noexcept))
+ return Result;
+
+ Diag(Tok, diag::warn_cxx98_compat_noexcept_decl);
+
+ // If we already had a dynamic specification, parse the noexcept for,
+ // recovery, but emit a diagnostic and don't store the results.
+ SourceRange NoexceptRange;
+ ExceptionSpecificationType NoexceptType = EST_None;
+
+ SourceLocation KeywordLoc = ConsumeToken();
+ if (Tok.is(tok::l_paren)) {
+ // There is an argument.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ NoexceptExpr = ParseConstantExpression();
+ T.consumeClose();
+ if (!NoexceptExpr.isInvalid()) {
+ NoexceptExpr =
+ Actions.ActOnNoexceptSpec(NoexceptExpr.get(), NoexceptType);
+ NoexceptRange = SourceRange(KeywordLoc, T.getCloseLocation());
+ } else {
+ NoexceptType = EST_BasicNoexcept;
+ }
+ } else {
+ // There is no argument.
+ NoexceptType = EST_BasicNoexcept;
+ NoexceptRange = SourceRange(KeywordLoc, KeywordLoc);
+ }
+
+ if (Result == EST_None) {
+ SpecificationRange = NoexceptRange;
+ Result = NoexceptType;
+
+ // If there's a dynamic specification after a noexcept specification,
+ // parse that and ignore the results.
+ if (Tok.is(tok::kw_throw)) {
+ Diag(Tok.getLocation(), diag::err_dynamic_and_noexcept_specification);
+ ParseDynamicExceptionSpecification(NoexceptRange, DynamicExceptions,
+ DynamicExceptionRanges);
+ }
+ } else {
+ Diag(Tok.getLocation(), diag::err_dynamic_and_noexcept_specification);
+ }
+
+ return Result;
+}
+
+static void diagnoseDynamicExceptionSpecification(Parser &P, SourceRange Range,
+ bool IsNoexcept) {
+ if (P.getLangOpts().CPlusPlus11) {
+ const char *Replacement = IsNoexcept ? "noexcept" : "noexcept(false)";
+ P.Diag(Range.getBegin(), P.getLangOpts().CPlusPlus17 && !IsNoexcept
+ ? diag::ext_dynamic_exception_spec
+ : diag::warn_exception_spec_deprecated)
+ << Range;
+ P.Diag(Range.getBegin(), diag::note_exception_spec_deprecated)
+ << Replacement << FixItHint::CreateReplacement(Range, Replacement);
+ }
+}
+
+/// ParseDynamicExceptionSpecification - Parse a C++
+/// dynamic-exception-specification (C++ [except.spec]).
+///
+/// dynamic-exception-specification:
+/// 'throw' '(' type-id-list [opt] ')'
+/// [MS] 'throw' '(' '...' ')'
+///
+/// type-id-list:
+/// type-id ... [opt]
+/// type-id-list ',' type-id ... [opt]
+///
+ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification(
+ SourceRange &SpecificationRange, SmallVectorImpl<ParsedType> &Exceptions,
+ SmallVectorImpl<SourceRange> &Ranges) {
+ assert(Tok.is(tok::kw_throw) && "expected throw");
+
+ SpecificationRange.setBegin(ConsumeToken());
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected_lparen_after) << "throw";
+ SpecificationRange.setEnd(SpecificationRange.getBegin());
+ return EST_DynamicNone;
+ }
+
+ // Parse throw(...), a Microsoft extension that means "this function
+ // can throw anything".
+ if (Tok.is(tok::ellipsis)) {
+ SourceLocation EllipsisLoc = ConsumeToken();
+ if (!getLangOpts().MicrosoftExt)
+ Diag(EllipsisLoc, diag::ext_ellipsis_exception_spec);
+ T.consumeClose();
+ SpecificationRange.setEnd(T.getCloseLocation());
+ diagnoseDynamicExceptionSpecification(*this, SpecificationRange, false);
+ return EST_MSAny;
+ }
+
+ // Parse the sequence of type-ids.
+ SourceRange Range;
+ while (Tok.isNot(tok::r_paren)) {
+ TypeResult Res(ParseTypeName(&Range));
+
+ if (Tok.is(tok::ellipsis)) {
+ // C++0x [temp.variadic]p5:
+ // - In a dynamic-exception-specification (15.4); the pattern is a
+ // type-id.
+ SourceLocation Ellipsis = ConsumeToken();
+ Range.setEnd(Ellipsis);
+ if (!Res.isInvalid())
+ Res = Actions.ActOnPackExpansion(Res.get(), Ellipsis);
+ }
+
+ if (!Res.isInvalid()) {
+ Exceptions.push_back(Res.get());
+ Ranges.push_back(Range);
+ }
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ T.consumeClose();
+ SpecificationRange.setEnd(T.getCloseLocation());
+ diagnoseDynamicExceptionSpecification(*this, SpecificationRange,
+ Exceptions.empty());
+ return Exceptions.empty() ? EST_DynamicNone : EST_Dynamic;
+}
+
+/// ParseTrailingReturnType - Parse a trailing return type on a new-style
+/// function declaration.
+TypeResult Parser::ParseTrailingReturnType(SourceRange &Range,
+ bool MayBeFollowedByDirectInit) {
+ assert(Tok.is(tok::arrow) && "expected arrow");
+
+ ConsumeToken();
+
+ return ParseTypeName(&Range, MayBeFollowedByDirectInit
+ ? DeclaratorContext::TrailingReturnVar
+ : DeclaratorContext::TrailingReturn);
+}
+
+/// Parse a requires-clause as part of a function declaration.
+void Parser::ParseTrailingRequiresClause(Declarator &D) {
+ assert(Tok.is(tok::kw_requires) && "expected requires");
+
+ SourceLocation RequiresKWLoc = ConsumeToken();
+
+ ExprResult TrailingRequiresClause;
+ ParseScope ParamScope(this, Scope::DeclScope |
+ Scope::FunctionDeclarationScope |
+ Scope::FunctionPrototypeScope);
+
+ Actions.ActOnStartTrailingRequiresClause(getCurScope(), D);
+
+ std::optional<Sema::CXXThisScopeRAII> ThisScope;
+ InitCXXThisScopeForDeclaratorIfRelevant(D, D.getDeclSpec(), ThisScope);
+
+ TrailingRequiresClause =
+ ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true);
+
+ TrailingRequiresClause =
+ Actions.ActOnFinishTrailingRequiresClause(TrailingRequiresClause);
+
+ if (!D.isDeclarationOfFunction()) {
+ Diag(RequiresKWLoc,
+ diag::err_requires_clause_on_declarator_not_declaring_a_function);
+ return;
+ }
+
+ if (TrailingRequiresClause.isInvalid())
+ SkipUntil({tok::l_brace, tok::arrow, tok::kw_try, tok::comma, tok::colon},
+ StopAtSemi | StopBeforeMatch);
+ else
+ D.setTrailingRequiresClause(TrailingRequiresClause.get());
+
+ // Did the user swap the trailing return type and requires clause?
+ if (D.isFunctionDeclarator() && Tok.is(tok::arrow) &&
+ D.getDeclSpec().getTypeSpecType() == TST_auto) {
+ SourceLocation ArrowLoc = Tok.getLocation();
+ SourceRange Range;
+ TypeResult TrailingReturnType =
+ ParseTrailingReturnType(Range, /*MayBeFollowedByDirectInit=*/false);
+
+ if (!TrailingReturnType.isInvalid()) {
+ Diag(ArrowLoc,
+ diag::err_requires_clause_must_appear_after_trailing_return)
+ << Range;
+ auto &FunctionChunk = D.getFunctionTypeInfo();
+ FunctionChunk.HasTrailingReturnType = TrailingReturnType.isUsable();
+ FunctionChunk.TrailingReturnType = TrailingReturnType.get();
+ FunctionChunk.TrailingReturnTypeLoc = Range.getBegin();
+ } else
+ SkipUntil({tok::equal, tok::l_brace, tok::arrow, tok::kw_try, tok::comma},
+ StopAtSemi | StopBeforeMatch);
+ }
+}
+
+/// We have just started parsing the definition of a new class,
+/// so push that class onto our stack of classes that is currently
+/// being parsed.
+Sema::ParsingClassState Parser::PushParsingClass(Decl *ClassDecl,
+ bool NonNestedClass,
+ bool IsInterface) {
+ assert((NonNestedClass || !ClassStack.empty()) &&
+ "Nested class without outer class");
+ ClassStack.push(new ParsingClass(ClassDecl, NonNestedClass, IsInterface));
+ return Actions.PushParsingClass();
+}
+
+/// Deallocate the given parsed class and all of its nested
+/// classes.
+void Parser::DeallocateParsedClasses(Parser::ParsingClass *Class) {
+ for (unsigned I = 0, N = Class->LateParsedDeclarations.size(); I != N; ++I)
+ delete Class->LateParsedDeclarations[I];
+ delete Class;
+}
+
+/// Pop the top class of the stack of classes that are
+/// currently being parsed.
+///
+/// This routine should be called when we have finished parsing the
+/// definition of a class, but have not yet popped the Scope
+/// associated with the class's definition.
+void Parser::PopParsingClass(Sema::ParsingClassState state) {
+ assert(!ClassStack.empty() && "Mismatched push/pop for class parsing");
+
+ Actions.PopParsingClass(state);
+
+ ParsingClass *Victim = ClassStack.top();
+ ClassStack.pop();
+ if (Victim->TopLevelClass) {
+ // Deallocate all of the nested classes of this class,
+ // recursively: we don't need to keep any of this information.
+ DeallocateParsedClasses(Victim);
+ return;
+ }
+ assert(!ClassStack.empty() && "Missing top-level class?");
+
+ if (Victim->LateParsedDeclarations.empty()) {
+ // The victim is a nested class, but we will not need to perform
+ // any processing after the definition of this class since it has
+ // no members whose handling was delayed. Therefore, we can just
+ // remove this nested class.
+ DeallocateParsedClasses(Victim);
+ return;
+ }
+
+ // This nested class has some members that will need to be processed
+ // after the top-level class is completely defined. Therefore, add
+ // it to the list of nested classes within its parent.
+ assert(getCurScope()->isClassScope() &&
+ "Nested class outside of class scope?");
+ ClassStack.top()->LateParsedDeclarations.push_back(
+ new LateParsedClass(this, Victim));
+}
+
+/// Try to parse an 'identifier' which appears within an attribute-token.
+///
+/// \return the parsed identifier on success, and 0 if the next token is not an
+/// attribute-token.
+///
+/// C++11 [dcl.attr.grammar]p3:
+/// If a keyword or an alternative token that satisfies the syntactic
+/// requirements of an identifier is contained in an attribute-token,
+/// it is considered an identifier.
+IdentifierInfo *
+Parser::TryParseCXX11AttributeIdentifier(SourceLocation &Loc,
+ Sema::AttributeCompletion Completion,
+ const IdentifierInfo *Scope) {
+ switch (Tok.getKind()) {
+ default:
+ // Identifiers and keywords have identifier info attached.
+ if (!Tok.isAnnotation()) {
+ if (IdentifierInfo *II = Tok.getIdentifierInfo()) {
+ Loc = ConsumeToken();
+ return II;
+ }
+ }
+ return nullptr;
+
+ case tok::code_completion:
+ cutOffParsing();
+ Actions.CodeCompleteAttribute(getLangOpts().CPlusPlus ? ParsedAttr::AS_CXX11
+ : ParsedAttr::AS_C2x,
+ Completion, Scope);
+ return nullptr;
+
+ case tok::numeric_constant: {
+ // If we got a numeric constant, check to see if it comes from a macro that
+ // corresponds to the predefined __clang__ macro. If it does, warn the user
+ // and recover by pretending they said _Clang instead.
+ if (Tok.getLocation().isMacroID()) {
+ SmallString<8> ExpansionBuf;
+ SourceLocation ExpansionLoc =
+ PP.getSourceManager().getExpansionLoc(Tok.getLocation());
+ StringRef Spelling = PP.getSpelling(ExpansionLoc, ExpansionBuf);
+ if (Spelling == "__clang__") {
+ SourceRange TokRange(
+ ExpansionLoc,
+ PP.getSourceManager().getExpansionLoc(Tok.getEndLoc()));
+ Diag(Tok, diag::warn_wrong_clang_attr_namespace)
+ << FixItHint::CreateReplacement(TokRange, "_Clang");
+ Loc = ConsumeToken();
+ return &PP.getIdentifierTable().get("_Clang");
+ }
+ }
+ return nullptr;
+ }
+
+ case tok::ampamp: // 'and'
+ case tok::pipe: // 'bitor'
+ case tok::pipepipe: // 'or'
+ case tok::caret: // 'xor'
+ case tok::tilde: // 'compl'
+ case tok::amp: // 'bitand'
+ case tok::ampequal: // 'and_eq'
+ case tok::pipeequal: // 'or_eq'
+ case tok::caretequal: // 'xor_eq'
+ case tok::exclaim: // 'not'
+ case tok::exclaimequal: // 'not_eq'
+ // Alternative tokens do not have identifier info, but their spelling
+ // starts with an alphabetical character.
+ SmallString<8> SpellingBuf;
+ SourceLocation SpellingLoc =
+ PP.getSourceManager().getSpellingLoc(Tok.getLocation());
+ StringRef Spelling = PP.getSpelling(SpellingLoc, SpellingBuf);
+ if (isLetter(Spelling[0])) {
+ Loc = ConsumeToken();
+ return &PP.getIdentifierTable().get(Spelling);
+ }
+ return nullptr;
+ }
+}
+
+void Parser::ParseOpenMPAttributeArgs(IdentifierInfo *AttrName,
+ CachedTokens &OpenMPTokens) {
+ // Both 'sequence' and 'directive' attributes require arguments, so parse the
+ // open paren for the argument list.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return;
+ }
+
+ if (AttrName->isStr("directive")) {
+ // If the attribute is named `directive`, we can consume its argument list
+ // and push the tokens from it into the cached token stream for a new OpenMP
+ // pragma directive.
+ Token OMPBeginTok;
+ OMPBeginTok.startToken();
+ OMPBeginTok.setKind(tok::annot_attr_openmp);
+ OMPBeginTok.setLocation(Tok.getLocation());
+ OpenMPTokens.push_back(OMPBeginTok);
+
+ ConsumeAndStoreUntil(tok::r_paren, OpenMPTokens, /*StopAtSemi=*/false,
+ /*ConsumeFinalToken*/ false);
+ Token OMPEndTok;
+ OMPEndTok.startToken();
+ OMPEndTok.setKind(tok::annot_pragma_openmp_end);
+ OMPEndTok.setLocation(Tok.getLocation());
+ OpenMPTokens.push_back(OMPEndTok);
+ } else {
+ assert(AttrName->isStr("sequence") &&
+ "Expected either 'directive' or 'sequence'");
+ // If the attribute is named 'sequence', its argument is a list of one or
+ // more OpenMP attributes (either 'omp::directive' or 'omp::sequence',
+ // where the 'omp::' is optional).
+ do {
+ // We expect to see one of the following:
+ // * An identifier (omp) for the attribute namespace followed by ::
+ // * An identifier (directive) or an identifier (sequence).
+ SourceLocation IdentLoc;
+ IdentifierInfo *Ident = TryParseCXX11AttributeIdentifier(IdentLoc);
+
+ // If there is an identifier and it is 'omp', a double colon is required
+ // followed by the actual identifier we're after.
+ if (Ident && Ident->isStr("omp") && !ExpectAndConsume(tok::coloncolon))
+ Ident = TryParseCXX11AttributeIdentifier(IdentLoc);
+
+ // If we failed to find an identifier (scoped or otherwise), or we found
+ // an unexpected identifier, diagnose.
+ if (!Ident || (!Ident->isStr("directive") && !Ident->isStr("sequence"))) {
+ Diag(Tok.getLocation(), diag::err_expected_sequence_or_directive);
+ SkipUntil(tok::r_paren, StopBeforeMatch);
+ continue;
+ }
+ // We read an identifier. If the identifier is one of the ones we
+ // expected, we can recurse to parse the args.
+ ParseOpenMPAttributeArgs(Ident, OpenMPTokens);
+
+ // There may be a comma to signal that we expect another directive in the
+ // sequence.
+ } while (TryConsumeToken(tok::comma));
+ }
+ // Parse the closing paren for the argument list.
+ T.consumeClose();
+}
+
+static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
+ IdentifierInfo *ScopeName) {
+ switch (
+ ParsedAttr::getParsedKind(AttrName, ScopeName, ParsedAttr::AS_CXX11)) {
+ case ParsedAttr::AT_CarriesDependency:
+ case ParsedAttr::AT_Deprecated:
+ case ParsedAttr::AT_FallThrough:
+ case ParsedAttr::AT_CXX11NoReturn:
+ case ParsedAttr::AT_NoUniqueAddress:
+ case ParsedAttr::AT_Likely:
+ case ParsedAttr::AT_Unlikely:
+ return true;
+ case ParsedAttr::AT_WarnUnusedResult:
+ return !ScopeName && AttrName->getName().equals("nodiscard");
+ case ParsedAttr::AT_Unused:
+ return !ScopeName && AttrName->getName().equals("maybe_unused");
+ default:
+ return false;
+ }
+}
+
+/// ParseCXX11AttributeArgs -- Parse a C++11 attribute-argument-clause.
+///
+/// [C++11] attribute-argument-clause:
+/// '(' balanced-token-seq ')'
+///
+/// [C++11] balanced-token-seq:
+/// balanced-token
+/// balanced-token-seq balanced-token
+///
+/// [C++11] balanced-token:
+/// '(' balanced-token-seq ')'
+/// '[' balanced-token-seq ']'
+/// '{' balanced-token-seq '}'
+/// any token but '(', ')', '[', ']', '{', or '}'
+bool Parser::ParseCXX11AttributeArgs(
+ IdentifierInfo *AttrName, SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, CachedTokens &OpenMPTokens) {
+ assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list");
+ SourceLocation LParenLoc = Tok.getLocation();
+ const LangOptions &LO = getLangOpts();
+ ParsedAttr::Syntax Syntax =
+ LO.CPlusPlus ? ParsedAttr::AS_CXX11 : ParsedAttr::AS_C2x;
+
+ // Try parsing microsoft attributes
+ if (getLangOpts().MicrosoftExt || getLangOpts().HLSL) {
+ if (hasAttribute(AttributeCommonInfo::Syntax::AS_Microsoft, ScopeName,
+ AttrName, getTargetInfo(), getLangOpts()))
+ Syntax = ParsedAttr::AS_Microsoft;
+ }
+
+ // If the attribute isn't known, we will not attempt to parse any
+ // arguments.
+ if (Syntax != ParsedAttr::AS_Microsoft &&
+ !hasAttribute(LO.CPlusPlus ? AttributeCommonInfo::Syntax::AS_CXX11
+ : AttributeCommonInfo::Syntax::AS_C2x,
+ ScopeName, AttrName, getTargetInfo(), getLangOpts())) {
+ if (getLangOpts().MicrosoftExt || getLangOpts().HLSL) {
+ }
+ // Eat the left paren, then skip to the ending right paren.
+ ConsumeParen();
+ SkipUntil(tok::r_paren);
+ return false;
+ }
+
+ if (ScopeName && (ScopeName->isStr("gnu") || ScopeName->isStr("__gnu__"))) {
+ // GNU-scoped attributes have some special cases to handle GNU-specific
+ // behaviors.
+ ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
+ ScopeLoc, Syntax, nullptr);
+ return true;
+ }
+
+ if (ScopeName && ScopeName->isStr("omp")) {
+ Diag(AttrNameLoc, getLangOpts().OpenMP >= 51
+ ? diag::warn_omp51_compat_attributes
+ : diag::ext_omp_attributes);
+
+ ParseOpenMPAttributeArgs(AttrName, OpenMPTokens);
+
+ // We claim that an attribute was parsed and added so that one is not
+ // created for us by the caller.
+ return true;
+ }
+
+ unsigned NumArgs;
+ // Some Clang-scoped attributes have some special parsing behavior.
+ if (ScopeName && (ScopeName->isStr("clang") || ScopeName->isStr("_Clang")))
+ NumArgs = ParseClangAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ else
+ NumArgs = ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+
+ if (!Attrs.empty() &&
+ IsBuiltInOrStandardCXX11Attribute(AttrName, ScopeName)) {
+ ParsedAttr &Attr = Attrs.back();
+ // If the attribute is a standard or built-in attribute and we are
+ // parsing an argument list, we need to determine whether this attribute
+ // was allowed to have an argument list (such as [[deprecated]]), and how
+ // many arguments were parsed (so we can diagnose on [[deprecated()]]).
+ if (Attr.getMaxArgs() && !NumArgs) {
+ // The attribute was allowed to have arguments, but none were provided
+ // even though the attribute parsed successfully. This is an error.
+ Diag(LParenLoc, diag::err_attribute_requires_arguments) << AttrName;
+ Attr.setInvalid(true);
+ } else if (!Attr.getMaxArgs()) {
+ // The attribute parsed successfully, but was not allowed to have any
+ // arguments. It doesn't matter whether any were provided -- the
+ // presence of the argument list (even if empty) is diagnosed.
+ Diag(LParenLoc, diag::err_cxx11_attribute_forbids_arguments)
+ << AttrName
+ << FixItHint::CreateRemoval(SourceRange(LParenLoc, *EndLoc));
+ Attr.setInvalid(true);
+ }
+ }
+ return true;
+}
+
+/// Parse a C++11 or C2x attribute-specifier.
+///
+/// [C++11] attribute-specifier:
+/// '[' '[' attribute-list ']' ']'
+/// alignment-specifier
+///
+/// [C++11] attribute-list:
+/// attribute[opt]
+/// attribute-list ',' attribute[opt]
+/// attribute '...'
+/// attribute-list ',' attribute '...'
+///
+/// [C++11] attribute:
+/// attribute-token attribute-argument-clause[opt]
+///
+/// [C++11] attribute-token:
+/// identifier
+/// attribute-scoped-token
+///
+/// [C++11] attribute-scoped-token:
+/// attribute-namespace '::' identifier
+///
+/// [C++11] attribute-namespace:
+/// identifier
+void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
+ CachedTokens &OpenMPTokens,
+ SourceLocation *EndLoc) {
+ if (Tok.is(tok::kw_alignas)) {
+ Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas);
+ ParseAlignmentSpecifier(Attrs, EndLoc);
+ return;
+ }
+
+ assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square) &&
+ "Not a double square bracket attribute list");
+
+ SourceLocation OpenLoc = Tok.getLocation();
+ Diag(OpenLoc, diag::warn_cxx98_compat_attribute);
+
+ ConsumeBracket();
+ checkCompoundToken(OpenLoc, tok::l_square, CompoundToken::AttrBegin);
+ ConsumeBracket();
+
+ SourceLocation CommonScopeLoc;
+ IdentifierInfo *CommonScopeName = nullptr;
+ if (Tok.is(tok::kw_using)) {
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
+ ? diag::warn_cxx14_compat_using_attribute_ns
+ : diag::ext_using_attribute_ns);
+ ConsumeToken();
+
+ CommonScopeName = TryParseCXX11AttributeIdentifier(
+ CommonScopeLoc, Sema::AttributeCompletion::Scope);
+ if (!CommonScopeName) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
+ }
+ if (!TryConsumeToken(tok::colon) && CommonScopeName)
+ Diag(Tok.getLocation(), diag::err_expected) << tok::colon;
+ }
+
+ bool AttrParsed = false;
+ while (!Tok.isOneOf(tok::r_square, tok::semi, tok::eof)) {
+ if (AttrParsed) {
+ // If we parsed an attribute, a comma is required before parsing any
+ // additional attributes.
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_square, StopAtSemi | StopBeforeMatch);
+ continue;
+ }
+ AttrParsed = false;
+ }
+
+ // Eat all remaining superfluous commas before parsing the next attribute.
+ while (TryConsumeToken(tok::comma))
+ ;
+
+ SourceLocation ScopeLoc, AttrLoc;
+ IdentifierInfo *ScopeName = nullptr, *AttrName = nullptr;
+
+ AttrName = TryParseCXX11AttributeIdentifier(
+ AttrLoc, Sema::AttributeCompletion::Attribute, CommonScopeName);
+ if (!AttrName)
+ // Break out to the "expected ']'" diagnostic.
+ break;
+
+ // scoped attribute
+ if (TryConsumeToken(tok::coloncolon)) {
+ ScopeName = AttrName;
+ ScopeLoc = AttrLoc;
+
+ AttrName = TryParseCXX11AttributeIdentifier(
+ AttrLoc, Sema::AttributeCompletion::Attribute, ScopeName);
+ if (!AttrName) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_square, tok::comma, StopAtSemi | StopBeforeMatch);
+ continue;
+ }
+ }
+
+ if (CommonScopeName) {
+ if (ScopeName) {
+ Diag(ScopeLoc, diag::err_using_attribute_ns_conflict)
+ << SourceRange(CommonScopeLoc);
+ } else {
+ ScopeName = CommonScopeName;
+ ScopeLoc = CommonScopeLoc;
+ }
+ }
+
+ // Parse attribute arguments
+ if (Tok.is(tok::l_paren))
+ AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, OpenMPTokens);
+
+ if (!AttrParsed) {
+ Attrs.addNew(
+ AttrName,
+ SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc, AttrLoc),
+ ScopeName, ScopeLoc, nullptr, 0,
+ getLangOpts().CPlusPlus ? ParsedAttr::AS_CXX11 : ParsedAttr::AS_C2x);
+ AttrParsed = true;
+ }
+
+ if (TryConsumeToken(tok::ellipsis))
+ Diag(Tok, diag::err_cxx11_attribute_forbids_ellipsis) << AttrName;
+ }
+
+ // If we hit an error and recovered by parsing up to a semicolon, eat the
+ // semicolon and don't issue further diagnostics about missing brackets.
+ if (Tok.is(tok::semi)) {
+ ConsumeToken();
+ return;
+ }
+
+ SourceLocation CloseLoc = Tok.getLocation();
+ if (ExpectAndConsume(tok::r_square))
+ SkipUntil(tok::r_square);
+ else if (Tok.is(tok::r_square))
+ checkCompoundToken(CloseLoc, tok::r_square, CompoundToken::AttrEnd);
+ if (EndLoc)
+ *EndLoc = Tok.getLocation();
+ if (ExpectAndConsume(tok::r_square))
+ SkipUntil(tok::r_square);
+}
+
+/// ParseCXX11Attributes - Parse a C++11 or C2x attribute-specifier-seq.
+///
+/// attribute-specifier-seq:
+/// attribute-specifier-seq[opt] attribute-specifier
+void Parser::ParseCXX11Attributes(ParsedAttributes &Attrs) {
+ assert(standardAttributesAllowed());
+
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc = StartLoc;
+
+ do {
+ ParseCXX11AttributeSpecifier(Attrs, &EndLoc);
+ } while (isCXX11AttributeSpecifier());
+
+ Attrs.Range = SourceRange(StartLoc, EndLoc);
+}
+
+void Parser::DiagnoseAndSkipCXX11Attributes() {
+ // Start and end location of an attribute or an attribute list.
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc = SkipCXX11Attributes();
+
+ if (EndLoc.isValid()) {
+ SourceRange Range(StartLoc, EndLoc);
+ Diag(StartLoc, diag::err_attributes_not_allowed) << Range;
+ }
+}
+
+SourceLocation Parser::SkipCXX11Attributes() {
+ SourceLocation EndLoc;
+
+ if (!isCXX11AttributeSpecifier())
+ return EndLoc;
+
+ do {
+ if (Tok.is(tok::l_square)) {
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+ T.skipToEnd();
+ EndLoc = T.getCloseLocation();
+ } else {
+ assert(Tok.is(tok::kw_alignas) && "not an attribute specifier");
+ ConsumeToken();
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (!T.consumeOpen())
+ T.skipToEnd();
+ EndLoc = T.getCloseLocation();
+ }
+ } while (isCXX11AttributeSpecifier());
+
+ return EndLoc;
+}
+
+/// Parse uuid() attribute when it appears in a [] Microsoft attribute.
+void Parser::ParseMicrosoftUuidAttributeArgs(ParsedAttributes &Attrs) {
+ assert(Tok.is(tok::identifier) && "Not a Microsoft attribute list");
+ IdentifierInfo *UuidIdent = Tok.getIdentifierInfo();
+ assert(UuidIdent->getName() == "uuid" && "Not a Microsoft attribute list");
+
+ SourceLocation UuidLoc = Tok.getLocation();
+ ConsumeToken();
+
+ // Ignore the left paren location for now.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return;
+ }
+
+ ArgsVector ArgExprs;
+ if (Tok.is(tok::string_literal)) {
+ // Easy case: uuid("...") -- quoted string.
+ ExprResult StringResult = ParseStringLiteralExpression();
+ if (StringResult.isInvalid())
+ return;
+ ArgExprs.push_back(StringResult.get());
+ } else {
+ // something like uuid({000000A0-0000-0000-C000-000000000049}) -- no
+ // quotes in the parens. Just append the spelling of all tokens encountered
+ // until the closing paren.
+
+ SmallString<42> StrBuffer; // 2 "", 36 bytes UUID, 2 optional {}, 1 nul
+ StrBuffer += "\"";
+
+ // Since none of C++'s keywords match [a-f]+, accepting just tok::l_brace,
+ // tok::r_brace, tok::minus, tok::identifier (think C000) and
+ // tok::numeric_constant (0000) should be enough. But the spelling of the
+ // uuid argument is checked later anyways, so there's no harm in accepting
+ // almost anything here.
+ // cl is very strict about whitespace in this form and errors out if any
+ // is present, so check the space flags on the tokens.
+ SourceLocation StartLoc = Tok.getLocation();
+ while (Tok.isNot(tok::r_paren)) {
+ if (Tok.hasLeadingSpace() || Tok.isAtStartOfLine()) {
+ Diag(Tok, diag::err_attribute_uuid_malformed_guid);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ SmallString<16> SpellingBuffer;
+ SpellingBuffer.resize(Tok.getLength() + 1);
+ bool Invalid = false;
+ StringRef TokSpelling = PP.getSpelling(Tok, SpellingBuffer, &Invalid);
+ if (Invalid) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ StrBuffer += TokSpelling;
+ ConsumeAnyToken();
+ }
+ StrBuffer += "\"";
+
+ if (Tok.hasLeadingSpace() || Tok.isAtStartOfLine()) {
+ Diag(Tok, diag::err_attribute_uuid_malformed_guid);
+ ConsumeParen();
+ return;
+ }
+
+ // Pretend the user wrote the appropriate string literal here.
+ // ActOnStringLiteral() copies the string data into the literal, so it's
+ // ok that the Token points to StrBuffer.
+ Token Toks[1];
+ Toks[0].startToken();
+ Toks[0].setKind(tok::string_literal);
+ Toks[0].setLocation(StartLoc);
+ Toks[0].setLiteralData(StrBuffer.data());
+ Toks[0].setLength(StrBuffer.size());
+ StringLiteral *UuidString =
+ cast<StringLiteral>(Actions.ActOnStringLiteral(Toks, nullptr).get());
+ ArgExprs.push_back(UuidString);
+ }
+
+ if (!T.consumeClose()) {
+ Attrs.addNew(UuidIdent, SourceRange(UuidLoc, T.getCloseLocation()), nullptr,
+ SourceLocation(), ArgExprs.data(), ArgExprs.size(),
+ ParsedAttr::AS_Microsoft);
+ }
+}
+
+/// ParseMicrosoftAttributes - Parse Microsoft attributes [Attr]
+///
+/// [MS] ms-attribute:
+/// '[' token-seq ']'
+///
+/// [MS] ms-attribute-seq:
+/// ms-attribute[opt]
+/// ms-attribute ms-attribute-seq
+void Parser::ParseMicrosoftAttributes(ParsedAttributes &Attrs) {
+ assert(Tok.is(tok::l_square) && "Not a Microsoft attribute list");
+
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc = StartLoc;
+ do {
+ // FIXME: If this is actually a C++11 attribute, parse it as one.
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+
+ // Skip most ms attributes except for a specific list.
+ while (true) {
+ SkipUntil(tok::r_square, tok::identifier,
+ StopAtSemi | StopBeforeMatch | StopAtCodeCompletion);
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteAttribute(AttributeCommonInfo::AS_Microsoft,
+ Sema::AttributeCompletion::Attribute,
+ /*Scope=*/nullptr);
+ break;
+ }
+ if (Tok.isNot(tok::identifier)) // ']', but also eof
+ break;
+ if (Tok.getIdentifierInfo()->getName() == "uuid")
+ ParseMicrosoftUuidAttributeArgs(Attrs);
+ else {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ SourceLocation NameLoc = Tok.getLocation();
+ ConsumeToken();
+ ParsedAttr::Kind AttrKind =
+ ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_Microsoft);
+ // For HLSL we want to handle all attributes, but for MSVC compat, we
+ // silently ignore unknown Microsoft attributes.
+ if (getLangOpts().HLSL || AttrKind != ParsedAttr::UnknownAttribute) {
+ bool AttrParsed = false;
+ if (Tok.is(tok::l_paren)) {
+ CachedTokens OpenMPTokens;
+ AttrParsed =
+ ParseCXX11AttributeArgs(II, NameLoc, Attrs, &EndLoc, nullptr,
+ SourceLocation(), OpenMPTokens);
+ ReplayOpenMPAttributeTokens(OpenMPTokens);
+ }
+ if (!AttrParsed) {
+ Attrs.addNew(II, NameLoc, nullptr, SourceLocation(), nullptr, 0,
+ ParsedAttr::AS_Microsoft);
+ }
+ }
+ }
+ }
+
+ T.consumeClose();
+ EndLoc = T.getCloseLocation();
+ } while (Tok.is(tok::l_square));
+
+ Attrs.Range = SourceRange(StartLoc, EndLoc);
+}
+
+void Parser::ParseMicrosoftIfExistsClassDeclaration(
+ DeclSpec::TST TagType, ParsedAttributes &AccessAttrs,
+ AccessSpecifier &CurAS) {
+ IfExistsCondition Result;
+ if (ParseMicrosoftIfExistsCondition(Result))
+ return;
+
+ BalancedDelimiterTracker Braces(*this, tok::l_brace);
+ if (Braces.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return;
+ }
+
+ switch (Result.Behavior) {
+ case IEB_Parse:
+ // Parse the declarations below.
+ break;
+
+ case IEB_Dependent:
+ Diag(Result.KeywordLoc, diag::warn_microsoft_dependent_exists)
+ << Result.IsIfExists;
+ // Fall through to skip.
+ [[fallthrough]];
+
+ case IEB_Skip:
+ Braces.skipToEnd();
+ return;
+ }
+
+ while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
+ // __if_exists, __if_not_exists can nest.
+ if (Tok.isOneOf(tok::kw___if_exists, tok::kw___if_not_exists)) {
+ ParseMicrosoftIfExistsClassDeclaration(TagType, AccessAttrs, CurAS);
+ continue;
+ }
+
+ // Check for extraneous top-level semicolon.
+ if (Tok.is(tok::semi)) {
+ ConsumeExtraSemi(InsideStruct, TagType);
+ continue;
+ }
+
+ AccessSpecifier AS = getAccessSpecifierIfPresent();
+ if (AS != AS_none) {
+ // Current token is a C++ access specifier.
+ CurAS = AS;
+ SourceLocation ASLoc = Tok.getLocation();
+ ConsumeToken();
+ if (Tok.is(tok::colon))
+ Actions.ActOnAccessSpecifier(AS, ASLoc, Tok.getLocation(),
+ ParsedAttributesView{});
+ else
+ Diag(Tok, diag::err_expected) << tok::colon;
+ ConsumeToken();
+ continue;
+ }
+
+ // Parse all the comma separated declarators.
+ ParseCXXClassMemberDeclaration(CurAS, AccessAttrs);
+ }
+
+ Braces.consumeClose();
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseExpr.cpp b/contrib/libs/clang16/lib/Parse/ParseExpr.cpp
new file mode 100644
index 0000000000..66d937ac57
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseExpr.cpp
@@ -0,0 +1,3782 @@
+//===--- ParseExpr.cpp - Expression Parsing -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Provides the Expression parsing implementation.
+///
+/// Expressions in C99 basically consist of a bunch of binary operators with
+/// unary operators and other random stuff at the leaves.
+///
+/// In the C99 grammar, these unary operators bind tightest and are represented
+/// as the 'cast-expression' production. Everything else is either a binary
+/// operator (e.g. '/') or a ternary operator ("?:"). The unary leaves are
+/// handled by ParseCastExpression, the higher level pieces are handled by
+/// ParseBinaryExpression.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Parse/Parser.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Basic/PrettyStackTrace.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/ParsedTemplate.h"
+#include "clang/Sema/Scope.h"
+#include "clang/Sema/TypoCorrection.h"
+#include "llvm/ADT/SmallVector.h"
+#include <optional>
+using namespace clang;
+
+/// Simple precedence-based parser for binary/ternary operators.
+///
+/// Note: we diverge from the C99 grammar when parsing the assignment-expression
+/// production. C99 specifies that the LHS of an assignment operator should be
+/// parsed as a unary-expression, but consistency dictates that it be a
+/// conditional-expession. In practice, the important thing here is that the
+/// LHS of an assignment has to be an l-value, which productions between
+/// unary-expression and conditional-expression don't produce. Because we want
+/// consistency, we parse the LHS as a conditional-expression, then check for
+/// l-value-ness in semantic analysis stages.
+///
+/// \verbatim
+/// pm-expression: [C++ 5.5]
+/// cast-expression
+/// pm-expression '.*' cast-expression
+/// pm-expression '->*' cast-expression
+///
+/// multiplicative-expression: [C99 6.5.5]
+/// Note: in C++, apply pm-expression instead of cast-expression
+/// cast-expression
+/// multiplicative-expression '*' cast-expression
+/// multiplicative-expression '/' cast-expression
+/// multiplicative-expression '%' cast-expression
+///
+/// additive-expression: [C99 6.5.6]
+/// multiplicative-expression
+/// additive-expression '+' multiplicative-expression
+/// additive-expression '-' multiplicative-expression
+///
+/// shift-expression: [C99 6.5.7]
+/// additive-expression
+/// shift-expression '<<' additive-expression
+/// shift-expression '>>' additive-expression
+///
+/// compare-expression: [C++20 expr.spaceship]
+/// shift-expression
+/// compare-expression '<=>' shift-expression
+///
+/// relational-expression: [C99 6.5.8]
+/// compare-expression
+/// relational-expression '<' compare-expression
+/// relational-expression '>' compare-expression
+/// relational-expression '<=' compare-expression
+/// relational-expression '>=' compare-expression
+///
+/// equality-expression: [C99 6.5.9]
+/// relational-expression
+/// equality-expression '==' relational-expression
+/// equality-expression '!=' relational-expression
+///
+/// AND-expression: [C99 6.5.10]
+/// equality-expression
+/// AND-expression '&' equality-expression
+///
+/// exclusive-OR-expression: [C99 6.5.11]
+/// AND-expression
+/// exclusive-OR-expression '^' AND-expression
+///
+/// inclusive-OR-expression: [C99 6.5.12]
+/// exclusive-OR-expression
+/// inclusive-OR-expression '|' exclusive-OR-expression
+///
+/// logical-AND-expression: [C99 6.5.13]
+/// inclusive-OR-expression
+/// logical-AND-expression '&&' inclusive-OR-expression
+///
+/// logical-OR-expression: [C99 6.5.14]
+/// logical-AND-expression
+/// logical-OR-expression '||' logical-AND-expression
+///
+/// conditional-expression: [C99 6.5.15]
+/// logical-OR-expression
+/// logical-OR-expression '?' expression ':' conditional-expression
+/// [GNU] logical-OR-expression '?' ':' conditional-expression
+/// [C++] the third operand is an assignment-expression
+///
+/// assignment-expression: [C99 6.5.16]
+/// conditional-expression
+/// unary-expression assignment-operator assignment-expression
+/// [C++] throw-expression [C++ 15]
+///
+/// assignment-operator: one of
+/// = *= /= %= += -= <<= >>= &= ^= |=
+///
+/// expression: [C99 6.5.17]
+/// assignment-expression ...[opt]
+/// expression ',' assignment-expression ...[opt]
+/// \endverbatim
+ExprResult Parser::ParseExpression(TypeCastState isTypeCast) {
+ ExprResult LHS(ParseAssignmentExpression(isTypeCast));
+ return ParseRHSOfBinaryExpression(LHS, prec::Comma);
+}
+
+/// This routine is called when the '@' is seen and consumed.
+/// Current token is an Identifier and is not a 'try'. This
+/// routine is necessary to disambiguate \@try-statement from,
+/// for example, \@encode-expression.
+///
+ExprResult
+Parser::ParseExpressionWithLeadingAt(SourceLocation AtLoc) {
+ ExprResult LHS(ParseObjCAtExpression(AtLoc));
+ return ParseRHSOfBinaryExpression(LHS, prec::Comma);
+}
+
+/// This routine is called when a leading '__extension__' is seen and
+/// consumed. This is necessary because the token gets consumed in the
+/// process of disambiguating between an expression and a declaration.
+ExprResult
+Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) {
+ ExprResult LHS(true);
+ {
+ // Silence extension warnings in the sub-expression
+ ExtensionRAIIObject O(Diags);
+
+ LHS = ParseCastExpression(AnyCastExpr);
+ }
+
+ if (!LHS.isInvalid())
+ LHS = Actions.ActOnUnaryOp(getCurScope(), ExtLoc, tok::kw___extension__,
+ LHS.get());
+
+ return ParseRHSOfBinaryExpression(LHS, prec::Comma);
+}
+
+/// Parse an expr that doesn't include (top-level) commas.
+ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteExpression(getCurScope(),
+ PreferredType.get(Tok.getLocation()));
+ return ExprError();
+ }
+
+ if (Tok.is(tok::kw_throw))
+ return ParseThrowExpression();
+ if (Tok.is(tok::kw_co_yield))
+ return ParseCoyieldExpression();
+
+ ExprResult LHS = ParseCastExpression(AnyCastExpr,
+ /*isAddressOfOperand=*/false,
+ isTypeCast);
+ return ParseRHSOfBinaryExpression(LHS, prec::Assignment);
+}
+
+/// Parse an assignment expression where part of an Objective-C message
+/// send has already been parsed.
+///
+/// In this case \p LBracLoc indicates the location of the '[' of the message
+/// send, and either \p ReceiverName or \p ReceiverExpr is non-null indicating
+/// the receiver of the message.
+///
+/// Since this handles full assignment-expression's, it handles postfix
+/// expressions and other binary operators for these expressions as well.
+ExprResult
+Parser::ParseAssignmentExprWithObjCMessageExprStart(SourceLocation LBracLoc,
+ SourceLocation SuperLoc,
+ ParsedType ReceiverType,
+ Expr *ReceiverExpr) {
+ ExprResult R
+ = ParseObjCMessageExpressionBody(LBracLoc, SuperLoc,
+ ReceiverType, ReceiverExpr);
+ R = ParsePostfixExpressionSuffix(R);
+ return ParseRHSOfBinaryExpression(R, prec::Assignment);
+}
+
+ExprResult
+Parser::ParseConstantExpressionInExprEvalContext(TypeCastState isTypeCast) {
+ assert(Actions.ExprEvalContexts.back().Context ==
+ Sema::ExpressionEvaluationContext::ConstantEvaluated &&
+ "Call this function only if your ExpressionEvaluationContext is "
+ "already ConstantEvaluated");
+ ExprResult LHS(ParseCastExpression(AnyCastExpr, false, isTypeCast));
+ ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional));
+ return Actions.ActOnConstantExpression(Res);
+}
+
+ExprResult Parser::ParseConstantExpression() {
+ // C++03 [basic.def.odr]p2:
+ // An expression is potentially evaluated unless it appears where an
+ // integral constant expression is required (see 5.19) [...].
+ // C++98 and C++11 have no such rule, but this is only a defect in C++98.
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ return ParseConstantExpressionInExprEvalContext(NotTypeCast);
+}
+
+ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) {
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ ExprResult LHS(ParseCastExpression(AnyCastExpr, false, NotTypeCast));
+ ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional));
+ return Actions.ActOnCaseExpr(CaseLoc, Res);
+}
+
+/// Parse a constraint-expression.
+///
+/// \verbatim
+/// constraint-expression: C++2a[temp.constr.decl]p1
+/// logical-or-expression
+/// \endverbatim
+ExprResult Parser::ParseConstraintExpression() {
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+ ExprResult LHS(ParseCastExpression(AnyCastExpr));
+ ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr));
+ if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) {
+ Actions.CorrectDelayedTyposInExpr(Res);
+ return ExprError();
+ }
+ return Res;
+}
+
+/// \brief Parse a constraint-logical-and-expression.
+///
+/// \verbatim
+/// C++2a[temp.constr.decl]p1
+/// constraint-logical-and-expression:
+/// primary-expression
+/// constraint-logical-and-expression '&&' primary-expression
+///
+/// \endverbatim
+ExprResult
+Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) {
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+ bool NotPrimaryExpression = false;
+ auto ParsePrimary = [&] () {
+ ExprResult E = ParseCastExpression(PrimaryExprOnly,
+ /*isAddressOfOperand=*/false,
+ /*isTypeCast=*/NotTypeCast,
+ /*isVectorLiteral=*/false,
+ &NotPrimaryExpression);
+ if (E.isInvalid())
+ return ExprError();
+ auto RecoverFromNonPrimary = [&] (ExprResult E, bool Note) {
+ E = ParsePostfixExpressionSuffix(E);
+ // Use InclusiveOr, the precedence just after '&&' to not parse the
+ // next arguments to the logical and.
+ E = ParseRHSOfBinaryExpression(E, prec::InclusiveOr);
+ if (!E.isInvalid())
+ Diag(E.get()->getExprLoc(),
+ Note
+ ? diag::note_unparenthesized_non_primary_expr_in_requires_clause
+ : diag::err_unparenthesized_non_primary_expr_in_requires_clause)
+ << FixItHint::CreateInsertion(E.get()->getBeginLoc(), "(")
+ << FixItHint::CreateInsertion(
+ PP.getLocForEndOfToken(E.get()->getEndLoc()), ")")
+ << E.get()->getSourceRange();
+ return E;
+ };
+
+ if (NotPrimaryExpression ||
+ // Check if the following tokens must be a part of a non-primary
+ // expression
+ getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator,
+ /*CPlusPlus11=*/true) > prec::LogicalAnd ||
+ // Postfix operators other than '(' (which will be checked for in
+ // CheckConstraintExpression).
+ Tok.isOneOf(tok::period, tok::plusplus, tok::minusminus) ||
+ (Tok.is(tok::l_square) && !NextToken().is(tok::l_square))) {
+ E = RecoverFromNonPrimary(E, /*Note=*/false);
+ if (E.isInvalid())
+ return ExprError();
+ NotPrimaryExpression = false;
+ }
+ bool PossibleNonPrimary;
+ bool IsConstraintExpr =
+ Actions.CheckConstraintExpression(E.get(), Tok, &PossibleNonPrimary,
+ IsTrailingRequiresClause);
+ if (!IsConstraintExpr || PossibleNonPrimary) {
+ // Atomic constraint might be an unparenthesized non-primary expression
+ // (such as a binary operator), in which case we might get here (e.g. in
+ // 'requires 0 + 1 && true' we would now be at '+', and parse and ignore
+ // the rest of the addition expression). Try to parse the rest of it here.
+ if (PossibleNonPrimary)
+ E = RecoverFromNonPrimary(E, /*Note=*/!IsConstraintExpr);
+ Actions.CorrectDelayedTyposInExpr(E);
+ return ExprError();
+ }
+ return E;
+ };
+ ExprResult LHS = ParsePrimary();
+ if (LHS.isInvalid())
+ return ExprError();
+ while (Tok.is(tok::ampamp)) {
+ SourceLocation LogicalAndLoc = ConsumeToken();
+ ExprResult RHS = ParsePrimary();
+ if (RHS.isInvalid()) {
+ Actions.CorrectDelayedTyposInExpr(LHS);
+ return ExprError();
+ }
+ ExprResult Op = Actions.ActOnBinOp(getCurScope(), LogicalAndLoc,
+ tok::ampamp, LHS.get(), RHS.get());
+ if (!Op.isUsable()) {
+ Actions.CorrectDelayedTyposInExpr(RHS);
+ Actions.CorrectDelayedTyposInExpr(LHS);
+ return ExprError();
+ }
+ LHS = Op;
+ }
+ return LHS;
+}
+
+/// \brief Parse a constraint-logical-or-expression.
+///
+/// \verbatim
+/// C++2a[temp.constr.decl]p1
+/// constraint-logical-or-expression:
+/// constraint-logical-and-expression
+/// constraint-logical-or-expression '||'
+/// constraint-logical-and-expression
+///
+/// \endverbatim
+ExprResult
+Parser::ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause) {
+ ExprResult LHS(ParseConstraintLogicalAndExpression(IsTrailingRequiresClause));
+ if (!LHS.isUsable())
+ return ExprError();
+ while (Tok.is(tok::pipepipe)) {
+ SourceLocation LogicalOrLoc = ConsumeToken();
+ ExprResult RHS =
+ ParseConstraintLogicalAndExpression(IsTrailingRequiresClause);
+ if (!RHS.isUsable()) {
+ Actions.CorrectDelayedTyposInExpr(LHS);
+ return ExprError();
+ }
+ ExprResult Op = Actions.ActOnBinOp(getCurScope(), LogicalOrLoc,
+ tok::pipepipe, LHS.get(), RHS.get());
+ if (!Op.isUsable()) {
+ Actions.CorrectDelayedTyposInExpr(RHS);
+ Actions.CorrectDelayedTyposInExpr(LHS);
+ return ExprError();
+ }
+ LHS = Op;
+ }
+ return LHS;
+}
+
+bool Parser::isNotExpressionStart() {
+ tok::TokenKind K = Tok.getKind();
+ if (K == tok::l_brace || K == tok::r_brace ||
+ K == tok::kw_for || K == tok::kw_while ||
+ K == tok::kw_if || K == tok::kw_else ||
+ K == tok::kw_goto || K == tok::kw_try)
+ return true;
+ // If this is a decl-specifier, we can't be at the start of an expression.
+ return isKnownToBeDeclarationSpecifier();
+}
+
+bool Parser::isFoldOperator(prec::Level Level) const {
+ return Level > prec::Unknown && Level != prec::Conditional &&
+ Level != prec::Spaceship;
+}
+
+bool Parser::isFoldOperator(tok::TokenKind Kind) const {
+ return isFoldOperator(getBinOpPrecedence(Kind, GreaterThanIsOperator, true));
+}
+
+/// Parse a binary expression that starts with \p LHS and has a
+/// precedence of at least \p MinPrec.
+ExprResult
+Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
+ prec::Level NextTokPrec = getBinOpPrecedence(Tok.getKind(),
+ GreaterThanIsOperator,
+ getLangOpts().CPlusPlus11);
+ SourceLocation ColonLoc;
+
+ auto SavedType = PreferredType;
+ while (true) {
+ // Every iteration may rely on a preferred type for the whole expression.
+ PreferredType = SavedType;
+ // If this token has a lower precedence than we are allowed to parse (e.g.
+ // because we are called recursively, or because the token is not a binop),
+ // then we are done!
+ if (NextTokPrec < MinPrec)
+ return LHS;
+
+ // Consume the operator, saving the operator token for error reporting.
+ Token OpToken = Tok;
+ ConsumeToken();
+
+ if (OpToken.is(tok::caretcaret)) {
+ return ExprError(Diag(Tok, diag::err_opencl_logical_exclusive_or));
+ }
+
+ // If we're potentially in a template-id, we may now be able to determine
+ // whether we're actually in one or not.
+ if (OpToken.isOneOf(tok::comma, tok::greater, tok::greatergreater,
+ tok::greatergreatergreater) &&
+ checkPotentialAngleBracketDelimiter(OpToken))
+ return ExprError();
+
+ // Bail out when encountering a comma followed by a token which can't
+ // possibly be the start of an expression. For instance:
+ // int f() { return 1, }
+ // We can't do this before consuming the comma, because
+ // isNotExpressionStart() looks at the token stream.
+ if (OpToken.is(tok::comma) && isNotExpressionStart()) {
+ PP.EnterToken(Tok, /*IsReinject*/true);
+ Tok = OpToken;
+ return LHS;
+ }
+
+ // If the next token is an ellipsis, then this is a fold-expression. Leave
+ // it alone so we can handle it in the paren expression.
+ if (isFoldOperator(NextTokPrec) && Tok.is(tok::ellipsis)) {
+ // FIXME: We can't check this via lookahead before we consume the token
+ // because that tickles a lexer bug.
+ PP.EnterToken(Tok, /*IsReinject*/true);
+ Tok = OpToken;
+ return LHS;
+ }
+
+ // In Objective-C++, alternative operator tokens can be used as keyword args
+ // in message expressions. Unconsume the token so that it can reinterpreted
+ // as an identifier in ParseObjCMessageExpressionBody. i.e., we support:
+ // [foo meth:0 and:0];
+ // [foo not_eq];
+ if (getLangOpts().ObjC && getLangOpts().CPlusPlus &&
+ Tok.isOneOf(tok::colon, tok::r_square) &&
+ OpToken.getIdentifierInfo() != nullptr) {
+ PP.EnterToken(Tok, /*IsReinject*/true);
+ Tok = OpToken;
+ return LHS;
+ }
+
+ // Special case handling for the ternary operator.
+ ExprResult TernaryMiddle(true);
+ if (NextTokPrec == prec::Conditional) {
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ // Parse a braced-init-list here for error recovery purposes.
+ SourceLocation BraceLoc = Tok.getLocation();
+ TernaryMiddle = ParseBraceInitializer();
+ if (!TernaryMiddle.isInvalid()) {
+ Diag(BraceLoc, diag::err_init_list_bin_op)
+ << /*RHS*/ 1 << PP.getSpelling(OpToken)
+ << Actions.getExprRange(TernaryMiddle.get());
+ TernaryMiddle = ExprError();
+ }
+ } else if (Tok.isNot(tok::colon)) {
+ // Don't parse FOO:BAR as if it were a typo for FOO::BAR.
+ ColonProtectionRAIIObject X(*this);
+
+ // Handle this production specially:
+ // logical-OR-expression '?' expression ':' conditional-expression
+ // In particular, the RHS of the '?' is 'expression', not
+ // 'logical-OR-expression' as we might expect.
+ TernaryMiddle = ParseExpression();
+ } else {
+ // Special case handling of "X ? Y : Z" where Y is empty:
+ // logical-OR-expression '?' ':' conditional-expression [GNU]
+ TernaryMiddle = nullptr;
+ Diag(Tok, diag::ext_gnu_conditional_expr);
+ }
+
+ if (TernaryMiddle.isInvalid()) {
+ Actions.CorrectDelayedTyposInExpr(LHS);
+ LHS = ExprError();
+ TernaryMiddle = nullptr;
+ }
+
+ if (!TryConsumeToken(tok::colon, ColonLoc)) {
+ // Otherwise, we're missing a ':'. Assume that this was a typo that
+ // the user forgot. If we're not in a macro expansion, we can suggest
+ // a fixit hint. If there were two spaces before the current token,
+ // suggest inserting the colon in between them, otherwise insert ": ".
+ SourceLocation FILoc = Tok.getLocation();
+ const char *FIText = ": ";
+ const SourceManager &SM = PP.getSourceManager();
+ if (FILoc.isFileID() || PP.isAtStartOfMacroExpansion(FILoc, &FILoc)) {
+ assert(FILoc.isFileID());
+ bool IsInvalid = false;
+ const char *SourcePtr =
+ SM.getCharacterData(FILoc.getLocWithOffset(-1), &IsInvalid);
+ if (!IsInvalid && *SourcePtr == ' ') {
+ SourcePtr =
+ SM.getCharacterData(FILoc.getLocWithOffset(-2), &IsInvalid);
+ if (!IsInvalid && *SourcePtr == ' ') {
+ FILoc = FILoc.getLocWithOffset(-1);
+ FIText = ":";
+ }
+ }
+ }
+
+ Diag(Tok, diag::err_expected)
+ << tok::colon << FixItHint::CreateInsertion(FILoc, FIText);
+ Diag(OpToken, diag::note_matching) << tok::question;
+ ColonLoc = Tok.getLocation();
+ }
+ }
+
+ PreferredType.enterBinary(Actions, Tok.getLocation(), LHS.get(),
+ OpToken.getKind());
+ // Parse another leaf here for the RHS of the operator.
+ // ParseCastExpression works here because all RHS expressions in C have it
+ // as a prefix, at least. However, in C++, an assignment-expression could
+ // be a throw-expression, which is not a valid cast-expression.
+ // Therefore we need some special-casing here.
+ // Also note that the third operand of the conditional operator is
+ // an assignment-expression in C++, and in C++11, we can have a
+ // braced-init-list on the RHS of an assignment. For better diagnostics,
+ // parse as if we were allowed braced-init-lists everywhere, and check that
+ // they only appear on the RHS of assignments later.
+ ExprResult RHS;
+ bool RHSIsInitList = false;
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ RHS = ParseBraceInitializer();
+ RHSIsInitList = true;
+ } else if (getLangOpts().CPlusPlus && NextTokPrec <= prec::Conditional)
+ RHS = ParseAssignmentExpression();
+ else
+ RHS = ParseCastExpression(AnyCastExpr);
+
+ if (RHS.isInvalid()) {
+ // FIXME: Errors generated by the delayed typo correction should be
+ // printed before errors from parsing the RHS, not after.
+ Actions.CorrectDelayedTyposInExpr(LHS);
+ if (TernaryMiddle.isUsable())
+ TernaryMiddle = Actions.CorrectDelayedTyposInExpr(TernaryMiddle);
+ LHS = ExprError();
+ }
+
+ // Remember the precedence of this operator and get the precedence of the
+ // operator immediately to the right of the RHS.
+ prec::Level ThisPrec = NextTokPrec;
+ NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator,
+ getLangOpts().CPlusPlus11);
+
+ // Assignment and conditional expressions are right-associative.
+ bool isRightAssoc = ThisPrec == prec::Conditional ||
+ ThisPrec == prec::Assignment;
+
+ // Get the precedence of the operator to the right of the RHS. If it binds
+ // more tightly with RHS than we do, evaluate it completely first.
+ if (ThisPrec < NextTokPrec ||
+ (ThisPrec == NextTokPrec && isRightAssoc)) {
+ if (!RHS.isInvalid() && RHSIsInitList) {
+ Diag(Tok, diag::err_init_list_bin_op)
+ << /*LHS*/0 << PP.getSpelling(Tok) << Actions.getExprRange(RHS.get());
+ RHS = ExprError();
+ }
+ // If this is left-associative, only parse things on the RHS that bind
+ // more tightly than the current operator. If it is left-associative, it
+ // is okay, to bind exactly as tightly. For example, compile A=B=C=D as
+ // A=(B=(C=D)), where each paren is a level of recursion here.
+ // The function takes ownership of the RHS.
+ RHS = ParseRHSOfBinaryExpression(RHS,
+ static_cast<prec::Level>(ThisPrec + !isRightAssoc));
+ RHSIsInitList = false;
+
+ if (RHS.isInvalid()) {
+ // FIXME: Errors generated by the delayed typo correction should be
+ // printed before errors from ParseRHSOfBinaryExpression, not after.
+ Actions.CorrectDelayedTyposInExpr(LHS);
+ if (TernaryMiddle.isUsable())
+ TernaryMiddle = Actions.CorrectDelayedTyposInExpr(TernaryMiddle);
+ LHS = ExprError();
+ }
+
+ NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator,
+ getLangOpts().CPlusPlus11);
+ }
+
+ if (!RHS.isInvalid() && RHSIsInitList) {
+ if (ThisPrec == prec::Assignment) {
+ Diag(OpToken, diag::warn_cxx98_compat_generalized_initializer_lists)
+ << Actions.getExprRange(RHS.get());
+ } else if (ColonLoc.isValid()) {
+ Diag(ColonLoc, diag::err_init_list_bin_op)
+ << /*RHS*/1 << ":"
+ << Actions.getExprRange(RHS.get());
+ LHS = ExprError();
+ } else {
+ Diag(OpToken, diag::err_init_list_bin_op)
+ << /*RHS*/1 << PP.getSpelling(OpToken)
+ << Actions.getExprRange(RHS.get());
+ LHS = ExprError();
+ }
+ }
+
+ ExprResult OrigLHS = LHS;
+ if (!LHS.isInvalid()) {
+ // Combine the LHS and RHS into the LHS (e.g. build AST).
+ if (TernaryMiddle.isInvalid()) {
+ // If we're using '>>' as an operator within a template
+ // argument list (in C++98), suggest the addition of
+ // parentheses so that the code remains well-formed in C++0x.
+ if (!GreaterThanIsOperator && OpToken.is(tok::greatergreater))
+ SuggestParentheses(OpToken.getLocation(),
+ diag::warn_cxx11_right_shift_in_template_arg,
+ SourceRange(Actions.getExprRange(LHS.get()).getBegin(),
+ Actions.getExprRange(RHS.get()).getEnd()));
+
+ ExprResult BinOp =
+ Actions.ActOnBinOp(getCurScope(), OpToken.getLocation(),
+ OpToken.getKind(), LHS.get(), RHS.get());
+ if (BinOp.isInvalid())
+ BinOp = Actions.CreateRecoveryExpr(LHS.get()->getBeginLoc(),
+ RHS.get()->getEndLoc(),
+ {LHS.get(), RHS.get()});
+
+ LHS = BinOp;
+ } else {
+ ExprResult CondOp = Actions.ActOnConditionalOp(
+ OpToken.getLocation(), ColonLoc, LHS.get(), TernaryMiddle.get(),
+ RHS.get());
+ if (CondOp.isInvalid()) {
+ std::vector<clang::Expr *> Args;
+ // TernaryMiddle can be null for the GNU conditional expr extension.
+ if (TernaryMiddle.get())
+ Args = {LHS.get(), TernaryMiddle.get(), RHS.get()};
+ else
+ Args = {LHS.get(), RHS.get()};
+ CondOp = Actions.CreateRecoveryExpr(LHS.get()->getBeginLoc(),
+ RHS.get()->getEndLoc(), Args);
+ }
+
+ LHS = CondOp;
+ }
+ // In this case, ActOnBinOp or ActOnConditionalOp performed the
+ // CorrectDelayedTyposInExpr check.
+ if (!getLangOpts().CPlusPlus)
+ continue;
+ }
+
+ // Ensure potential typos aren't left undiagnosed.
+ if (LHS.isInvalid()) {
+ Actions.CorrectDelayedTyposInExpr(OrigLHS);
+ Actions.CorrectDelayedTyposInExpr(TernaryMiddle);
+ Actions.CorrectDelayedTyposInExpr(RHS);
+ }
+ }
+}
+
+/// Parse a cast-expression, unary-expression or primary-expression, based
+/// on \p ExprType.
+///
+/// \p isAddressOfOperand exists because an id-expression that is the
+/// operand of address-of gets special treatment due to member pointers.
+///
+ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
+ bool isAddressOfOperand,
+ TypeCastState isTypeCast,
+ bool isVectorLiteral,
+ bool *NotPrimaryExpression) {
+ bool NotCastExpr;
+ ExprResult Res = ParseCastExpression(ParseKind,
+ isAddressOfOperand,
+ NotCastExpr,
+ isTypeCast,
+ isVectorLiteral,
+ NotPrimaryExpression);
+ if (NotCastExpr)
+ Diag(Tok, diag::err_expected_expression);
+ return Res;
+}
+
+namespace {
+class CastExpressionIdValidator final : public CorrectionCandidateCallback {
+ public:
+ CastExpressionIdValidator(Token Next, bool AllowTypes, bool AllowNonTypes)
+ : NextToken(Next), AllowNonTypes(AllowNonTypes) {
+ WantTypeSpecifiers = WantFunctionLikeCasts = AllowTypes;
+ }
+
+ bool ValidateCandidate(const TypoCorrection &candidate) override {
+ NamedDecl *ND = candidate.getCorrectionDecl();
+ if (!ND)
+ return candidate.isKeyword();
+
+ if (isa<TypeDecl>(ND))
+ return WantTypeSpecifiers;
+
+ if (!AllowNonTypes || !CorrectionCandidateCallback::ValidateCandidate(candidate))
+ return false;
+
+ if (!NextToken.isOneOf(tok::equal, tok::arrow, tok::period))
+ return true;
+
+ for (auto *C : candidate) {
+ NamedDecl *ND = C->getUnderlyingDecl();
+ if (isa<ValueDecl>(ND) && !isa<FunctionDecl>(ND))
+ return true;
+ }
+ return false;
+ }
+
+ std::unique_ptr<CorrectionCandidateCallback> clone() override {
+ return std::make_unique<CastExpressionIdValidator>(*this);
+ }
+
+ private:
+ Token NextToken;
+ bool AllowNonTypes;
+};
+}
+
+/// Parse a cast-expression, or, if \pisUnaryExpression is true, parse
+/// a unary-expression.
+///
+/// \p isAddressOfOperand exists because an id-expression that is the operand
+/// of address-of gets special treatment due to member pointers. NotCastExpr
+/// is set to true if the token is not the start of a cast-expression, and no
+/// diagnostic is emitted in this case and no tokens are consumed.
+///
+/// \verbatim
+/// cast-expression: [C99 6.5.4]
+/// unary-expression
+/// '(' type-name ')' cast-expression
+///
+/// unary-expression: [C99 6.5.3]
+/// postfix-expression
+/// '++' unary-expression
+/// '--' unary-expression
+/// [Coro] 'co_await' cast-expression
+/// unary-operator cast-expression
+/// 'sizeof' unary-expression
+/// 'sizeof' '(' type-name ')'
+/// [C++11] 'sizeof' '...' '(' identifier ')'
+/// [GNU] '__alignof' unary-expression
+/// [GNU] '__alignof' '(' type-name ')'
+/// [C11] '_Alignof' '(' type-name ')'
+/// [C++11] 'alignof' '(' type-id ')'
+/// [GNU] '&&' identifier
+/// [C++11] 'noexcept' '(' expression ')' [C++11 5.3.7]
+/// [C++] new-expression
+/// [C++] delete-expression
+///
+/// unary-operator: one of
+/// '&' '*' '+' '-' '~' '!'
+/// [GNU] '__extension__' '__real' '__imag'
+///
+/// primary-expression: [C99 6.5.1]
+/// [C99] identifier
+/// [C++] id-expression
+/// constant
+/// string-literal
+/// [C++] boolean-literal [C++ 2.13.5]
+/// [C++11] 'nullptr' [C++11 2.14.7]
+/// [C++11] user-defined-literal
+/// '(' expression ')'
+/// [C11] generic-selection
+/// [C++2a] requires-expression
+/// '__func__' [C99 6.4.2.2]
+/// [GNU] '__FUNCTION__'
+/// [MS] '__FUNCDNAME__'
+/// [MS] 'L__FUNCTION__'
+/// [MS] '__FUNCSIG__'
+/// [MS] 'L__FUNCSIG__'
+/// [GNU] '__PRETTY_FUNCTION__'
+/// [GNU] '(' compound-statement ')'
+/// [GNU] '__builtin_va_arg' '(' assignment-expression ',' type-name ')'
+/// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')'
+/// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ','
+/// assign-expr ')'
+/// [GNU] '__builtin_FILE' '(' ')'
+/// [GNU] '__builtin_FUNCTION' '(' ')'
+/// [GNU] '__builtin_LINE' '(' ')'
+/// [CLANG] '__builtin_COLUMN' '(' ')'
+/// [GNU] '__builtin_source_location' '(' ')'
+/// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')'
+/// [GNU] '__null'
+/// [OBJC] '[' objc-message-expr ']'
+/// [OBJC] '\@selector' '(' objc-selector-arg ')'
+/// [OBJC] '\@protocol' '(' identifier ')'
+/// [OBJC] '\@encode' '(' type-name ')'
+/// [OBJC] objc-string-literal
+/// [C++] simple-type-specifier '(' expression-list[opt] ')' [C++ 5.2.3]
+/// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3]
+/// [C++] typename-specifier '(' expression-list[opt] ')' [C++ 5.2.3]
+/// [C++11] typename-specifier braced-init-list [C++11 5.2.3]
+/// [C++] 'const_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1]
+/// [C++] 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1]
+/// [C++] 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1]
+/// [C++] 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1]
+/// [C++] 'typeid' '(' expression ')' [C++ 5.2p1]
+/// [C++] 'typeid' '(' type-id ')' [C++ 5.2p1]
+/// [C++] 'this' [C++ 9.3.2]
+/// [G++] unary-type-trait '(' type-id ')'
+/// [G++] binary-type-trait '(' type-id ',' type-id ')' [TODO]
+/// [EMBT] array-type-trait '(' type-id ',' integer ')'
+/// [clang] '^' block-literal
+///
+/// constant: [C99 6.4.4]
+/// integer-constant
+/// floating-constant
+/// enumeration-constant -> identifier
+/// character-constant
+///
+/// id-expression: [C++ 5.1]
+/// unqualified-id
+/// qualified-id
+///
+/// unqualified-id: [C++ 5.1]
+/// identifier
+/// operator-function-id
+/// conversion-function-id
+/// '~' class-name
+/// template-id
+///
+/// new-expression: [C++ 5.3.4]
+/// '::'[opt] 'new' new-placement[opt] new-type-id
+/// new-initializer[opt]
+/// '::'[opt] 'new' new-placement[opt] '(' type-id ')'
+/// new-initializer[opt]
+///
+/// delete-expression: [C++ 5.3.5]
+/// '::'[opt] 'delete' cast-expression
+/// '::'[opt] 'delete' '[' ']' cast-expression
+///
+/// [GNU/Embarcadero] unary-type-trait:
+/// '__is_arithmetic'
+/// '__is_floating_point'
+/// '__is_integral'
+/// '__is_lvalue_expr'
+/// '__is_rvalue_expr'
+/// '__is_complete_type'
+/// '__is_void'
+/// '__is_array'
+/// '__is_function'
+/// '__is_reference'
+/// '__is_lvalue_reference'
+/// '__is_rvalue_reference'
+/// '__is_fundamental'
+/// '__is_object'
+/// '__is_scalar'
+/// '__is_compound'
+/// '__is_pointer'
+/// '__is_member_object_pointer'
+/// '__is_member_function_pointer'
+/// '__is_member_pointer'
+/// '__is_const'
+/// '__is_volatile'
+/// '__is_trivial'
+/// '__is_standard_layout'
+/// '__is_signed'
+/// '__is_unsigned'
+///
+/// [GNU] unary-type-trait:
+/// '__has_nothrow_assign'
+/// '__has_nothrow_copy'
+/// '__has_nothrow_constructor'
+/// '__has_trivial_assign' [TODO]
+/// '__has_trivial_copy' [TODO]
+/// '__has_trivial_constructor'
+/// '__has_trivial_destructor'
+/// '__has_virtual_destructor'
+/// '__is_abstract' [TODO]
+/// '__is_class'
+/// '__is_empty' [TODO]
+/// '__is_enum'
+/// '__is_final'
+/// '__is_pod'
+/// '__is_polymorphic'
+/// '__is_sealed' [MS]
+/// '__is_trivial'
+/// '__is_union'
+/// '__has_unique_object_representations'
+///
+/// [Clang] unary-type-trait:
+/// '__is_aggregate'
+/// '__trivially_copyable'
+///
+/// binary-type-trait:
+/// [GNU] '__is_base_of'
+/// [MS] '__is_convertible_to'
+/// '__is_convertible'
+/// '__is_same'
+///
+/// [Embarcadero] array-type-trait:
+/// '__array_rank'
+/// '__array_extent'
+///
+/// [Embarcadero] expression-trait:
+/// '__is_lvalue_expr'
+/// '__is_rvalue_expr'
+/// \endverbatim
+///
+ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
+ bool isAddressOfOperand,
+ bool &NotCastExpr,
+ TypeCastState isTypeCast,
+ bool isVectorLiteral,
+ bool *NotPrimaryExpression) {
+ ExprResult Res;
+ tok::TokenKind SavedKind = Tok.getKind();
+ auto SavedType = PreferredType;
+ NotCastExpr = false;
+
+ // Are postfix-expression suffix operators permitted after this
+ // cast-expression? If not, and we find some, we'll parse them anyway and
+ // diagnose them.
+ bool AllowSuffix = true;
+
+ // This handles all of cast-expression, unary-expression, postfix-expression,
+ // and primary-expression. We handle them together like this for efficiency
+ // and to simplify handling of an expression starting with a '(' token: which
+ // may be one of a parenthesized expression, cast-expression, compound literal
+ // expression, or statement expression.
+ //
+ // If the parsed tokens consist of a primary-expression, the cases below
+ // break out of the switch; at the end we call ParsePostfixExpressionSuffix
+ // to handle the postfix expression suffixes. Cases that cannot be followed
+ // by postfix exprs should set AllowSuffix to false.
+ switch (SavedKind) {
+ case tok::l_paren: {
+ // If this expression is limited to being a unary-expression, the paren can
+ // not start a cast expression.
+ ParenParseOption ParenExprType;
+ switch (ParseKind) {
+ case CastParseKind::UnaryExprOnly:
+ assert(getLangOpts().CPlusPlus && "not possible to get here in C");
+ [[fallthrough]];
+ case CastParseKind::AnyCastExpr:
+ ParenExprType = ParenParseOption::CastExpr;
+ break;
+ case CastParseKind::PrimaryExprOnly:
+ ParenExprType = FoldExpr;
+ break;
+ }
+ ParsedType CastTy;
+ SourceLocation RParenLoc;
+ Res = ParseParenExpression(ParenExprType, false/*stopIfCastExr*/,
+ isTypeCast == IsTypeCast, CastTy, RParenLoc);
+
+ // FIXME: What should we do if a vector literal is followed by a
+ // postfix-expression suffix? Usually postfix operators are permitted on
+ // literals.
+ if (isVectorLiteral)
+ return Res;
+
+ switch (ParenExprType) {
+ case SimpleExpr: break; // Nothing else to do.
+ case CompoundStmt: break; // Nothing else to do.
+ case CompoundLiteral:
+ // We parsed '(' type-name ')' '{' ... '}'. If any suffixes of
+ // postfix-expression exist, parse them now.
+ break;
+ case CastExpr:
+ // We have parsed the cast-expression and no postfix-expr pieces are
+ // following.
+ return Res;
+ case FoldExpr:
+ // We only parsed a fold-expression. There might be postfix-expr pieces
+ // afterwards; parse them now.
+ break;
+ }
+
+ break;
+ }
+
+ // primary-expression
+ case tok::numeric_constant:
+ // constant: integer-constant
+ // constant: floating-constant
+
+ Res = Actions.ActOnNumericConstant(Tok, /*UDLScope*/getCurScope());
+ ConsumeToken();
+ break;
+
+ case tok::kw_true:
+ case tok::kw_false:
+ Res = ParseCXXBoolLiteral();
+ break;
+
+ case tok::kw___objc_yes:
+ case tok::kw___objc_no:
+ Res = ParseObjCBoolLiteral();
+ break;
+
+ case tok::kw_nullptr:
+ if (getLangOpts().CPlusPlus)
+ Diag(Tok, diag::warn_cxx98_compat_nullptr);
+ else
+ Diag(Tok, getLangOpts().C2x ? diag::warn_c17_compat_nullptr
+ : diag::ext_c_nullptr);
+
+ Res = Actions.ActOnCXXNullPtrLiteral(ConsumeToken());
+ break;
+
+ case tok::annot_primary_expr:
+ case tok::annot_overload_set:
+ Res = getExprAnnotation(Tok);
+ if (!Res.isInvalid() && Tok.getKind() == tok::annot_overload_set)
+ Res = Actions.ActOnNameClassifiedAsOverloadSet(getCurScope(), Res.get());
+ ConsumeAnnotationToken();
+ if (!Res.isInvalid() && Tok.is(tok::less))
+ checkPotentialAngleBracket(Res);
+ break;
+
+ case tok::annot_non_type:
+ case tok::annot_non_type_dependent:
+ case tok::annot_non_type_undeclared: {
+ CXXScopeSpec SS;
+ Token Replacement;
+ Res = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement);
+ assert(!Res.isUnset() &&
+ "should not perform typo correction on annotation token");
+ break;
+ }
+
+ case tok::kw___super:
+ case tok::kw_decltype:
+ // Annotate the token and tail recurse.
+ if (TryAnnotateTypeOrScopeToken())
+ return ExprError();
+ assert(Tok.isNot(tok::kw_decltype) && Tok.isNot(tok::kw___super));
+ return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast,
+ isVectorLiteral, NotPrimaryExpression);
+
+ case tok::identifier:
+ ParseIdentifier: { // primary-expression: identifier
+ // unqualified-id: identifier
+ // constant: enumeration-constant
+ // Turn a potentially qualified name into a annot_typename or
+ // annot_cxxscope if it would be valid. This handles things like x::y, etc.
+ if (getLangOpts().CPlusPlus) {
+ // Avoid the unnecessary parse-time lookup in the common case
+ // where the syntax forbids a type.
+ const Token &Next = NextToken();
+
+ // If this identifier was reverted from a token ID, and the next token
+ // is a parenthesis, this is likely to be a use of a type trait. Check
+ // those tokens.
+ if (Next.is(tok::l_paren) &&
+ Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo()->hasRevertedTokenIDToIdentifier()) {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ // Build up the mapping of revertible type traits, for future use.
+ if (RevertibleTypeTraits.empty()) {
+#define RTT_JOIN(X,Y) X##Y
+#define REVERTIBLE_TYPE_TRAIT(Name) \
+ RevertibleTypeTraits[PP.getIdentifierInfo(#Name)] \
+ = RTT_JOIN(tok::kw_,Name)
+
+ REVERTIBLE_TYPE_TRAIT(__is_abstract);
+ REVERTIBLE_TYPE_TRAIT(__is_aggregate);
+ REVERTIBLE_TYPE_TRAIT(__is_arithmetic);
+ REVERTIBLE_TYPE_TRAIT(__is_array);
+ REVERTIBLE_TYPE_TRAIT(__is_assignable);
+ REVERTIBLE_TYPE_TRAIT(__is_base_of);
+ REVERTIBLE_TYPE_TRAIT(__is_bounded_array);
+ REVERTIBLE_TYPE_TRAIT(__is_class);
+ REVERTIBLE_TYPE_TRAIT(__is_complete_type);
+ REVERTIBLE_TYPE_TRAIT(__is_compound);
+ REVERTIBLE_TYPE_TRAIT(__is_const);
+ REVERTIBLE_TYPE_TRAIT(__is_constructible);
+ REVERTIBLE_TYPE_TRAIT(__is_convertible);
+ REVERTIBLE_TYPE_TRAIT(__is_convertible_to);
+ REVERTIBLE_TYPE_TRAIT(__is_destructible);
+ REVERTIBLE_TYPE_TRAIT(__is_empty);
+ REVERTIBLE_TYPE_TRAIT(__is_enum);
+ REVERTIBLE_TYPE_TRAIT(__is_floating_point);
+ REVERTIBLE_TYPE_TRAIT(__is_final);
+ REVERTIBLE_TYPE_TRAIT(__is_function);
+ REVERTIBLE_TYPE_TRAIT(__is_fundamental);
+ REVERTIBLE_TYPE_TRAIT(__is_integral);
+ REVERTIBLE_TYPE_TRAIT(__is_interface_class);
+ REVERTIBLE_TYPE_TRAIT(__is_literal);
+ REVERTIBLE_TYPE_TRAIT(__is_lvalue_expr);
+ REVERTIBLE_TYPE_TRAIT(__is_lvalue_reference);
+ REVERTIBLE_TYPE_TRAIT(__is_member_function_pointer);
+ REVERTIBLE_TYPE_TRAIT(__is_member_object_pointer);
+ REVERTIBLE_TYPE_TRAIT(__is_member_pointer);
+ REVERTIBLE_TYPE_TRAIT(__is_nothrow_assignable);
+ REVERTIBLE_TYPE_TRAIT(__is_nothrow_constructible);
+ REVERTIBLE_TYPE_TRAIT(__is_nothrow_destructible);
+ REVERTIBLE_TYPE_TRAIT(__is_nullptr);
+ REVERTIBLE_TYPE_TRAIT(__is_object);
+ REVERTIBLE_TYPE_TRAIT(__is_pod);
+ REVERTIBLE_TYPE_TRAIT(__is_pointer);
+ REVERTIBLE_TYPE_TRAIT(__is_polymorphic);
+ REVERTIBLE_TYPE_TRAIT(__is_reference);
+ REVERTIBLE_TYPE_TRAIT(__is_referenceable);
+ REVERTIBLE_TYPE_TRAIT(__is_rvalue_expr);
+ REVERTIBLE_TYPE_TRAIT(__is_rvalue_reference);
+ REVERTIBLE_TYPE_TRAIT(__is_same);
+ REVERTIBLE_TYPE_TRAIT(__is_scalar);
+ REVERTIBLE_TYPE_TRAIT(__is_scoped_enum);
+ REVERTIBLE_TYPE_TRAIT(__is_sealed);
+ REVERTIBLE_TYPE_TRAIT(__is_signed);
+ REVERTIBLE_TYPE_TRAIT(__is_standard_layout);
+ REVERTIBLE_TYPE_TRAIT(__is_trivial);
+ REVERTIBLE_TYPE_TRAIT(__is_trivially_assignable);
+ REVERTIBLE_TYPE_TRAIT(__is_trivially_constructible);
+ REVERTIBLE_TYPE_TRAIT(__is_trivially_copyable);
+ REVERTIBLE_TYPE_TRAIT(__is_unbounded_array);
+ REVERTIBLE_TYPE_TRAIT(__is_union);
+ REVERTIBLE_TYPE_TRAIT(__is_unsigned);
+ REVERTIBLE_TYPE_TRAIT(__is_void);
+ REVERTIBLE_TYPE_TRAIT(__is_volatile);
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \
+ REVERTIBLE_TYPE_TRAIT(RTT_JOIN(__, Trait));
+#include "clang/Basic/TransformTypeTraits.def"
+#undef REVERTIBLE_TYPE_TRAIT
+#undef RTT_JOIN
+ }
+
+ // If we find that this is in fact the name of a type trait,
+ // update the token kind in place and parse again to treat it as
+ // the appropriate kind of type trait.
+ llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind>::iterator Known
+ = RevertibleTypeTraits.find(II);
+ if (Known != RevertibleTypeTraits.end()) {
+ Tok.setKind(Known->second);
+ return ParseCastExpression(ParseKind, isAddressOfOperand,
+ NotCastExpr, isTypeCast,
+ isVectorLiteral, NotPrimaryExpression);
+ }
+ }
+
+ if ((!ColonIsSacred && Next.is(tok::colon)) ||
+ Next.isOneOf(tok::coloncolon, tok::less, tok::l_paren,
+ tok::l_brace)) {
+ // If TryAnnotateTypeOrScopeToken annotates the token, tail recurse.
+ if (TryAnnotateTypeOrScopeToken())
+ return ExprError();
+ if (!Tok.is(tok::identifier))
+ return ParseCastExpression(ParseKind, isAddressOfOperand,
+ NotCastExpr, isTypeCast,
+ isVectorLiteral,
+ NotPrimaryExpression);
+ }
+ }
+
+ // Consume the identifier so that we can see if it is followed by a '(' or
+ // '.'.
+ IdentifierInfo &II = *Tok.getIdentifierInfo();
+ SourceLocation ILoc = ConsumeToken();
+
+ // Support 'Class.property' and 'super.property' notation.
+ if (getLangOpts().ObjC && Tok.is(tok::period) &&
+ (Actions.getTypeName(II, ILoc, getCurScope()) ||
+ // Allow the base to be 'super' if in an objc-method.
+ (&II == Ident_super && getCurScope()->isInObjcMethodScope()))) {
+ ConsumeToken();
+
+ if (Tok.is(tok::code_completion) && &II != Ident_super) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCClassPropertyRefExpr(
+ getCurScope(), II, ILoc, ExprStatementTokLoc == ILoc);
+ return ExprError();
+ }
+ // Allow either an identifier or the keyword 'class' (in C++).
+ if (Tok.isNot(tok::identifier) &&
+ !(getLangOpts().CPlusPlus && Tok.is(tok::kw_class))) {
+ Diag(Tok, diag::err_expected_property_name);
+ return ExprError();
+ }
+ IdentifierInfo &PropertyName = *Tok.getIdentifierInfo();
+ SourceLocation PropertyLoc = ConsumeToken();
+
+ Res = Actions.ActOnClassPropertyRefExpr(II, PropertyName,
+ ILoc, PropertyLoc);
+ break;
+ }
+
+ // In an Objective-C method, if we have "super" followed by an identifier,
+ // the token sequence is ill-formed. However, if there's a ':' or ']' after
+ // that identifier, this is probably a message send with a missing open
+ // bracket. Treat it as such.
+ if (getLangOpts().ObjC && &II == Ident_super && !InMessageExpression &&
+ getCurScope()->isInObjcMethodScope() &&
+ ((Tok.is(tok::identifier) &&
+ (NextToken().is(tok::colon) || NextToken().is(tok::r_square))) ||
+ Tok.is(tok::code_completion))) {
+ Res = ParseObjCMessageExpressionBody(SourceLocation(), ILoc, nullptr,
+ nullptr);
+ break;
+ }
+
+ // If we have an Objective-C class name followed by an identifier
+ // and either ':' or ']', this is an Objective-C class message
+ // send that's missing the opening '['. Recovery
+ // appropriately. Also take this path if we're performing code
+ // completion after an Objective-C class name.
+ if (getLangOpts().ObjC &&
+ ((Tok.is(tok::identifier) && !InMessageExpression) ||
+ Tok.is(tok::code_completion))) {
+ const Token& Next = NextToken();
+ if (Tok.is(tok::code_completion) ||
+ Next.is(tok::colon) || Next.is(tok::r_square))
+ if (ParsedType Typ = Actions.getTypeName(II, ILoc, getCurScope()))
+ if (Typ.get()->isObjCObjectOrInterfaceType()) {
+ // Fake up a Declarator to use with ActOnTypeName.
+ DeclSpec DS(AttrFactory);
+ DS.SetRangeStart(ILoc);
+ DS.SetRangeEnd(ILoc);
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ DS.SetTypeSpecType(TST_typename, ILoc, PrevSpec, DiagID, Typ,
+ Actions.getASTContext().getPrintingPolicy());
+
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ TypeResult Ty = Actions.ActOnTypeName(getCurScope(),
+ DeclaratorInfo);
+ if (Ty.isInvalid())
+ break;
+
+ Res = ParseObjCMessageExpressionBody(SourceLocation(),
+ SourceLocation(),
+ Ty.get(), nullptr);
+ break;
+ }
+ }
+
+ // Make sure to pass down the right value for isAddressOfOperand.
+ if (isAddressOfOperand && isPostfixExpressionSuffixStart())
+ isAddressOfOperand = false;
+
+ // Function designators are allowed to be undeclared (C99 6.5.1p2), so we
+ // need to know whether or not this identifier is a function designator or
+ // not.
+ UnqualifiedId Name;
+ CXXScopeSpec ScopeSpec;
+ SourceLocation TemplateKWLoc;
+ Token Replacement;
+ CastExpressionIdValidator Validator(
+ /*Next=*/Tok,
+ /*AllowTypes=*/isTypeCast != NotTypeCast,
+ /*AllowNonTypes=*/isTypeCast != IsTypeCast);
+ Validator.IsAddressOfOperand = isAddressOfOperand;
+ if (Tok.isOneOf(tok::periodstar, tok::arrowstar)) {
+ Validator.WantExpressionKeywords = false;
+ Validator.WantRemainingKeywords = false;
+ } else {
+ Validator.WantRemainingKeywords = Tok.isNot(tok::r_paren);
+ }
+ Name.setIdentifier(&II, ILoc);
+ Res = Actions.ActOnIdExpression(
+ getCurScope(), ScopeSpec, TemplateKWLoc, Name, Tok.is(tok::l_paren),
+ isAddressOfOperand, &Validator,
+ /*IsInlineAsmIdentifier=*/false,
+ Tok.is(tok::r_paren) ? nullptr : &Replacement);
+ if (!Res.isInvalid() && Res.isUnset()) {
+ UnconsumeToken(Replacement);
+ return ParseCastExpression(ParseKind, isAddressOfOperand,
+ NotCastExpr, isTypeCast,
+ /*isVectorLiteral=*/false,
+ NotPrimaryExpression);
+ }
+ if (!Res.isInvalid() && Tok.is(tok::less))
+ checkPotentialAngleBracket(Res);
+ break;
+ }
+ case tok::char_constant: // constant: character-constant
+ case tok::wide_char_constant:
+ case tok::utf8_char_constant:
+ case tok::utf16_char_constant:
+ case tok::utf32_char_constant:
+ Res = Actions.ActOnCharacterConstant(Tok, /*UDLScope*/getCurScope());
+ ConsumeToken();
+ break;
+ case tok::kw___func__: // primary-expression: __func__ [C99 6.4.2.2]
+ case tok::kw___FUNCTION__: // primary-expression: __FUNCTION__ [GNU]
+ case tok::kw___FUNCDNAME__: // primary-expression: __FUNCDNAME__ [MS]
+ case tok::kw___FUNCSIG__: // primary-expression: __FUNCSIG__ [MS]
+ case tok::kw_L__FUNCTION__: // primary-expression: L__FUNCTION__ [MS]
+ case tok::kw_L__FUNCSIG__: // primary-expression: L__FUNCSIG__ [MS]
+ case tok::kw___PRETTY_FUNCTION__: // primary-expression: __P..Y_F..N__ [GNU]
+ Res = Actions.ActOnPredefinedExpr(Tok.getLocation(), SavedKind);
+ ConsumeToken();
+ break;
+ case tok::string_literal: // primary-expression: string-literal
+ case tok::wide_string_literal:
+ case tok::utf8_string_literal:
+ case tok::utf16_string_literal:
+ case tok::utf32_string_literal:
+ Res = ParseStringLiteralExpression(true);
+ break;
+ case tok::kw__Generic: // primary-expression: generic-selection [C11 6.5.1]
+ Res = ParseGenericSelectionExpression();
+ break;
+ case tok::kw___builtin_available:
+ Res = ParseAvailabilityCheckExpr(Tok.getLocation());
+ break;
+ case tok::kw___builtin_va_arg:
+ case tok::kw___builtin_offsetof:
+ case tok::kw___builtin_choose_expr:
+ case tok::kw___builtin_astype: // primary-expression: [OCL] as_type()
+ case tok::kw___builtin_convertvector:
+ case tok::kw___builtin_COLUMN:
+ case tok::kw___builtin_FILE:
+ case tok::kw___builtin_FUNCTION:
+ case tok::kw___builtin_LINE:
+ case tok::kw___builtin_source_location:
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ // This parses the complete suffix; we can return early.
+ return ParseBuiltinPrimaryExpression();
+ case tok::kw___null:
+ Res = Actions.ActOnGNUNullExpr(ConsumeToken());
+ break;
+
+ case tok::plusplus: // unary-expression: '++' unary-expression [C99]
+ case tok::minusminus: { // unary-expression: '--' unary-expression [C99]
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ // C++ [expr.unary] has:
+ // unary-expression:
+ // ++ cast-expression
+ // -- cast-expression
+ Token SavedTok = Tok;
+ ConsumeToken();
+
+ PreferredType.enterUnary(Actions, Tok.getLocation(), SavedTok.getKind(),
+ SavedTok.getLocation());
+ // One special case is implicitly handled here: if the preceding tokens are
+ // an ambiguous cast expression, such as "(T())++", then we recurse to
+ // determine whether the '++' is prefix or postfix.
+ Res = ParseCastExpression(getLangOpts().CPlusPlus ?
+ UnaryExprOnly : AnyCastExpr,
+ /*isAddressOfOperand*/false, NotCastExpr,
+ NotTypeCast);
+ if (NotCastExpr) {
+ // If we return with NotCastExpr = true, we must not consume any tokens,
+ // so put the token back where we found it.
+ assert(Res.isInvalid());
+ UnconsumeToken(SavedTok);
+ return ExprError();
+ }
+ if (!Res.isInvalid()) {
+ Expr *Arg = Res.get();
+ Res = Actions.ActOnUnaryOp(getCurScope(), SavedTok.getLocation(),
+ SavedKind, Arg);
+ if (Res.isInvalid())
+ Res = Actions.CreateRecoveryExpr(SavedTok.getLocation(),
+ Arg->getEndLoc(), Arg);
+ }
+ return Res;
+ }
+ case tok::amp: { // unary-expression: '&' cast-expression
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ // Special treatment because of member pointers
+ SourceLocation SavedLoc = ConsumeToken();
+ PreferredType.enterUnary(Actions, Tok.getLocation(), tok::amp, SavedLoc);
+
+ Res = ParseCastExpression(AnyCastExpr, /*isAddressOfOperand=*/true);
+ if (!Res.isInvalid()) {
+ Expr *Arg = Res.get();
+ Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Arg);
+ if (Res.isInvalid())
+ Res = Actions.CreateRecoveryExpr(Tok.getLocation(), Arg->getEndLoc(),
+ Arg);
+ }
+ return Res;
+ }
+
+ case tok::star: // unary-expression: '*' cast-expression
+ case tok::plus: // unary-expression: '+' cast-expression
+ case tok::minus: // unary-expression: '-' cast-expression
+ case tok::tilde: // unary-expression: '~' cast-expression
+ case tok::exclaim: // unary-expression: '!' cast-expression
+ case tok::kw___real: // unary-expression: '__real' cast-expression [GNU]
+ case tok::kw___imag: { // unary-expression: '__imag' cast-expression [GNU]
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ SourceLocation SavedLoc = ConsumeToken();
+ PreferredType.enterUnary(Actions, Tok.getLocation(), SavedKind, SavedLoc);
+ Res = ParseCastExpression(AnyCastExpr);
+ if (!Res.isInvalid()) {
+ Expr *Arg = Res.get();
+ Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Arg,
+ isAddressOfOperand);
+ if (Res.isInvalid())
+ Res = Actions.CreateRecoveryExpr(SavedLoc, Arg->getEndLoc(), Arg);
+ }
+ return Res;
+ }
+
+ case tok::kw_co_await: { // unary-expression: 'co_await' cast-expression
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ SourceLocation CoawaitLoc = ConsumeToken();
+ Res = ParseCastExpression(AnyCastExpr);
+ if (!Res.isInvalid())
+ Res = Actions.ActOnCoawaitExpr(getCurScope(), CoawaitLoc, Res.get());
+ return Res;
+ }
+
+ case tok::kw___extension__:{//unary-expression:'__extension__' cast-expr [GNU]
+ // __extension__ silences extension warnings in the subexpression.
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ ExtensionRAIIObject O(Diags); // Use RAII to do this.
+ SourceLocation SavedLoc = ConsumeToken();
+ Res = ParseCastExpression(AnyCastExpr);
+ if (!Res.isInvalid())
+ Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
+ return Res;
+ }
+ case tok::kw__Alignof: // unary-expression: '_Alignof' '(' type-name ')'
+ if (!getLangOpts().C11)
+ Diag(Tok, diag::ext_c11_feature) << Tok.getName();
+ [[fallthrough]];
+ case tok::kw_alignof: // unary-expression: 'alignof' '(' type-id ')'
+ case tok::kw___alignof: // unary-expression: '__alignof' unary-expression
+ // unary-expression: '__alignof' '(' type-name ')'
+ case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression
+ // unary-expression: 'sizeof' '(' type-name ')'
+ case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression
+ // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')'
+ case tok::kw___builtin_omp_required_simd_align:
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ AllowSuffix = false;
+ Res = ParseUnaryExprOrTypeTraitExpression();
+ break;
+ case tok::ampamp: { // unary-expression: '&&' identifier
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ SourceLocation AmpAmpLoc = ConsumeToken();
+ if (Tok.isNot(tok::identifier))
+ return ExprError(Diag(Tok, diag::err_expected) << tok::identifier);
+
+ if (getCurScope()->getFnParent() == nullptr)
+ return ExprError(Diag(Tok, diag::err_address_of_label_outside_fn));
+
+ Diag(AmpAmpLoc, diag::ext_gnu_address_of_label);
+ LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(),
+ Tok.getLocation());
+ Res = Actions.ActOnAddrLabel(AmpAmpLoc, Tok.getLocation(), LD);
+ ConsumeToken();
+ AllowSuffix = false;
+ break;
+ }
+ case tok::kw_const_cast:
+ case tok::kw_dynamic_cast:
+ case tok::kw_reinterpret_cast:
+ case tok::kw_static_cast:
+ case tok::kw_addrspace_cast:
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseCXXCasts();
+ break;
+ case tok::kw___builtin_bit_cast:
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseBuiltinBitCast();
+ break;
+ case tok::kw_typeid:
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseCXXTypeid();
+ break;
+ case tok::kw___uuidof:
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseCXXUuidof();
+ break;
+ case tok::kw_this:
+ Res = ParseCXXThis();
+ break;
+ case tok::kw___builtin_sycl_unique_stable_name:
+ Res = ParseSYCLUniqueStableNameExpression();
+ break;
+
+ case tok::annot_typename:
+ if (isStartOfObjCClassMessageMissingOpenBracket()) {
+ TypeResult Type = getTypeAnnotation(Tok);
+
+ // Fake up a Declarator to use with ActOnTypeName.
+ DeclSpec DS(AttrFactory);
+ DS.SetRangeStart(Tok.getLocation());
+ DS.SetRangeEnd(Tok.getLastLoc());
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ DS.SetTypeSpecType(TST_typename, Tok.getAnnotationEndLoc(),
+ PrevSpec, DiagID, Type,
+ Actions.getASTContext().getPrintingPolicy());
+
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ TypeResult Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ if (Ty.isInvalid())
+ break;
+
+ ConsumeAnnotationToken();
+ Res = ParseObjCMessageExpressionBody(SourceLocation(), SourceLocation(),
+ Ty.get(), nullptr);
+ break;
+ }
+ [[fallthrough]];
+
+ case tok::annot_decltype:
+ case tok::kw_char:
+ case tok::kw_wchar_t:
+ case tok::kw_char8_t:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ case tok::kw_bool:
+ case tok::kw_short:
+ case tok::kw_int:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw__ExtInt:
+ case tok::kw__BitInt:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_half:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw___bf16:
+ case tok::kw__Float16:
+ case tok::kw___float128:
+ case tok::kw___ibm128:
+ case tok::kw_void:
+ case tok::kw_auto:
+ case tok::kw_typename:
+ case tok::kw_typeof:
+ case tok::kw___vector:
+#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
+#include "clang/Basic/OpenCLImageTypes.def"
+ {
+ if (!getLangOpts().CPlusPlus) {
+ Diag(Tok, diag::err_expected_expression);
+ return ExprError();
+ }
+
+ // Everything henceforth is a postfix-expression.
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+
+ if (SavedKind == tok::kw_typename) {
+ // postfix-expression: typename-specifier '(' expression-list[opt] ')'
+ // typename-specifier braced-init-list
+ if (TryAnnotateTypeOrScopeToken())
+ return ExprError();
+
+ if (!Actions.isSimpleTypeSpecifier(Tok.getKind()))
+ // We are trying to parse a simple-type-specifier but might not get such
+ // a token after error recovery.
+ return ExprError();
+ }
+
+ // postfix-expression: simple-type-specifier '(' expression-list[opt] ')'
+ // simple-type-specifier braced-init-list
+ //
+ DeclSpec DS(AttrFactory);
+
+ ParseCXXSimpleTypeSpecifier(DS);
+ if (Tok.isNot(tok::l_paren) &&
+ (!getLangOpts().CPlusPlus11 || Tok.isNot(tok::l_brace)))
+ return ExprError(Diag(Tok, diag::err_expected_lparen_after_type)
+ << DS.getSourceRange());
+
+ if (Tok.is(tok::l_brace))
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+
+ Res = ParseCXXTypeConstructExpression(DS);
+ break;
+ }
+
+ case tok::annot_cxxscope: { // [C++] id-expression: qualified-id
+ // If TryAnnotateTypeOrScopeToken annotates the token, tail recurse.
+ // (We can end up in this situation after tentative parsing.)
+ if (TryAnnotateTypeOrScopeToken())
+ return ExprError();
+ if (!Tok.is(tok::annot_cxxscope))
+ return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr,
+ isTypeCast, isVectorLiteral,
+ NotPrimaryExpression);
+
+ Token Next = NextToken();
+ if (Next.is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next);
+ if (TemplateId->Kind == TNK_Type_template) {
+ // We have a qualified template-id that we know refers to a
+ // type, translate it into a type and continue parsing as a
+ // cast expression.
+ CXXScopeSpec SS;
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false);
+ AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes);
+ return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr,
+ isTypeCast, isVectorLiteral,
+ NotPrimaryExpression);
+ }
+ }
+
+ // Parse as an id-expression.
+ Res = ParseCXXIdExpression(isAddressOfOperand);
+ break;
+ }
+
+ case tok::annot_template_id: { // [C++] template-id
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ if (TemplateId->Kind == TNK_Type_template) {
+ // We have a template-id that we know refers to a type,
+ // translate it into a type and continue parsing as a cast
+ // expression.
+ CXXScopeSpec SS;
+ AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes);
+ return ParseCastExpression(ParseKind, isAddressOfOperand,
+ NotCastExpr, isTypeCast, isVectorLiteral,
+ NotPrimaryExpression);
+ }
+
+ // Fall through to treat the template-id as an id-expression.
+ [[fallthrough]];
+ }
+
+ case tok::kw_operator: // [C++] id-expression: operator/conversion-function-id
+ Res = ParseCXXIdExpression(isAddressOfOperand);
+ break;
+
+ case tok::coloncolon: {
+ // ::foo::bar -> global qualified name etc. If TryAnnotateTypeOrScopeToken
+ // annotates the token, tail recurse.
+ if (TryAnnotateTypeOrScopeToken())
+ return ExprError();
+ if (!Tok.is(tok::coloncolon))
+ return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast,
+ isVectorLiteral, NotPrimaryExpression);
+
+ // ::new -> [C++] new-expression
+ // ::delete -> [C++] delete-expression
+ SourceLocation CCLoc = ConsumeToken();
+ if (Tok.is(tok::kw_new)) {
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseCXXNewExpression(true, CCLoc);
+ AllowSuffix = false;
+ break;
+ }
+ if (Tok.is(tok::kw_delete)) {
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseCXXDeleteExpression(true, CCLoc);
+ AllowSuffix = false;
+ break;
+ }
+
+ // This is not a type name or scope specifier, it is an invalid expression.
+ Diag(CCLoc, diag::err_expected_expression);
+ return ExprError();
+ }
+
+ case tok::kw_new: // [C++] new-expression
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseCXXNewExpression(false, Tok.getLocation());
+ AllowSuffix = false;
+ break;
+
+ case tok::kw_delete: // [C++] delete-expression
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseCXXDeleteExpression(false, Tok.getLocation());
+ AllowSuffix = false;
+ break;
+
+ case tok::kw_requires: // [C++2a] requires-expression
+ Res = ParseRequiresExpression();
+ AllowSuffix = false;
+ break;
+
+ case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')'
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Diag(Tok, diag::warn_cxx98_compat_noexcept_expr);
+ SourceLocation KeyLoc = ConsumeToken();
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "noexcept"))
+ return ExprError();
+ // C++11 [expr.unary.noexcept]p1:
+ // The noexcept operator determines whether the evaluation of its operand,
+ // which is an unevaluated operand, can throw an exception.
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+ Res = ParseExpression();
+
+ T.consumeClose();
+
+ if (!Res.isInvalid())
+ Res = Actions.ActOnNoexceptExpr(KeyLoc, T.getOpenLocation(), Res.get(),
+ T.getCloseLocation());
+ AllowSuffix = false;
+ break;
+ }
+
+#define TYPE_TRAIT(N,Spelling,K) \
+ case tok::kw_##Spelling:
+#include "clang/Basic/TokenKinds.def"
+ Res = ParseTypeTrait();
+ break;
+
+ case tok::kw___array_rank:
+ case tok::kw___array_extent:
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseArrayTypeTrait();
+ break;
+
+ case tok::kw___is_lvalue_expr:
+ case tok::kw___is_rvalue_expr:
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseExpressionTrait();
+ break;
+
+ case tok::at: {
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ SourceLocation AtLoc = ConsumeToken();
+ return ParseObjCAtExpression(AtLoc);
+ }
+ case tok::caret:
+ Res = ParseBlockLiteralExpression();
+ break;
+ case tok::code_completion: {
+ cutOffParsing();
+ Actions.CodeCompleteExpression(getCurScope(),
+ PreferredType.get(Tok.getLocation()));
+ return ExprError();
+ }
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
+ // HACK: libstdc++ uses some of the transform-type-traits as alias
+ // templates, so we need to work around this.
+ if (!NextToken().is(tok::l_paren)) {
+ Tok.setKind(tok::identifier);
+ Diag(Tok, diag::ext_keyword_as_ident)
+ << Tok.getIdentifierInfo()->getName() << 0;
+ goto ParseIdentifier;
+ }
+ goto ExpectedExpression;
+ case tok::l_square:
+ if (getLangOpts().CPlusPlus11) {
+ if (getLangOpts().ObjC) {
+ // C++11 lambda expressions and Objective-C message sends both start with a
+ // square bracket. There are three possibilities here:
+ // we have a valid lambda expression, we have an invalid lambda
+ // expression, or we have something that doesn't appear to be a lambda.
+ // If we're in the last case, we fall back to ParseObjCMessageExpression.
+ Res = TryParseLambdaExpression();
+ if (!Res.isInvalid() && !Res.get()) {
+ // We assume Objective-C++ message expressions are not
+ // primary-expressions.
+ if (NotPrimaryExpression)
+ *NotPrimaryExpression = true;
+ Res = ParseObjCMessageExpression();
+ }
+ break;
+ }
+ Res = ParseLambdaExpression();
+ break;
+ }
+ if (getLangOpts().ObjC) {
+ Res = ParseObjCMessageExpression();
+ break;
+ }
+ [[fallthrough]];
+ default:
+ ExpectedExpression:
+ NotCastExpr = true;
+ return ExprError();
+ }
+
+ // Check to see whether Res is a function designator only. If it is and we
+ // are compiling for OpenCL, we need to return an error as this implies
+ // that the address of the function is being taken, which is illegal in CL.
+
+ if (ParseKind == PrimaryExprOnly)
+ // This is strictly a primary-expression - no postfix-expr pieces should be
+ // parsed.
+ return Res;
+
+ if (!AllowSuffix) {
+ // FIXME: Don't parse a primary-expression suffix if we encountered a parse
+ // error already.
+ if (Res.isInvalid())
+ return Res;
+
+ switch (Tok.getKind()) {
+ case tok::l_square:
+ case tok::l_paren:
+ case tok::plusplus:
+ case tok::minusminus:
+ // "expected ';'" or similar is probably the right diagnostic here. Let
+ // the caller decide what to do.
+ if (Tok.isAtStartOfLine())
+ return Res;
+
+ [[fallthrough]];
+ case tok::period:
+ case tok::arrow:
+ break;
+
+ default:
+ return Res;
+ }
+
+ // This was a unary-expression for which a postfix-expression suffix is
+ // not permitted by the grammar (eg, a sizeof expression or
+ // new-expression or similar). Diagnose but parse the suffix anyway.
+ Diag(Tok.getLocation(), diag::err_postfix_after_unary_requires_parens)
+ << Tok.getKind() << Res.get()->getSourceRange()
+ << FixItHint::CreateInsertion(Res.get()->getBeginLoc(), "(")
+ << FixItHint::CreateInsertion(PP.getLocForEndOfToken(PrevTokLocation),
+ ")");
+ }
+
+ // These can be followed by postfix-expr pieces.
+ PreferredType = SavedType;
+ Res = ParsePostfixExpressionSuffix(Res);
+ if (getLangOpts().OpenCL &&
+ !getActions().getOpenCLOptions().isAvailableOption(
+ "__cl_clang_function_pointers", getLangOpts()))
+ if (Expr *PostfixExpr = Res.get()) {
+ QualType Ty = PostfixExpr->getType();
+ if (!Ty.isNull() && Ty->isFunctionType()) {
+ Diag(PostfixExpr->getExprLoc(),
+ diag::err_opencl_taking_function_address_parser);
+ return ExprError();
+ }
+ }
+
+ return Res;
+}
+
+/// Once the leading part of a postfix-expression is parsed, this
+/// method parses any suffixes that apply.
+///
+/// \verbatim
+/// postfix-expression: [C99 6.5.2]
+/// primary-expression
+/// postfix-expression '[' expression ']'
+/// postfix-expression '[' braced-init-list ']'
+/// postfix-expression '[' expression-list [opt] ']' [C++2b 12.4.5]
+/// postfix-expression '(' argument-expression-list[opt] ')'
+/// postfix-expression '.' identifier
+/// postfix-expression '->' identifier
+/// postfix-expression '++'
+/// postfix-expression '--'
+/// '(' type-name ')' '{' initializer-list '}'
+/// '(' type-name ')' '{' initializer-list ',' '}'
+///
+/// argument-expression-list: [C99 6.5.2]
+/// argument-expression ...[opt]
+/// argument-expression-list ',' assignment-expression ...[opt]
+/// \endverbatim
+ExprResult
+Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
+ // Now that the primary-expression piece of the postfix-expression has been
+ // parsed, see if there are any postfix-expression pieces here.
+ SourceLocation Loc;
+ auto SavedType = PreferredType;
+ while (true) {
+ // Each iteration relies on preferred type for the whole expression.
+ PreferredType = SavedType;
+ switch (Tok.getKind()) {
+ case tok::code_completion:
+ if (InMessageExpression)
+ return LHS;
+
+ cutOffParsing();
+ Actions.CodeCompletePostfixExpression(
+ getCurScope(), LHS, PreferredType.get(Tok.getLocation()));
+ return ExprError();
+
+ case tok::identifier:
+ // If we see identifier: after an expression, and we're not already in a
+ // message send, then this is probably a message send with a missing
+ // opening bracket '['.
+ if (getLangOpts().ObjC && !InMessageExpression &&
+ (NextToken().is(tok::colon) || NextToken().is(tok::r_square))) {
+ LHS = ParseObjCMessageExpressionBody(SourceLocation(), SourceLocation(),
+ nullptr, LHS.get());
+ break;
+ }
+ // Fall through; this isn't a message send.
+ [[fallthrough]];
+
+ default: // Not a postfix-expression suffix.
+ return LHS;
+ case tok::l_square: { // postfix-expression: p-e '[' expression ']'
+ // If we have a array postfix expression that starts on a new line and
+ // Objective-C is enabled, it is highly likely that the user forgot a
+ // semicolon after the base expression and that the array postfix-expr is
+ // actually another message send. In this case, do some look-ahead to see
+ // if the contents of the square brackets are obviously not a valid
+ // expression and recover by pretending there is no suffix.
+ if (getLangOpts().ObjC && Tok.isAtStartOfLine() &&
+ isSimpleObjCMessageExpression())
+ return LHS;
+
+ // Reject array indices starting with a lambda-expression. '[[' is
+ // reserved for attributes.
+ if (CheckProhibitedCXX11Attribute()) {
+ (void)Actions.CorrectDelayedTyposInExpr(LHS);
+ return ExprError();
+ }
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+ Loc = T.getOpenLocation();
+ ExprResult Length, Stride;
+ SourceLocation ColonLocFirst, ColonLocSecond;
+ ExprVector ArgExprs;
+ bool HasError = false;
+ PreferredType.enterSubscript(Actions, Tok.getLocation(), LHS.get());
+
+ // We try to parse a list of indexes in all language mode first
+ // and, in we find 0 or one index, we try to parse an OpenMP array
+ // section. This allow us to support C++2b multi dimensional subscript and
+ // OpenMp sections in the same language mode.
+ if (!getLangOpts().OpenMP || Tok.isNot(tok::colon)) {
+ if (!getLangOpts().CPlusPlus2b) {
+ ExprResult Idx;
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+ Idx = ParseBraceInitializer();
+ } else {
+ Idx = ParseExpression(); // May be a comma expression
+ }
+ LHS = Actions.CorrectDelayedTyposInExpr(LHS);
+ Idx = Actions.CorrectDelayedTyposInExpr(Idx);
+ if (Idx.isInvalid()) {
+ HasError = true;
+ } else {
+ ArgExprs.push_back(Idx.get());
+ }
+ } else if (Tok.isNot(tok::r_square)) {
+ if (ParseExpressionList(ArgExprs)) {
+ LHS = Actions.CorrectDelayedTyposInExpr(LHS);
+ HasError = true;
+ }
+ }
+ }
+
+ if (ArgExprs.size() <= 1 && getLangOpts().OpenMP) {
+ ColonProtectionRAIIObject RAII(*this);
+ if (Tok.is(tok::colon)) {
+ // Consume ':'
+ ColonLocFirst = ConsumeToken();
+ if (Tok.isNot(tok::r_square) &&
+ (getLangOpts().OpenMP < 50 ||
+ ((Tok.isNot(tok::colon) && getLangOpts().OpenMP >= 50)))) {
+ Length = ParseExpression();
+ Length = Actions.CorrectDelayedTyposInExpr(Length);
+ }
+ }
+ if (getLangOpts().OpenMP >= 50 &&
+ (OMPClauseKind == llvm::omp::Clause::OMPC_to ||
+ OMPClauseKind == llvm::omp::Clause::OMPC_from) &&
+ Tok.is(tok::colon)) {
+ // Consume ':'
+ ColonLocSecond = ConsumeToken();
+ if (Tok.isNot(tok::r_square)) {
+ Stride = ParseExpression();
+ }
+ }
+ }
+
+ SourceLocation RLoc = Tok.getLocation();
+ LHS = Actions.CorrectDelayedTyposInExpr(LHS);
+
+ if (!LHS.isInvalid() && !HasError && !Length.isInvalid() &&
+ !Stride.isInvalid() && Tok.is(tok::r_square)) {
+ if (ColonLocFirst.isValid() || ColonLocSecond.isValid()) {
+ LHS = Actions.ActOnOMPArraySectionExpr(
+ LHS.get(), Loc, ArgExprs.empty() ? nullptr : ArgExprs[0],
+ ColonLocFirst, ColonLocSecond, Length.get(), Stride.get(), RLoc);
+ } else {
+ LHS = Actions.ActOnArraySubscriptExpr(getCurScope(), LHS.get(), Loc,
+ ArgExprs, RLoc);
+ }
+ } else {
+ LHS = ExprError();
+ }
+
+ // Match the ']'.
+ T.consumeClose();
+ break;
+ }
+
+ case tok::l_paren: // p-e: p-e '(' argument-expression-list[opt] ')'
+ case tok::lesslessless: { // p-e: p-e '<<<' argument-expression-list '>>>'
+ // '(' argument-expression-list[opt] ')'
+ tok::TokenKind OpKind = Tok.getKind();
+ InMessageExpressionRAIIObject InMessage(*this, false);
+
+ Expr *ExecConfig = nullptr;
+
+ BalancedDelimiterTracker PT(*this, tok::l_paren);
+
+ if (OpKind == tok::lesslessless) {
+ ExprVector ExecConfigExprs;
+ SourceLocation OpenLoc = ConsumeToken();
+
+ if (ParseSimpleExpressionList(ExecConfigExprs)) {
+ (void)Actions.CorrectDelayedTyposInExpr(LHS);
+ LHS = ExprError();
+ }
+
+ SourceLocation CloseLoc;
+ if (TryConsumeToken(tok::greatergreatergreater, CloseLoc)) {
+ } else if (LHS.isInvalid()) {
+ SkipUntil(tok::greatergreatergreater, StopAtSemi);
+ } else {
+ // There was an error closing the brackets
+ Diag(Tok, diag::err_expected) << tok::greatergreatergreater;
+ Diag(OpenLoc, diag::note_matching) << tok::lesslessless;
+ SkipUntil(tok::greatergreatergreater, StopAtSemi);
+ LHS = ExprError();
+ }
+
+ if (!LHS.isInvalid()) {
+ if (ExpectAndConsume(tok::l_paren))
+ LHS = ExprError();
+ else
+ Loc = PrevTokLocation;
+ }
+
+ if (!LHS.isInvalid()) {
+ ExprResult ECResult = Actions.ActOnCUDAExecConfigExpr(getCurScope(),
+ OpenLoc,
+ ExecConfigExprs,
+ CloseLoc);
+ if (ECResult.isInvalid())
+ LHS = ExprError();
+ else
+ ExecConfig = ECResult.get();
+ }
+ } else {
+ PT.consumeOpen();
+ Loc = PT.getOpenLocation();
+ }
+
+ ExprVector ArgExprs;
+ auto RunSignatureHelp = [&]() -> QualType {
+ QualType PreferredType = Actions.ProduceCallSignatureHelp(
+ LHS.get(), ArgExprs, PT.getOpenLocation());
+ CalledSignatureHelp = true;
+ return PreferredType;
+ };
+ if (OpKind == tok::l_paren || !LHS.isInvalid()) {
+ if (Tok.isNot(tok::r_paren)) {
+ if (ParseExpressionList(ArgExprs, [&] {
+ PreferredType.enterFunctionArgument(Tok.getLocation(),
+ RunSignatureHelp);
+ })) {
+ (void)Actions.CorrectDelayedTyposInExpr(LHS);
+ // If we got an error when parsing expression list, we don't call
+ // the CodeCompleteCall handler inside the parser. So call it here
+ // to make sure we get overload suggestions even when we are in the
+ // middle of a parameter.
+ if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
+ RunSignatureHelp();
+ LHS = ExprError();
+ } else if (LHS.isInvalid()) {
+ for (auto &E : ArgExprs)
+ Actions.CorrectDelayedTyposInExpr(E);
+ }
+ }
+ }
+
+ // Match the ')'.
+ if (LHS.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ } else if (Tok.isNot(tok::r_paren)) {
+ bool HadDelayedTypo = false;
+ if (Actions.CorrectDelayedTyposInExpr(LHS).get() != LHS.get())
+ HadDelayedTypo = true;
+ for (auto &E : ArgExprs)
+ if (Actions.CorrectDelayedTyposInExpr(E).get() != E)
+ HadDelayedTypo = true;
+ // If there were delayed typos in the LHS or ArgExprs, call SkipUntil
+ // instead of PT.consumeClose() to avoid emitting extra diagnostics for
+ // the unmatched l_paren.
+ if (HadDelayedTypo)
+ SkipUntil(tok::r_paren, StopAtSemi);
+ else
+ PT.consumeClose();
+ LHS = ExprError();
+ } else {
+ Expr *Fn = LHS.get();
+ SourceLocation RParLoc = Tok.getLocation();
+ LHS = Actions.ActOnCallExpr(getCurScope(), Fn, Loc, ArgExprs, RParLoc,
+ ExecConfig);
+ if (LHS.isInvalid()) {
+ ArgExprs.insert(ArgExprs.begin(), Fn);
+ LHS =
+ Actions.CreateRecoveryExpr(Fn->getBeginLoc(), RParLoc, ArgExprs);
+ }
+ PT.consumeClose();
+ }
+
+ break;
+ }
+ case tok::arrow:
+ case tok::period: {
+ // postfix-expression: p-e '->' template[opt] id-expression
+ // postfix-expression: p-e '.' template[opt] id-expression
+ tok::TokenKind OpKind = Tok.getKind();
+ SourceLocation OpLoc = ConsumeToken(); // Eat the "." or "->" token.
+
+ CXXScopeSpec SS;
+ ParsedType ObjectType;
+ bool MayBePseudoDestructor = false;
+ Expr* OrigLHS = !LHS.isInvalid() ? LHS.get() : nullptr;
+
+ PreferredType.enterMemAccess(Actions, Tok.getLocation(), OrigLHS);
+
+ if (getLangOpts().CPlusPlus && !LHS.isInvalid()) {
+ Expr *Base = OrigLHS;
+ const Type* BaseType = Base->getType().getTypePtrOrNull();
+ if (BaseType && Tok.is(tok::l_paren) &&
+ (BaseType->isFunctionType() ||
+ BaseType->isSpecificPlaceholderType(BuiltinType::BoundMember))) {
+ Diag(OpLoc, diag::err_function_is_not_record)
+ << OpKind << Base->getSourceRange()
+ << FixItHint::CreateRemoval(OpLoc);
+ return ParsePostfixExpressionSuffix(Base);
+ }
+
+ LHS = Actions.ActOnStartCXXMemberReference(getCurScope(), Base, OpLoc,
+ OpKind, ObjectType,
+ MayBePseudoDestructor);
+ if (LHS.isInvalid()) {
+ // Clang will try to perform expression based completion as a
+ // fallback, which is confusing in case of member references. So we
+ // stop here without any completions.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ return ExprError();
+ }
+ break;
+ }
+ ParseOptionalCXXScopeSpecifier(
+ SS, ObjectType, LHS.get() && LHS.get()->containsErrors(),
+ /*EnteringContext=*/false, &MayBePseudoDestructor);
+ if (SS.isNotEmpty())
+ ObjectType = nullptr;
+ }
+
+ if (Tok.is(tok::code_completion)) {
+ tok::TokenKind CorrectedOpKind =
+ OpKind == tok::arrow ? tok::period : tok::arrow;
+ ExprResult CorrectedLHS(/*Invalid=*/true);
+ if (getLangOpts().CPlusPlus && OrigLHS) {
+ // FIXME: Creating a TentativeAnalysisScope from outside Sema is a
+ // hack.
+ Sema::TentativeAnalysisScope Trap(Actions);
+ CorrectedLHS = Actions.ActOnStartCXXMemberReference(
+ getCurScope(), OrigLHS, OpLoc, CorrectedOpKind, ObjectType,
+ MayBePseudoDestructor);
+ }
+
+ Expr *Base = LHS.get();
+ Expr *CorrectedBase = CorrectedLHS.get();
+ if (!CorrectedBase && !getLangOpts().CPlusPlus)
+ CorrectedBase = Base;
+
+ // Code completion for a member access expression.
+ cutOffParsing();
+ Actions.CodeCompleteMemberReferenceExpr(
+ getCurScope(), Base, CorrectedBase, OpLoc, OpKind == tok::arrow,
+ Base && ExprStatementTokLoc == Base->getBeginLoc(),
+ PreferredType.get(Tok.getLocation()));
+
+ return ExprError();
+ }
+
+ if (MayBePseudoDestructor && !LHS.isInvalid()) {
+ LHS = ParseCXXPseudoDestructor(LHS.get(), OpLoc, OpKind, SS,
+ ObjectType);
+ break;
+ }
+
+ // Either the action has told us that this cannot be a
+ // pseudo-destructor expression (based on the type of base
+ // expression), or we didn't see a '~' in the right place. We
+ // can still parse a destructor name here, but in that case it
+ // names a real destructor.
+ // Allow explicit constructor calls in Microsoft mode.
+ // FIXME: Add support for explicit call of template constructor.
+ SourceLocation TemplateKWLoc;
+ UnqualifiedId Name;
+ if (getLangOpts().ObjC && OpKind == tok::period &&
+ Tok.is(tok::kw_class)) {
+ // Objective-C++:
+ // After a '.' in a member access expression, treat the keyword
+ // 'class' as if it were an identifier.
+ //
+ // This hack allows property access to the 'class' method because it is
+ // such a common method name. For other C++ keywords that are
+ // Objective-C method names, one must use the message send syntax.
+ IdentifierInfo *Id = Tok.getIdentifierInfo();
+ SourceLocation Loc = ConsumeToken();
+ Name.setIdentifier(Id, Loc);
+ } else if (ParseUnqualifiedId(
+ SS, ObjectType, LHS.get() && LHS.get()->containsErrors(),
+ /*EnteringContext=*/false,
+ /*AllowDestructorName=*/true,
+ /*AllowConstructorName=*/
+ getLangOpts().MicrosoftExt && SS.isNotEmpty(),
+ /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name)) {
+ (void)Actions.CorrectDelayedTyposInExpr(LHS);
+ LHS = ExprError();
+ }
+
+ if (!LHS.isInvalid())
+ LHS = Actions.ActOnMemberAccessExpr(getCurScope(), LHS.get(), OpLoc,
+ OpKind, SS, TemplateKWLoc, Name,
+ CurParsedObjCImpl ? CurParsedObjCImpl->Dcl
+ : nullptr);
+ if (!LHS.isInvalid()) {
+ if (Tok.is(tok::less))
+ checkPotentialAngleBracket(LHS);
+ } else if (OrigLHS && Name.isValid()) {
+ // Preserve the LHS if the RHS is an invalid member.
+ LHS = Actions.CreateRecoveryExpr(OrigLHS->getBeginLoc(),
+ Name.getEndLoc(), {OrigLHS});
+ }
+ break;
+ }
+ case tok::plusplus: // postfix-expression: postfix-expression '++'
+ case tok::minusminus: // postfix-expression: postfix-expression '--'
+ if (!LHS.isInvalid()) {
+ Expr *Arg = LHS.get();
+ LHS = Actions.ActOnPostfixUnaryOp(getCurScope(), Tok.getLocation(),
+ Tok.getKind(), Arg);
+ if (LHS.isInvalid())
+ LHS = Actions.CreateRecoveryExpr(Arg->getBeginLoc(),
+ Tok.getLocation(), Arg);
+ }
+ ConsumeToken();
+ break;
+ }
+ }
+}
+
+/// ParseExprAfterUnaryExprOrTypeTrait - We parsed a typeof/sizeof/alignof/
+/// vec_step and we are at the start of an expression or a parenthesized
+/// type-id. OpTok is the operand token (typeof/sizeof/alignof). Returns the
+/// expression (isCastExpr == false) or the type (isCastExpr == true).
+///
+/// \verbatim
+/// unary-expression: [C99 6.5.3]
+/// 'sizeof' unary-expression
+/// 'sizeof' '(' type-name ')'
+/// [GNU] '__alignof' unary-expression
+/// [GNU] '__alignof' '(' type-name ')'
+/// [C11] '_Alignof' '(' type-name ')'
+/// [C++0x] 'alignof' '(' type-id ')'
+///
+/// [GNU] typeof-specifier:
+/// typeof ( expressions )
+/// typeof ( type-name )
+/// [GNU/C++] typeof unary-expression
+/// [C2x] typeof-specifier:
+/// typeof '(' typeof-specifier-argument ')'
+/// typeof_unqual '(' typeof-specifier-argument ')'
+///
+/// typeof-specifier-argument:
+/// expression
+/// type-name
+///
+/// [OpenCL 1.1 6.11.12] vec_step built-in function:
+/// vec_step ( expressions )
+/// vec_step ( type-name )
+/// \endverbatim
+ExprResult
+Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
+ bool &isCastExpr,
+ ParsedType &CastTy,
+ SourceRange &CastRange) {
+
+ assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual, tok::kw_sizeof,
+ tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof,
+ tok::kw_vec_step,
+ tok::kw___builtin_omp_required_simd_align) &&
+ "Not a typeof/sizeof/alignof/vec_step expression!");
+
+ ExprResult Operand;
+
+ // If the operand doesn't start with an '(', it must be an expression.
+ if (Tok.isNot(tok::l_paren)) {
+ // If construct allows a form without parenthesis, user may forget to put
+ // pathenthesis around type name.
+ if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___alignof, tok::kw_alignof,
+ tok::kw__Alignof)) {
+ if (isTypeIdUnambiguously()) {
+ DeclSpec DS(AttrFactory);
+ ParseSpecifierQualifierList(DS);
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ ParseDeclarator(DeclaratorInfo);
+
+ SourceLocation LParenLoc = PP.getLocForEndOfToken(OpTok.getLocation());
+ SourceLocation RParenLoc = PP.getLocForEndOfToken(PrevTokLocation);
+ if (LParenLoc.isInvalid() || RParenLoc.isInvalid()) {
+ Diag(OpTok.getLocation(),
+ diag::err_expected_parentheses_around_typename)
+ << OpTok.getName();
+ } else {
+ Diag(LParenLoc, diag::err_expected_parentheses_around_typename)
+ << OpTok.getName() << FixItHint::CreateInsertion(LParenLoc, "(")
+ << FixItHint::CreateInsertion(RParenLoc, ")");
+ }
+ isCastExpr = true;
+ return ExprEmpty();
+ }
+ }
+
+ isCastExpr = false;
+ if (OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual) &&
+ !getLangOpts().CPlusPlus) {
+ Diag(Tok, diag::err_expected_after) << OpTok.getIdentifierInfo()
+ << tok::l_paren;
+ return ExprError();
+ }
+
+ Operand = ParseCastExpression(UnaryExprOnly);
+ } else {
+ // If it starts with a '(', we know that it is either a parenthesized
+ // type-name, or it is a unary-expression that starts with a compound
+ // literal, or starts with a primary-expression that is a parenthesized
+ // expression.
+ ParenParseOption ExprType = CastExpr;
+ SourceLocation LParenLoc = Tok.getLocation(), RParenLoc;
+
+ Operand = ParseParenExpression(ExprType, true/*stopIfCastExpr*/,
+ false, CastTy, RParenLoc);
+ CastRange = SourceRange(LParenLoc, RParenLoc);
+
+ // If ParseParenExpression parsed a '(typename)' sequence only, then this is
+ // a type.
+ if (ExprType == CastExpr) {
+ isCastExpr = true;
+ return ExprEmpty();
+ }
+
+ if (getLangOpts().CPlusPlus ||
+ !OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual)) {
+ // GNU typeof in C requires the expression to be parenthesized. Not so for
+ // sizeof/alignof or in C++. Therefore, the parenthesized expression is
+ // the start of a unary-expression, but doesn't include any postfix
+ // pieces. Parse these now if present.
+ if (!Operand.isInvalid())
+ Operand = ParsePostfixExpressionSuffix(Operand.get());
+ }
+ }
+
+ // If we get here, the operand to the typeof/sizeof/alignof was an expression.
+ isCastExpr = false;
+ return Operand;
+}
+
+/// Parse a __builtin_sycl_unique_stable_name expression. Accepts a type-id as
+/// a parameter.
+ExprResult Parser::ParseSYCLUniqueStableNameExpression() {
+ assert(Tok.is(tok::kw___builtin_sycl_unique_stable_name) &&
+ "Not __builtin_sycl_unique_stable_name");
+
+ SourceLocation OpLoc = ConsumeToken();
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+
+ // __builtin_sycl_unique_stable_name expressions are always parenthesized.
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ "__builtin_sycl_unique_stable_name"))
+ return ExprError();
+
+ TypeResult Ty = ParseTypeName();
+
+ if (Ty.isInvalid()) {
+ T.skipToEnd();
+ return ExprError();
+ }
+
+ if (T.consumeClose())
+ return ExprError();
+
+ return Actions.ActOnSYCLUniqueStableNameExpr(OpLoc, T.getOpenLocation(),
+ T.getCloseLocation(), Ty.get());
+}
+
+/// Parse a sizeof or alignof expression.
+///
+/// \verbatim
+/// unary-expression: [C99 6.5.3]
+/// 'sizeof' unary-expression
+/// 'sizeof' '(' type-name ')'
+/// [C++11] 'sizeof' '...' '(' identifier ')'
+/// [GNU] '__alignof' unary-expression
+/// [GNU] '__alignof' '(' type-name ')'
+/// [C11] '_Alignof' '(' type-name ')'
+/// [C++11] 'alignof' '(' type-id ')'
+/// \endverbatim
+ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
+ assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___alignof, tok::kw_alignof,
+ tok::kw__Alignof, tok::kw_vec_step,
+ tok::kw___builtin_omp_required_simd_align) &&
+ "Not a sizeof/alignof/vec_step expression!");
+ Token OpTok = Tok;
+ ConsumeToken();
+
+ // [C++11] 'sizeof' '...' '(' identifier ')'
+ if (Tok.is(tok::ellipsis) && OpTok.is(tok::kw_sizeof)) {
+ SourceLocation EllipsisLoc = ConsumeToken();
+ SourceLocation LParenLoc, RParenLoc;
+ IdentifierInfo *Name = nullptr;
+ SourceLocation NameLoc;
+ if (Tok.is(tok::l_paren)) {
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ LParenLoc = T.getOpenLocation();
+ if (Tok.is(tok::identifier)) {
+ Name = Tok.getIdentifierInfo();
+ NameLoc = ConsumeToken();
+ T.consumeClose();
+ RParenLoc = T.getCloseLocation();
+ if (RParenLoc.isInvalid())
+ RParenLoc = PP.getLocForEndOfToken(NameLoc);
+ } else {
+ Diag(Tok, diag::err_expected_parameter_pack);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ }
+ } else if (Tok.is(tok::identifier)) {
+ Name = Tok.getIdentifierInfo();
+ NameLoc = ConsumeToken();
+ LParenLoc = PP.getLocForEndOfToken(EllipsisLoc);
+ RParenLoc = PP.getLocForEndOfToken(NameLoc);
+ Diag(LParenLoc, diag::err_paren_sizeof_parameter_pack)
+ << Name
+ << FixItHint::CreateInsertion(LParenLoc, "(")
+ << FixItHint::CreateInsertion(RParenLoc, ")");
+ } else {
+ Diag(Tok, diag::err_sizeof_parameter_pack);
+ }
+
+ if (!Name)
+ return ExprError();
+
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated,
+ Sema::ReuseLambdaContextDecl);
+
+ return Actions.ActOnSizeofParameterPackExpr(getCurScope(),
+ OpTok.getLocation(),
+ *Name, NameLoc,
+ RParenLoc);
+ }
+
+ if (OpTok.isOneOf(tok::kw_alignof, tok::kw__Alignof))
+ Diag(OpTok, diag::warn_cxx98_compat_alignof);
+
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated,
+ Sema::ReuseLambdaContextDecl);
+
+ bool isCastExpr;
+ ParsedType CastTy;
+ SourceRange CastRange;
+ ExprResult Operand = ParseExprAfterUnaryExprOrTypeTrait(OpTok,
+ isCastExpr,
+ CastTy,
+ CastRange);
+
+ UnaryExprOrTypeTrait ExprKind = UETT_SizeOf;
+ if (OpTok.isOneOf(tok::kw_alignof, tok::kw__Alignof))
+ ExprKind = UETT_AlignOf;
+ else if (OpTok.is(tok::kw___alignof))
+ ExprKind = UETT_PreferredAlignOf;
+ else if (OpTok.is(tok::kw_vec_step))
+ ExprKind = UETT_VecStep;
+ else if (OpTok.is(tok::kw___builtin_omp_required_simd_align))
+ ExprKind = UETT_OpenMPRequiredSimdAlign;
+
+ if (isCastExpr)
+ return Actions.ActOnUnaryExprOrTypeTraitExpr(OpTok.getLocation(),
+ ExprKind,
+ /*IsType=*/true,
+ CastTy.getAsOpaquePtr(),
+ CastRange);
+
+ if (OpTok.isOneOf(tok::kw_alignof, tok::kw__Alignof))
+ Diag(OpTok, diag::ext_alignof_expr) << OpTok.getIdentifierInfo();
+
+ // If we get here, the operand to the sizeof/alignof was an expression.
+ if (!Operand.isInvalid())
+ Operand = Actions.ActOnUnaryExprOrTypeTraitExpr(OpTok.getLocation(),
+ ExprKind,
+ /*IsType=*/false,
+ Operand.get(),
+ CastRange);
+ return Operand;
+}
+
+/// ParseBuiltinPrimaryExpression
+///
+/// \verbatim
+/// primary-expression: [C99 6.5.1]
+/// [GNU] '__builtin_va_arg' '(' assignment-expression ',' type-name ')'
+/// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')'
+/// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ','
+/// assign-expr ')'
+/// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')'
+/// [GNU] '__builtin_FILE' '(' ')'
+/// [GNU] '__builtin_FUNCTION' '(' ')'
+/// [GNU] '__builtin_LINE' '(' ')'
+/// [CLANG] '__builtin_COLUMN' '(' ')'
+/// [GNU] '__builtin_source_location' '(' ')'
+/// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')'
+///
+/// [GNU] offsetof-member-designator:
+/// [GNU] identifier
+/// [GNU] offsetof-member-designator '.' identifier
+/// [GNU] offsetof-member-designator '[' expression ']'
+/// \endverbatim
+ExprResult Parser::ParseBuiltinPrimaryExpression() {
+ ExprResult Res;
+ const IdentifierInfo *BuiltinII = Tok.getIdentifierInfo();
+
+ tok::TokenKind T = Tok.getKind();
+ SourceLocation StartLoc = ConsumeToken(); // Eat the builtin identifier.
+
+ // All of these start with an open paren.
+ if (Tok.isNot(tok::l_paren))
+ return ExprError(Diag(Tok, diag::err_expected_after) << BuiltinII
+ << tok::l_paren);
+
+ BalancedDelimiterTracker PT(*this, tok::l_paren);
+ PT.consumeOpen();
+
+ // TODO: Build AST.
+
+ switch (T) {
+ default: llvm_unreachable("Not a builtin primary expression!");
+ case tok::kw___builtin_va_arg: {
+ ExprResult Expr(ParseAssignmentExpression());
+
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ Expr = ExprError();
+ }
+
+ TypeResult Ty = ParseTypeName();
+
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ Expr = ExprError();
+ }
+
+ if (Expr.isInvalid() || Ty.isInvalid())
+ Res = ExprError();
+ else
+ Res = Actions.ActOnVAArg(StartLoc, Expr.get(), Ty.get(), ConsumeParen());
+ break;
+ }
+ case tok::kw___builtin_offsetof: {
+ SourceLocation TypeLoc = Tok.getLocation();
+ auto OOK = Sema::OffsetOfKind::OOK_Builtin;
+ if (Tok.getLocation().isMacroID()) {
+ StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
+ Tok.getLocation(), PP.getSourceManager(), getLangOpts());
+ if (MacroName == "offsetof")
+ OOK = Sema::OffsetOfKind::OOK_Macro;
+ }
+ TypeResult Ty;
+ {
+ OffsetOfStateRAIIObject InOffsetof(*this, OOK);
+ Ty = ParseTypeName();
+ if (Ty.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+ }
+
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ // We must have at least one identifier here.
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ // Keep track of the various subcomponents we see.
+ SmallVector<Sema::OffsetOfComponent, 4> Comps;
+
+ Comps.push_back(Sema::OffsetOfComponent());
+ Comps.back().isBrackets = false;
+ Comps.back().U.IdentInfo = Tok.getIdentifierInfo();
+ Comps.back().LocStart = Comps.back().LocEnd = ConsumeToken();
+
+ // FIXME: This loop leaks the index expressions on error.
+ while (true) {
+ if (Tok.is(tok::period)) {
+ // offsetof-member-designator: offsetof-member-designator '.' identifier
+ Comps.push_back(Sema::OffsetOfComponent());
+ Comps.back().isBrackets = false;
+ Comps.back().LocStart = ConsumeToken();
+
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+ Comps.back().U.IdentInfo = Tok.getIdentifierInfo();
+ Comps.back().LocEnd = ConsumeToken();
+ } else if (Tok.is(tok::l_square)) {
+ if (CheckProhibitedCXX11Attribute())
+ return ExprError();
+
+ // offsetof-member-designator: offsetof-member-design '[' expression ']'
+ Comps.push_back(Sema::OffsetOfComponent());
+ Comps.back().isBrackets = true;
+ BalancedDelimiterTracker ST(*this, tok::l_square);
+ ST.consumeOpen();
+ Comps.back().LocStart = ST.getOpenLocation();
+ Res = ParseExpression();
+ if (Res.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return Res;
+ }
+ Comps.back().U.E = Res.get();
+
+ ST.consumeClose();
+ Comps.back().LocEnd = ST.getCloseLocation();
+ } else {
+ if (Tok.isNot(tok::r_paren)) {
+ PT.consumeClose();
+ Res = ExprError();
+ } else if (Ty.isInvalid()) {
+ Res = ExprError();
+ } else {
+ PT.consumeClose();
+ Res = Actions.ActOnBuiltinOffsetOf(getCurScope(), StartLoc, TypeLoc,
+ Ty.get(), Comps,
+ PT.getCloseLocation());
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case tok::kw___builtin_choose_expr: {
+ ExprResult Cond(ParseAssignmentExpression());
+ if (Cond.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return Cond;
+ }
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ ExprResult Expr1(ParseAssignmentExpression());
+ if (Expr1.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return Expr1;
+ }
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ ExprResult Expr2(ParseAssignmentExpression());
+ if (Expr2.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return Expr2;
+ }
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ return ExprError();
+ }
+ Res = Actions.ActOnChooseExpr(StartLoc, Cond.get(), Expr1.get(),
+ Expr2.get(), ConsumeParen());
+ break;
+ }
+ case tok::kw___builtin_astype: {
+ // The first argument is an expression to be converted, followed by a comma.
+ ExprResult Expr(ParseAssignmentExpression());
+ if (Expr.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ // Second argument is the type to bitcast to.
+ TypeResult DestTy = ParseTypeName();
+ if (DestTy.isInvalid())
+ return ExprError();
+
+ // Attempt to consume the r-paren.
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ Res = Actions.ActOnAsTypeExpr(Expr.get(), DestTy.get(), StartLoc,
+ ConsumeParen());
+ break;
+ }
+ case tok::kw___builtin_convertvector: {
+ // The first argument is an expression to be converted, followed by a comma.
+ ExprResult Expr(ParseAssignmentExpression());
+ if (Expr.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ // Second argument is the type to bitcast to.
+ TypeResult DestTy = ParseTypeName();
+ if (DestTy.isInvalid())
+ return ExprError();
+
+ // Attempt to consume the r-paren.
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ Res = Actions.ActOnConvertVectorExpr(Expr.get(), DestTy.get(), StartLoc,
+ ConsumeParen());
+ break;
+ }
+ case tok::kw___builtin_COLUMN:
+ case tok::kw___builtin_FILE:
+ case tok::kw___builtin_FUNCTION:
+ case tok::kw___builtin_LINE:
+ case tok::kw___builtin_source_location: {
+ // Attempt to consume the r-paren.
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+ SourceLocExpr::IdentKind Kind = [&] {
+ switch (T) {
+ case tok::kw___builtin_FILE:
+ return SourceLocExpr::File;
+ case tok::kw___builtin_FUNCTION:
+ return SourceLocExpr::Function;
+ case tok::kw___builtin_LINE:
+ return SourceLocExpr::Line;
+ case tok::kw___builtin_COLUMN:
+ return SourceLocExpr::Column;
+ case tok::kw___builtin_source_location:
+ return SourceLocExpr::SourceLocStruct;
+ default:
+ llvm_unreachable("invalid keyword");
+ }
+ }();
+ Res = Actions.ActOnSourceLocExpr(Kind, StartLoc, ConsumeParen());
+ break;
+ }
+ }
+
+ if (Res.isInvalid())
+ return ExprError();
+
+ // These can be followed by postfix-expr pieces because they are
+ // primary-expressions.
+ return ParsePostfixExpressionSuffix(Res.get());
+}
+
+bool Parser::tryParseOpenMPArrayShapingCastPart() {
+ assert(Tok.is(tok::l_square) && "Expected open bracket");
+ bool ErrorFound = true;
+ TentativeParsingAction TPA(*this);
+ do {
+ if (Tok.isNot(tok::l_square))
+ break;
+ // Consume '['
+ ConsumeBracket();
+ // Skip inner expression.
+ while (!SkipUntil(tok::r_square, tok::annot_pragma_openmp_end,
+ StopAtSemi | StopBeforeMatch))
+ ;
+ if (Tok.isNot(tok::r_square))
+ break;
+ // Consume ']'
+ ConsumeBracket();
+ // Found ')' - done.
+ if (Tok.is(tok::r_paren)) {
+ ErrorFound = false;
+ break;
+ }
+ } while (Tok.isNot(tok::annot_pragma_openmp_end));
+ TPA.Revert();
+ return !ErrorFound;
+}
+
+/// ParseParenExpression - This parses the unit that starts with a '(' token,
+/// based on what is allowed by ExprType. The actual thing parsed is returned
+/// in ExprType. If stopIfCastExpr is true, it will only return the parsed type,
+/// not the parsed cast-expression.
+///
+/// \verbatim
+/// primary-expression: [C99 6.5.1]
+/// '(' expression ')'
+/// [GNU] '(' compound-statement ')' (if !ParenExprOnly)
+/// postfix-expression: [C99 6.5.2]
+/// '(' type-name ')' '{' initializer-list '}'
+/// '(' type-name ')' '{' initializer-list ',' '}'
+/// cast-expression: [C99 6.5.4]
+/// '(' type-name ')' cast-expression
+/// [ARC] bridged-cast-expression
+/// [ARC] bridged-cast-expression:
+/// (__bridge type-name) cast-expression
+/// (__bridge_transfer type-name) cast-expression
+/// (__bridge_retained type-name) cast-expression
+/// fold-expression: [C++1z]
+/// '(' cast-expression fold-operator '...' ')'
+/// '(' '...' fold-operator cast-expression ')'
+/// '(' cast-expression fold-operator '...'
+/// fold-operator cast-expression ')'
+/// [OPENMP] Array shaping operation
+/// '(' '[' expression ']' { '[' expression ']' } cast-expression
+/// \endverbatim
+ExprResult
+Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
+ bool isTypeCast, ParsedType &CastTy,
+ SourceLocation &RParenLoc) {
+ assert(Tok.is(tok::l_paren) && "Not a paren expr!");
+ ColonProtectionRAIIObject ColonProtection(*this, false);
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen())
+ return ExprError();
+ SourceLocation OpenLoc = T.getOpenLocation();
+
+ PreferredType.enterParenExpr(Tok.getLocation(), OpenLoc);
+
+ ExprResult Result(true);
+ bool isAmbiguousTypeId;
+ CastTy = nullptr;
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteExpression(
+ getCurScope(), PreferredType.get(Tok.getLocation()),
+ /*IsParenthesized=*/ExprType >= CompoundLiteral);
+ return ExprError();
+ }
+
+ // Diagnose use of bridge casts in non-arc mode.
+ bool BridgeCast = (getLangOpts().ObjC &&
+ Tok.isOneOf(tok::kw___bridge,
+ tok::kw___bridge_transfer,
+ tok::kw___bridge_retained,
+ tok::kw___bridge_retain));
+ if (BridgeCast && !getLangOpts().ObjCAutoRefCount) {
+ if (!TryConsumeToken(tok::kw___bridge)) {
+ StringRef BridgeCastName = Tok.getName();
+ SourceLocation BridgeKeywordLoc = ConsumeToken();
+ if (!PP.getSourceManager().isInSystemHeader(BridgeKeywordLoc))
+ Diag(BridgeKeywordLoc, diag::warn_arc_bridge_cast_nonarc)
+ << BridgeCastName
+ << FixItHint::CreateReplacement(BridgeKeywordLoc, "");
+ }
+ BridgeCast = false;
+ }
+
+ // None of these cases should fall through with an invalid Result
+ // unless they've already reported an error.
+ if (ExprType >= CompoundStmt && Tok.is(tok::l_brace)) {
+ Diag(Tok, OpenLoc.isMacroID() ? diag::ext_gnu_statement_expr_macro
+ : diag::ext_gnu_statement_expr);
+
+ checkCompoundToken(OpenLoc, tok::l_paren, CompoundToken::StmtExprBegin);
+
+ if (!getCurScope()->getFnParent() && !getCurScope()->getBlockParent()) {
+ Result = ExprError(Diag(OpenLoc, diag::err_stmtexpr_file_scope));
+ } else {
+ // Find the nearest non-record decl context. Variables declared in a
+ // statement expression behave as if they were declared in the enclosing
+ // function, block, or other code construct.
+ DeclContext *CodeDC = Actions.CurContext;
+ while (CodeDC->isRecord() || isa<EnumDecl>(CodeDC)) {
+ CodeDC = CodeDC->getParent();
+ assert(CodeDC && !CodeDC->isFileContext() &&
+ "statement expr not in code context");
+ }
+ Sema::ContextRAII SavedContext(Actions, CodeDC, /*NewThisContext=*/false);
+
+ Actions.ActOnStartStmtExpr();
+
+ StmtResult Stmt(ParseCompoundStatement(true));
+ ExprType = CompoundStmt;
+
+ // If the substmt parsed correctly, build the AST node.
+ if (!Stmt.isInvalid()) {
+ Result = Actions.ActOnStmtExpr(getCurScope(), OpenLoc, Stmt.get(),
+ Tok.getLocation());
+ } else {
+ Actions.ActOnStmtExprError();
+ }
+ }
+ } else if (ExprType >= CompoundLiteral && BridgeCast) {
+ tok::TokenKind tokenKind = Tok.getKind();
+ SourceLocation BridgeKeywordLoc = ConsumeToken();
+
+ // Parse an Objective-C ARC ownership cast expression.
+ ObjCBridgeCastKind Kind;
+ if (tokenKind == tok::kw___bridge)
+ Kind = OBC_Bridge;
+ else if (tokenKind == tok::kw___bridge_transfer)
+ Kind = OBC_BridgeTransfer;
+ else if (tokenKind == tok::kw___bridge_retained)
+ Kind = OBC_BridgeRetained;
+ else {
+ // As a hopefully temporary workaround, allow __bridge_retain as
+ // a synonym for __bridge_retained, but only in system headers.
+ assert(tokenKind == tok::kw___bridge_retain);
+ Kind = OBC_BridgeRetained;
+ if (!PP.getSourceManager().isInSystemHeader(BridgeKeywordLoc))
+ Diag(BridgeKeywordLoc, diag::err_arc_bridge_retain)
+ << FixItHint::CreateReplacement(BridgeKeywordLoc,
+ "__bridge_retained");
+ }
+
+ TypeResult Ty = ParseTypeName();
+ T.consumeClose();
+ ColonProtection.restore();
+ RParenLoc = T.getCloseLocation();
+
+ PreferredType.enterTypeCast(Tok.getLocation(), Ty.get().get());
+ ExprResult SubExpr = ParseCastExpression(AnyCastExpr);
+
+ if (Ty.isInvalid() || SubExpr.isInvalid())
+ return ExprError();
+
+ return Actions.ActOnObjCBridgedCast(getCurScope(), OpenLoc, Kind,
+ BridgeKeywordLoc, Ty.get(),
+ RParenLoc, SubExpr.get());
+ } else if (ExprType >= CompoundLiteral &&
+ isTypeIdInParens(isAmbiguousTypeId)) {
+
+ // Otherwise, this is a compound literal expression or cast expression.
+
+ // In C++, if the type-id is ambiguous we disambiguate based on context.
+ // If stopIfCastExpr is true the context is a typeof/sizeof/alignof
+ // in which case we should treat it as type-id.
+ // if stopIfCastExpr is false, we need to determine the context past the
+ // parens, so we defer to ParseCXXAmbiguousParenExpression for that.
+ if (isAmbiguousTypeId && !stopIfCastExpr) {
+ ExprResult res = ParseCXXAmbiguousParenExpression(ExprType, CastTy, T,
+ ColonProtection);
+ RParenLoc = T.getCloseLocation();
+ return res;
+ }
+
+ // Parse the type declarator.
+ DeclSpec DS(AttrFactory);
+ ParseSpecifierQualifierList(DS);
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ ParseDeclarator(DeclaratorInfo);
+
+ // If our type is followed by an identifier and either ':' or ']', then
+ // this is probably an Objective-C message send where the leading '[' is
+ // missing. Recover as if that were the case.
+ if (!DeclaratorInfo.isInvalidType() && Tok.is(tok::identifier) &&
+ !InMessageExpression && getLangOpts().ObjC &&
+ (NextToken().is(tok::colon) || NextToken().is(tok::r_square))) {
+ TypeResult Ty;
+ {
+ InMessageExpressionRAIIObject InMessage(*this, false);
+ Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ }
+ Result = ParseObjCMessageExpressionBody(SourceLocation(),
+ SourceLocation(),
+ Ty.get(), nullptr);
+ } else {
+ // Match the ')'.
+ T.consumeClose();
+ ColonProtection.restore();
+ RParenLoc = T.getCloseLocation();
+ if (Tok.is(tok::l_brace)) {
+ ExprType = CompoundLiteral;
+ TypeResult Ty;
+ {
+ InMessageExpressionRAIIObject InMessage(*this, false);
+ Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ }
+ return ParseCompoundLiteralExpression(Ty.get(), OpenLoc, RParenLoc);
+ }
+
+ if (Tok.is(tok::l_paren)) {
+ // This could be OpenCL vector Literals
+ if (getLangOpts().OpenCL)
+ {
+ TypeResult Ty;
+ {
+ InMessageExpressionRAIIObject InMessage(*this, false);
+ Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ }
+ if(Ty.isInvalid())
+ {
+ return ExprError();
+ }
+ QualType QT = Ty.get().get().getCanonicalType();
+ if (QT->isVectorType())
+ {
+ // We parsed '(' vector-type-name ')' followed by '('
+
+ // Parse the cast-expression that follows it next.
+ // isVectorLiteral = true will make sure we don't parse any
+ // Postfix expression yet
+ Result = ParseCastExpression(/*isUnaryExpression=*/AnyCastExpr,
+ /*isAddressOfOperand=*/false,
+ /*isTypeCast=*/IsTypeCast,
+ /*isVectorLiteral=*/true);
+
+ if (!Result.isInvalid()) {
+ Result = Actions.ActOnCastExpr(getCurScope(), OpenLoc,
+ DeclaratorInfo, CastTy,
+ RParenLoc, Result.get());
+ }
+
+ // After we performed the cast we can check for postfix-expr pieces.
+ if (!Result.isInvalid()) {
+ Result = ParsePostfixExpressionSuffix(Result);
+ }
+
+ return Result;
+ }
+ }
+ }
+
+ if (ExprType == CastExpr) {
+ // We parsed '(' type-name ')' and the thing after it wasn't a '{'.
+
+ if (DeclaratorInfo.isInvalidType())
+ return ExprError();
+
+ // Note that this doesn't parse the subsequent cast-expression, it just
+ // returns the parsed type to the callee.
+ if (stopIfCastExpr) {
+ TypeResult Ty;
+ {
+ InMessageExpressionRAIIObject InMessage(*this, false);
+ Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ }
+ CastTy = Ty.get();
+ return ExprResult();
+ }
+
+ // Reject the cast of super idiom in ObjC.
+ if (Tok.is(tok::identifier) && getLangOpts().ObjC &&
+ Tok.getIdentifierInfo() == Ident_super &&
+ getCurScope()->isInObjcMethodScope() &&
+ GetLookAheadToken(1).isNot(tok::period)) {
+ Diag(Tok.getLocation(), diag::err_illegal_super_cast)
+ << SourceRange(OpenLoc, RParenLoc);
+ return ExprError();
+ }
+
+ PreferredType.enterTypeCast(Tok.getLocation(), CastTy.get());
+ // Parse the cast-expression that follows it next.
+ // TODO: For cast expression with CastTy.
+ Result = ParseCastExpression(/*isUnaryExpression=*/AnyCastExpr,
+ /*isAddressOfOperand=*/false,
+ /*isTypeCast=*/IsTypeCast);
+ if (!Result.isInvalid()) {
+ Result = Actions.ActOnCastExpr(getCurScope(), OpenLoc,
+ DeclaratorInfo, CastTy,
+ RParenLoc, Result.get());
+ }
+ return Result;
+ }
+
+ Diag(Tok, diag::err_expected_lbrace_in_compound_literal);
+ return ExprError();
+ }
+ } else if (ExprType >= FoldExpr && Tok.is(tok::ellipsis) &&
+ isFoldOperator(NextToken().getKind())) {
+ ExprType = FoldExpr;
+ return ParseFoldExpression(ExprResult(), T);
+ } else if (isTypeCast) {
+ // Parse the expression-list.
+ InMessageExpressionRAIIObject InMessage(*this, false);
+ ExprVector ArgExprs;
+
+ if (!ParseSimpleExpressionList(ArgExprs)) {
+ // FIXME: If we ever support comma expressions as operands to
+ // fold-expressions, we'll need to allow multiple ArgExprs here.
+ if (ExprType >= FoldExpr && ArgExprs.size() == 1 &&
+ isFoldOperator(Tok.getKind()) && NextToken().is(tok::ellipsis)) {
+ ExprType = FoldExpr;
+ return ParseFoldExpression(ArgExprs[0], T);
+ }
+
+ ExprType = SimpleExpr;
+ Result = Actions.ActOnParenListExpr(OpenLoc, Tok.getLocation(),
+ ArgExprs);
+ }
+ } else if (getLangOpts().OpenMP >= 50 && OpenMPDirectiveParsing &&
+ ExprType == CastExpr && Tok.is(tok::l_square) &&
+ tryParseOpenMPArrayShapingCastPart()) {
+ bool ErrorFound = false;
+ SmallVector<Expr *, 4> OMPDimensions;
+ SmallVector<SourceRange, 4> OMPBracketsRanges;
+ do {
+ BalancedDelimiterTracker TS(*this, tok::l_square);
+ TS.consumeOpen();
+ ExprResult NumElements =
+ Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ if (!NumElements.isUsable()) {
+ ErrorFound = true;
+ while (!SkipUntil(tok::r_square, tok::r_paren,
+ StopAtSemi | StopBeforeMatch))
+ ;
+ }
+ TS.consumeClose();
+ OMPDimensions.push_back(NumElements.get());
+ OMPBracketsRanges.push_back(TS.getRange());
+ } while (Tok.isNot(tok::r_paren));
+ // Match the ')'.
+ T.consumeClose();
+ RParenLoc = T.getCloseLocation();
+ Result = Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+ if (ErrorFound) {
+ Result = ExprError();
+ } else if (!Result.isInvalid()) {
+ Result = Actions.ActOnOMPArrayShapingExpr(
+ Result.get(), OpenLoc, RParenLoc, OMPDimensions, OMPBracketsRanges);
+ }
+ return Result;
+ } else {
+ InMessageExpressionRAIIObject InMessage(*this, false);
+
+ Result = ParseExpression(MaybeTypeCast);
+ if (!getLangOpts().CPlusPlus && Result.isUsable()) {
+ // Correct typos in non-C++ code earlier so that implicit-cast-like
+ // expressions are parsed correctly.
+ Result = Actions.CorrectDelayedTyposInExpr(Result);
+ }
+
+ if (ExprType >= FoldExpr && isFoldOperator(Tok.getKind()) &&
+ NextToken().is(tok::ellipsis)) {
+ ExprType = FoldExpr;
+ return ParseFoldExpression(Result, T);
+ }
+ ExprType = SimpleExpr;
+
+ // Don't build a paren expression unless we actually match a ')'.
+ if (!Result.isInvalid() && Tok.is(tok::r_paren))
+ Result =
+ Actions.ActOnParenExpr(OpenLoc, Tok.getLocation(), Result.get());
+ }
+
+ // Match the ')'.
+ if (Result.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ T.consumeClose();
+ RParenLoc = T.getCloseLocation();
+ return Result;
+}
+
+/// ParseCompoundLiteralExpression - We have parsed the parenthesized type-name
+/// and we are at the left brace.
+///
+/// \verbatim
+/// postfix-expression: [C99 6.5.2]
+/// '(' type-name ')' '{' initializer-list '}'
+/// '(' type-name ')' '{' initializer-list ',' '}'
+/// \endverbatim
+ExprResult
+Parser::ParseCompoundLiteralExpression(ParsedType Ty,
+ SourceLocation LParenLoc,
+ SourceLocation RParenLoc) {
+ assert(Tok.is(tok::l_brace) && "Not a compound literal!");
+ if (!getLangOpts().C99) // Compound literals don't exist in C90.
+ Diag(LParenLoc, diag::ext_c99_compound_literal);
+ PreferredType.enterTypeCast(Tok.getLocation(), Ty.get());
+ ExprResult Result = ParseInitializer();
+ if (!Result.isInvalid() && Ty)
+ return Actions.ActOnCompoundLiteral(LParenLoc, Ty, RParenLoc, Result.get());
+ return Result;
+}
+
+/// ParseStringLiteralExpression - This handles the various token types that
+/// form string literals, and also handles string concatenation [C99 5.1.1.2,
+/// translation phase #6].
+///
+/// \verbatim
+/// primary-expression: [C99 6.5.1]
+/// string-literal
+/// \verbatim
+ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) {
+ assert(isTokenStringLiteral() && "Not a string literal!");
+
+ // String concat. Note that keywords like __func__ and __FUNCTION__ are not
+ // considered to be strings for concatenation purposes.
+ SmallVector<Token, 4> StringToks;
+
+ do {
+ StringToks.push_back(Tok);
+ ConsumeStringToken();
+ } while (isTokenStringLiteral());
+
+ // Pass the set of string tokens, ready for concatenation, to the actions.
+ return Actions.ActOnStringLiteral(StringToks,
+ AllowUserDefinedLiteral ? getCurScope()
+ : nullptr);
+}
+
+/// ParseGenericSelectionExpression - Parse a C11 generic-selection
+/// [C11 6.5.1.1].
+///
+/// \verbatim
+/// generic-selection:
+/// _Generic ( assignment-expression , generic-assoc-list )
+/// generic-assoc-list:
+/// generic-association
+/// generic-assoc-list , generic-association
+/// generic-association:
+/// type-name : assignment-expression
+/// default : assignment-expression
+/// \endverbatim
+ExprResult Parser::ParseGenericSelectionExpression() {
+ assert(Tok.is(tok::kw__Generic) && "_Generic keyword expected");
+ if (!getLangOpts().C11)
+ Diag(Tok, diag::ext_c11_feature) << Tok.getName();
+
+ SourceLocation KeyLoc = ConsumeToken();
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume())
+ return ExprError();
+
+ ExprResult ControllingExpr;
+ {
+ // C11 6.5.1.1p3 "The controlling expression of a generic selection is
+ // not evaluated."
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+ ControllingExpr =
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+ if (ControllingExpr.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+ }
+
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ SourceLocation DefaultLoc;
+ SmallVector<ParsedType, 12> Types;
+ ExprVector Exprs;
+ do {
+ ParsedType Ty;
+ if (Tok.is(tok::kw_default)) {
+ // C11 6.5.1.1p2 "A generic selection shall have no more than one default
+ // generic association."
+ if (!DefaultLoc.isInvalid()) {
+ Diag(Tok, diag::err_duplicate_default_assoc);
+ Diag(DefaultLoc, diag::note_previous_default_assoc);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+ DefaultLoc = ConsumeToken();
+ Ty = nullptr;
+ } else {
+ ColonProtectionRAIIObject X(*this);
+ TypeResult TR = ParseTypeName(nullptr, DeclaratorContext::Association);
+ if (TR.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+ Ty = TR.get();
+ }
+ Types.push_back(Ty);
+
+ if (ExpectAndConsume(tok::colon)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ // FIXME: These expressions should be parsed in a potentially potentially
+ // evaluated context.
+ ExprResult ER(
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
+ if (ER.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+ Exprs.push_back(ER.get());
+ } while (TryConsumeToken(tok::comma));
+
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid())
+ return ExprError();
+
+ return Actions.ActOnGenericSelectionExpr(KeyLoc, DefaultLoc,
+ T.getCloseLocation(),
+ ControllingExpr.get(),
+ Types, Exprs);
+}
+
+/// Parse A C++1z fold-expression after the opening paren and optional
+/// left-hand-side expression.
+///
+/// \verbatim
+/// fold-expression:
+/// ( cast-expression fold-operator ... )
+/// ( ... fold-operator cast-expression )
+/// ( cast-expression fold-operator ... fold-operator cast-expression )
+ExprResult Parser::ParseFoldExpression(ExprResult LHS,
+ BalancedDelimiterTracker &T) {
+ if (LHS.isInvalid()) {
+ T.skipToEnd();
+ return true;
+ }
+
+ tok::TokenKind Kind = tok::unknown;
+ SourceLocation FirstOpLoc;
+ if (LHS.isUsable()) {
+ Kind = Tok.getKind();
+ assert(isFoldOperator(Kind) && "missing fold-operator");
+ FirstOpLoc = ConsumeToken();
+ }
+
+ assert(Tok.is(tok::ellipsis) && "not a fold-expression");
+ SourceLocation EllipsisLoc = ConsumeToken();
+
+ ExprResult RHS;
+ if (Tok.isNot(tok::r_paren)) {
+ if (!isFoldOperator(Tok.getKind()))
+ return Diag(Tok.getLocation(), diag::err_expected_fold_operator);
+
+ if (Kind != tok::unknown && Tok.getKind() != Kind)
+ Diag(Tok.getLocation(), diag::err_fold_operator_mismatch)
+ << SourceRange(FirstOpLoc);
+ Kind = Tok.getKind();
+ ConsumeToken();
+
+ RHS = ParseExpression();
+ if (RHS.isInvalid()) {
+ T.skipToEnd();
+ return true;
+ }
+ }
+
+ Diag(EllipsisLoc, getLangOpts().CPlusPlus17
+ ? diag::warn_cxx14_compat_fold_expression
+ : diag::ext_fold_expression);
+
+ T.consumeClose();
+ return Actions.ActOnCXXFoldExpr(getCurScope(), T.getOpenLocation(), LHS.get(),
+ Kind, EllipsisLoc, RHS.get(),
+ T.getCloseLocation());
+}
+
+/// ParseExpressionList - Used for C/C++ (argument-)expression-list.
+///
+/// \verbatim
+/// argument-expression-list:
+/// assignment-expression
+/// argument-expression-list , assignment-expression
+///
+/// [C++] expression-list:
+/// [C++] assignment-expression
+/// [C++] expression-list , assignment-expression
+///
+/// [C++0x] expression-list:
+/// [C++0x] initializer-list
+///
+/// [C++0x] initializer-list
+/// [C++0x] initializer-clause ...[opt]
+/// [C++0x] initializer-list , initializer-clause ...[opt]
+///
+/// [C++0x] initializer-clause:
+/// [C++0x] assignment-expression
+/// [C++0x] braced-init-list
+/// \endverbatim
+bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
+ llvm::function_ref<void()> ExpressionStarts,
+ bool FailImmediatelyOnInvalidExpr,
+ bool EarlyTypoCorrection) {
+ bool SawError = false;
+ while (true) {
+ if (ExpressionStarts)
+ ExpressionStarts();
+
+ ExprResult Expr;
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+ Expr = ParseBraceInitializer();
+ } else
+ Expr = ParseAssignmentExpression();
+
+ if (EarlyTypoCorrection)
+ Expr = Actions.CorrectDelayedTyposInExpr(Expr);
+
+ if (Tok.is(tok::ellipsis))
+ Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken());
+ else if (Tok.is(tok::code_completion)) {
+ // There's nothing to suggest in here as we parsed a full expression.
+ // Instead fail and propogate the error since caller might have something
+ // the suggest, e.g. signature help in function call. Note that this is
+ // performed before pushing the \p Expr, so that signature help can report
+ // current argument correctly.
+ SawError = true;
+ cutOffParsing();
+ break;
+ }
+ if (Expr.isInvalid()) {
+ SawError = true;
+ if (FailImmediatelyOnInvalidExpr)
+ break;
+ SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch);
+ } else {
+ Exprs.push_back(Expr.get());
+ }
+
+ if (Tok.isNot(tok::comma))
+ break;
+ // Move to the next argument, remember where the comma was.
+ Token Comma = Tok;
+ ConsumeToken();
+ checkPotentialAngleBracketDelimiter(Comma);
+ }
+ if (SawError) {
+ // Ensure typos get diagnosed when errors were encountered while parsing the
+ // expression list.
+ for (auto &E : Exprs) {
+ ExprResult Expr = Actions.CorrectDelayedTyposInExpr(E);
+ if (Expr.isUsable()) E = Expr.get();
+ }
+ }
+ return SawError;
+}
+
+/// ParseSimpleExpressionList - A simple comma-separated list of expressions,
+/// used for misc language extensions.
+///
+/// \verbatim
+/// simple-expression-list:
+/// assignment-expression
+/// simple-expression-list , assignment-expression
+/// \endverbatim
+bool Parser::ParseSimpleExpressionList(SmallVectorImpl<Expr *> &Exprs) {
+ while (true) {
+ ExprResult Expr = ParseAssignmentExpression();
+ if (Expr.isInvalid())
+ return true;
+
+ Exprs.push_back(Expr.get());
+
+ // We might be parsing the LHS of a fold-expression. If we reached the fold
+ // operator, stop.
+ if (Tok.isNot(tok::comma) || NextToken().is(tok::ellipsis))
+ return false;
+
+ // Move to the next argument, remember where the comma was.
+ Token Comma = Tok;
+ ConsumeToken();
+ checkPotentialAngleBracketDelimiter(Comma);
+ }
+}
+
+/// ParseBlockId - Parse a block-id, which roughly looks like int (int x).
+///
+/// \verbatim
+/// [clang] block-id:
+/// [clang] specifier-qualifier-list block-declarator
+/// \endverbatim
+void Parser::ParseBlockId(SourceLocation CaretLoc) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Type);
+ return;
+ }
+
+ // Parse the specifier-qualifier-list piece.
+ DeclSpec DS(AttrFactory);
+ ParseSpecifierQualifierList(DS);
+
+ // Parse the block-declarator.
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::BlockLiteral);
+ DeclaratorInfo.setFunctionDefinitionKind(FunctionDefinitionKind::Definition);
+ ParseDeclarator(DeclaratorInfo);
+
+ MaybeParseGNUAttributes(DeclaratorInfo);
+
+ // Inform sema that we are starting a block.
+ Actions.ActOnBlockArguments(CaretLoc, DeclaratorInfo, getCurScope());
+}
+
+/// ParseBlockLiteralExpression - Parse a block literal, which roughly looks
+/// like ^(int x){ return x+1; }
+///
+/// \verbatim
+/// block-literal:
+/// [clang] '^' block-args[opt] compound-statement
+/// [clang] '^' block-id compound-statement
+/// [clang] block-args:
+/// [clang] '(' parameter-list ')'
+/// \endverbatim
+ExprResult Parser::ParseBlockLiteralExpression() {
+ assert(Tok.is(tok::caret) && "block literal starts with ^");
+ SourceLocation CaretLoc = ConsumeToken();
+
+ PrettyStackTraceLoc CrashInfo(PP.getSourceManager(), CaretLoc,
+ "block literal parsing");
+
+ // Enter a scope to hold everything within the block. This includes the
+ // argument decls, decls within the compound expression, etc. This also
+ // allows determining whether a variable reference inside the block is
+ // within or outside of the block.
+ ParseScope BlockScope(this, Scope::BlockScope | Scope::FnScope |
+ Scope::CompoundStmtScope | Scope::DeclScope);
+
+ // Inform sema that we are starting a block.
+ Actions.ActOnBlockStart(CaretLoc, getCurScope());
+
+ // Parse the return type if present.
+ DeclSpec DS(AttrFactory);
+ Declarator ParamInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::BlockLiteral);
+ ParamInfo.setFunctionDefinitionKind(FunctionDefinitionKind::Definition);
+ // FIXME: Since the return type isn't actually parsed, it can't be used to
+ // fill ParamInfo with an initial valid range, so do it manually.
+ ParamInfo.SetSourceRange(SourceRange(Tok.getLocation(), Tok.getLocation()));
+
+ // If this block has arguments, parse them. There is no ambiguity here with
+ // the expression case, because the expression case requires a parameter list.
+ if (Tok.is(tok::l_paren)) {
+ ParseParenDeclarator(ParamInfo);
+ // Parse the pieces after the identifier as if we had "int(...)".
+ // SetIdentifier sets the source range end, but in this case we're past
+ // that location.
+ SourceLocation Tmp = ParamInfo.getSourceRange().getEnd();
+ ParamInfo.SetIdentifier(nullptr, CaretLoc);
+ ParamInfo.SetRangeEnd(Tmp);
+ if (ParamInfo.isInvalidType()) {
+ // If there was an error parsing the arguments, they may have
+ // tried to use ^(x+y) which requires an argument list. Just
+ // skip the whole block literal.
+ Actions.ActOnBlockError(CaretLoc, getCurScope());
+ return ExprError();
+ }
+
+ MaybeParseGNUAttributes(ParamInfo);
+
+ // Inform sema that we are starting a block.
+ Actions.ActOnBlockArguments(CaretLoc, ParamInfo, getCurScope());
+ } else if (!Tok.is(tok::l_brace)) {
+ ParseBlockId(CaretLoc);
+ } else {
+ // Otherwise, pretend we saw (void).
+ SourceLocation NoLoc;
+ ParamInfo.AddTypeInfo(
+ DeclaratorChunk::getFunction(/*HasProto=*/true,
+ /*IsAmbiguous=*/false,
+ /*RParenLoc=*/NoLoc,
+ /*ArgInfo=*/nullptr,
+ /*NumParams=*/0,
+ /*EllipsisLoc=*/NoLoc,
+ /*RParenLoc=*/NoLoc,
+ /*RefQualifierIsLvalueRef=*/true,
+ /*RefQualifierLoc=*/NoLoc,
+ /*MutableLoc=*/NoLoc, EST_None,
+ /*ESpecRange=*/SourceRange(),
+ /*Exceptions=*/nullptr,
+ /*ExceptionRanges=*/nullptr,
+ /*NumExceptions=*/0,
+ /*NoexceptExpr=*/nullptr,
+ /*ExceptionSpecTokens=*/nullptr,
+ /*DeclsInPrototype=*/std::nullopt,
+ CaretLoc, CaretLoc, ParamInfo),
+ CaretLoc);
+
+ MaybeParseGNUAttributes(ParamInfo);
+
+ // Inform sema that we are starting a block.
+ Actions.ActOnBlockArguments(CaretLoc, ParamInfo, getCurScope());
+ }
+
+
+ ExprResult Result(true);
+ if (!Tok.is(tok::l_brace)) {
+ // Saw something like: ^expr
+ Diag(Tok, diag::err_expected_expression);
+ Actions.ActOnBlockError(CaretLoc, getCurScope());
+ return ExprError();
+ }
+
+ StmtResult Stmt(ParseCompoundStatementBody());
+ BlockScope.Exit();
+ if (!Stmt.isInvalid())
+ Result = Actions.ActOnBlockStmtExpr(CaretLoc, Stmt.get(), getCurScope());
+ else
+ Actions.ActOnBlockError(CaretLoc, getCurScope());
+ return Result;
+}
+
+/// ParseObjCBoolLiteral - This handles the objective-c Boolean literals.
+///
+/// '__objc_yes'
+/// '__objc_no'
+ExprResult Parser::ParseObjCBoolLiteral() {
+ tok::TokenKind Kind = Tok.getKind();
+ return Actions.ActOnObjCBoolLiteral(ConsumeToken(), Kind);
+}
+
+/// Validate availability spec list, emitting diagnostics if necessary. Returns
+/// true if invalid.
+static bool CheckAvailabilitySpecList(Parser &P,
+ ArrayRef<AvailabilitySpec> AvailSpecs) {
+ llvm::SmallSet<StringRef, 4> Platforms;
+ bool HasOtherPlatformSpec = false;
+ bool Valid = true;
+ for (const auto &Spec : AvailSpecs) {
+ if (Spec.isOtherPlatformSpec()) {
+ if (HasOtherPlatformSpec) {
+ P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_star);
+ Valid = false;
+ }
+
+ HasOtherPlatformSpec = true;
+ continue;
+ }
+
+ bool Inserted = Platforms.insert(Spec.getPlatform()).second;
+ if (!Inserted) {
+ // Rule out multiple version specs referring to the same platform.
+ // For example, we emit an error for:
+ // @available(macos 10.10, macos 10.11, *)
+ StringRef Platform = Spec.getPlatform();
+ P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_platform)
+ << Spec.getEndLoc() << Platform;
+ Valid = false;
+ }
+ }
+
+ if (!HasOtherPlatformSpec) {
+ SourceLocation InsertWildcardLoc = AvailSpecs.back().getEndLoc();
+ P.Diag(InsertWildcardLoc, diag::err_availability_query_wildcard_required)
+ << FixItHint::CreateInsertion(InsertWildcardLoc, ", *");
+ return true;
+ }
+
+ return !Valid;
+}
+
+/// Parse availability query specification.
+///
+/// availability-spec:
+/// '*'
+/// identifier version-tuple
+std::optional<AvailabilitySpec> Parser::ParseAvailabilitySpec() {
+ if (Tok.is(tok::star)) {
+ return AvailabilitySpec(ConsumeToken());
+ } else {
+ // Parse the platform name.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteAvailabilityPlatformName();
+ return std::nullopt;
+ }
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_avail_query_expected_platform_name);
+ return std::nullopt;
+ }
+
+ IdentifierLoc *PlatformIdentifier = ParseIdentifierLoc();
+ SourceRange VersionRange;
+ VersionTuple Version = ParseVersionTuple(VersionRange);
+
+ if (Version.empty())
+ return std::nullopt;
+
+ StringRef GivenPlatform = PlatformIdentifier->Ident->getName();
+ StringRef Platform =
+ AvailabilityAttr::canonicalizePlatformName(GivenPlatform);
+
+ if (AvailabilityAttr::getPrettyPlatformName(Platform).empty()) {
+ Diag(PlatformIdentifier->Loc,
+ diag::err_avail_query_unrecognized_platform_name)
+ << GivenPlatform;
+ return std::nullopt;
+ }
+
+ return AvailabilitySpec(Version, Platform, PlatformIdentifier->Loc,
+ VersionRange.getEnd());
+ }
+}
+
+ExprResult Parser::ParseAvailabilityCheckExpr(SourceLocation BeginLoc) {
+ assert(Tok.is(tok::kw___builtin_available) ||
+ Tok.isObjCAtKeyword(tok::objc_available));
+
+ // Eat the available or __builtin_available.
+ ConsumeToken();
+
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ if (Parens.expectAndConsume())
+ return ExprError();
+
+ SmallVector<AvailabilitySpec, 4> AvailSpecs;
+ bool HasError = false;
+ while (true) {
+ std::optional<AvailabilitySpec> Spec = ParseAvailabilitySpec();
+ if (!Spec)
+ HasError = true;
+ else
+ AvailSpecs.push_back(*Spec);
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ if (HasError) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ CheckAvailabilitySpecList(*this, AvailSpecs);
+
+ if (Parens.consumeClose())
+ return ExprError();
+
+ return Actions.ActOnObjCAvailabilityCheckExpr(AvailSpecs, BeginLoc,
+ Parens.getCloseLocation());
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseExprCXX.cpp b/contrib/libs/clang16/lib/Parse/ParseExprCXX.cpp
new file mode 100644
index 0000000000..7f09120574
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseExprCXX.cpp
@@ -0,0 +1,4099 @@
+//===--- ParseExprCXX.cpp - C++ Expression Parsing ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Expression parsing implementation for C++.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Basic/PrettyStackTrace.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/LiteralSupport.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/ParsedTemplate.h"
+#include "clang/Sema/Scope.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <numeric>
+
+using namespace clang;
+
+static int SelectDigraphErrorMessage(tok::TokenKind Kind) {
+ switch (Kind) {
+ // template name
+ case tok::unknown: return 0;
+ // casts
+ case tok::kw_addrspace_cast: return 1;
+ case tok::kw_const_cast: return 2;
+ case tok::kw_dynamic_cast: return 3;
+ case tok::kw_reinterpret_cast: return 4;
+ case tok::kw_static_cast: return 5;
+ default:
+ llvm_unreachable("Unknown type for digraph error message.");
+ }
+}
+
+// Are the two tokens adjacent in the same source file?
+bool Parser::areTokensAdjacent(const Token &First, const Token &Second) {
+ SourceManager &SM = PP.getSourceManager();
+ SourceLocation FirstLoc = SM.getSpellingLoc(First.getLocation());
+ SourceLocation FirstEnd = FirstLoc.getLocWithOffset(First.getLength());
+ return FirstEnd == SM.getSpellingLoc(Second.getLocation());
+}
+
+// Suggest fixit for "<::" after a cast.
+static void FixDigraph(Parser &P, Preprocessor &PP, Token &DigraphToken,
+ Token &ColonToken, tok::TokenKind Kind, bool AtDigraph) {
+ // Pull '<:' and ':' off token stream.
+ if (!AtDigraph)
+ PP.Lex(DigraphToken);
+ PP.Lex(ColonToken);
+
+ SourceRange Range;
+ Range.setBegin(DigraphToken.getLocation());
+ Range.setEnd(ColonToken.getLocation());
+ P.Diag(DigraphToken.getLocation(), diag::err_missing_whitespace_digraph)
+ << SelectDigraphErrorMessage(Kind)
+ << FixItHint::CreateReplacement(Range, "< ::");
+
+ // Update token information to reflect their change in token type.
+ ColonToken.setKind(tok::coloncolon);
+ ColonToken.setLocation(ColonToken.getLocation().getLocWithOffset(-1));
+ ColonToken.setLength(2);
+ DigraphToken.setKind(tok::less);
+ DigraphToken.setLength(1);
+
+ // Push new tokens back to token stream.
+ PP.EnterToken(ColonToken, /*IsReinject*/ true);
+ if (!AtDigraph)
+ PP.EnterToken(DigraphToken, /*IsReinject*/ true);
+}
+
+// Check for '<::' which should be '< ::' instead of '[:' when following
+// a template name.
+void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
+ bool EnteringContext,
+ IdentifierInfo &II, CXXScopeSpec &SS) {
+ if (!Next.is(tok::l_square) || Next.getLength() != 2)
+ return;
+
+ Token SecondToken = GetLookAheadToken(2);
+ if (!SecondToken.is(tok::colon) || !areTokensAdjacent(Next, SecondToken))
+ return;
+
+ TemplateTy Template;
+ UnqualifiedId TemplateName;
+ TemplateName.setIdentifier(&II, Tok.getLocation());
+ bool MemberOfUnknownSpecialization;
+ if (!Actions.isTemplateName(getCurScope(), SS, /*hasTemplateKeyword=*/false,
+ TemplateName, ObjectType, EnteringContext,
+ Template, MemberOfUnknownSpecialization))
+ return;
+
+ FixDigraph(*this, PP, Next, SecondToken, tok::unknown,
+ /*AtDigraph*/false);
+}
+
+/// Parse global scope or nested-name-specifier if present.
+///
+/// Parses a C++ global scope specifier ('::') or nested-name-specifier (which
+/// may be preceded by '::'). Note that this routine will not parse ::new or
+/// ::delete; it will just leave them in the token stream.
+///
+/// '::'[opt] nested-name-specifier
+/// '::'
+///
+/// nested-name-specifier:
+/// type-name '::'
+/// namespace-name '::'
+/// nested-name-specifier identifier '::'
+/// nested-name-specifier 'template'[opt] simple-template-id '::'
+///
+///
+/// \param SS the scope specifier that will be set to the parsed
+/// nested-name-specifier (or empty)
+///
+/// \param ObjectType if this nested-name-specifier is being parsed following
+/// the "." or "->" of a member access expression, this parameter provides the
+/// type of the object whose members are being accessed.
+///
+/// \param ObjectHadErrors if this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+/// When true, diagnostics for missing 'template' keyword will be supressed.
+///
+/// \param EnteringContext whether we will be entering into the context of
+/// the nested-name-specifier after parsing it.
+///
+/// \param MayBePseudoDestructor When non-NULL, points to a flag that
+/// indicates whether this nested-name-specifier may be part of a
+/// pseudo-destructor name. In this case, the flag will be set false
+/// if we don't actually end up parsing a destructor name. Moreover,
+/// if we do end up determining that we are parsing a destructor name,
+/// the last component of the nested-name-specifier is not parsed as
+/// part of the scope specifier.
+///
+/// \param IsTypename If \c true, this nested-name-specifier is known to be
+/// part of a type name. This is used to improve error recovery.
+///
+/// \param LastII When non-NULL, points to an IdentifierInfo* that will be
+/// filled in with the leading identifier in the last component of the
+/// nested-name-specifier, if any.
+///
+/// \param OnlyNamespace If true, only considers namespaces in lookup.
+///
+///
+/// \returns true if there was an error parsing a scope specifier
+bool Parser::ParseOptionalCXXScopeSpecifier(
+ CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+ bool EnteringContext, bool *MayBePseudoDestructor, bool IsTypename,
+ IdentifierInfo **LastII, bool OnlyNamespace, bool InUsingDeclaration) {
+ assert(getLangOpts().CPlusPlus &&
+ "Call sites of this function should be guarded by checking for C++");
+
+ if (Tok.is(tok::annot_cxxscope)) {
+ assert(!LastII && "want last identifier but have already annotated scope");
+ assert(!MayBePseudoDestructor && "unexpected annot_cxxscope");
+ Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
+ Tok.getAnnotationRange(),
+ SS);
+ ConsumeAnnotationToken();
+ return false;
+ }
+
+ // Has to happen before any "return false"s in this function.
+ bool CheckForDestructor = false;
+ if (MayBePseudoDestructor && *MayBePseudoDestructor) {
+ CheckForDestructor = true;
+ *MayBePseudoDestructor = false;
+ }
+
+ if (LastII)
+ *LastII = nullptr;
+
+ bool HasScopeSpecifier = false;
+
+ if (Tok.is(tok::coloncolon)) {
+ // ::new and ::delete aren't nested-name-specifiers.
+ tok::TokenKind NextKind = NextToken().getKind();
+ if (NextKind == tok::kw_new || NextKind == tok::kw_delete)
+ return false;
+
+ if (NextKind == tok::l_brace) {
+ // It is invalid to have :: {, consume the scope qualifier and pretend
+ // like we never saw it.
+ Diag(ConsumeToken(), diag::err_expected) << tok::identifier;
+ } else {
+ // '::' - Global scope qualifier.
+ if (Actions.ActOnCXXGlobalScopeSpecifier(ConsumeToken(), SS))
+ return true;
+
+ HasScopeSpecifier = true;
+ }
+ }
+
+ if (Tok.is(tok::kw___super)) {
+ SourceLocation SuperLoc = ConsumeToken();
+ if (!Tok.is(tok::coloncolon)) {
+ Diag(Tok.getLocation(), diag::err_expected_coloncolon_after_super);
+ return true;
+ }
+
+ return Actions.ActOnSuperScopeSpecifier(SuperLoc, ConsumeToken(), SS);
+ }
+
+ if (!HasScopeSpecifier &&
+ Tok.isOneOf(tok::kw_decltype, tok::annot_decltype)) {
+ DeclSpec DS(AttrFactory);
+ SourceLocation DeclLoc = Tok.getLocation();
+ SourceLocation EndLoc = ParseDecltypeSpecifier(DS);
+
+ SourceLocation CCLoc;
+ // Work around a standard defect: 'decltype(auto)::' is not a
+ // nested-name-specifier.
+ if (DS.getTypeSpecType() == DeclSpec::TST_decltype_auto ||
+ !TryConsumeToken(tok::coloncolon, CCLoc)) {
+ AnnotateExistingDecltypeSpecifier(DS, DeclLoc, EndLoc);
+ return false;
+ }
+
+ if (Actions.ActOnCXXNestedNameSpecifierDecltype(SS, DS, CCLoc))
+ SS.SetInvalid(SourceRange(DeclLoc, CCLoc));
+
+ HasScopeSpecifier = true;
+ }
+
+ // Preferred type might change when parsing qualifiers, we need the original.
+ auto SavedType = PreferredType;
+ while (true) {
+ if (HasScopeSpecifier) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ // Code completion for a nested-name-specifier, where the code
+ // completion token follows the '::'.
+ Actions.CodeCompleteQualifiedId(getCurScope(), SS, EnteringContext,
+ InUsingDeclaration, ObjectType.get(),
+ SavedType.get(SS.getBeginLoc()));
+ // Include code completion token into the range of the scope otherwise
+ // when we try to annotate the scope tokens the dangling code completion
+ // token will cause assertion in
+ // Preprocessor::AnnotatePreviousCachedTokens.
+ SS.setEndLoc(Tok.getLocation());
+ return true;
+ }
+
+ // C++ [basic.lookup.classref]p5:
+ // If the qualified-id has the form
+ //
+ // ::class-name-or-namespace-name::...
+ //
+ // the class-name-or-namespace-name is looked up in global scope as a
+ // class-name or namespace-name.
+ //
+ // To implement this, we clear out the object type as soon as we've
+ // seen a leading '::' or part of a nested-name-specifier.
+ ObjectType = nullptr;
+ }
+
+ // nested-name-specifier:
+ // nested-name-specifier 'template'[opt] simple-template-id '::'
+
+ // Parse the optional 'template' keyword, then make sure we have
+ // 'identifier <' after it.
+ if (Tok.is(tok::kw_template)) {
+ // If we don't have a scope specifier or an object type, this isn't a
+ // nested-name-specifier, since they aren't allowed to start with
+ // 'template'.
+ if (!HasScopeSpecifier && !ObjectType)
+ break;
+
+ TentativeParsingAction TPA(*this);
+ SourceLocation TemplateKWLoc = ConsumeToken();
+
+ UnqualifiedId TemplateName;
+ if (Tok.is(tok::identifier)) {
+ // Consume the identifier.
+ TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ ConsumeToken();
+ } else if (Tok.is(tok::kw_operator)) {
+ // We don't need to actually parse the unqualified-id in this case,
+ // because a simple-template-id cannot start with 'operator', but
+ // go ahead and parse it anyway for consistency with the case where
+ // we already annotated the template-id.
+ if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType,
+ TemplateName)) {
+ TPA.Commit();
+ break;
+ }
+
+ if (TemplateName.getKind() != UnqualifiedIdKind::IK_OperatorFunctionId &&
+ TemplateName.getKind() != UnqualifiedIdKind::IK_LiteralOperatorId) {
+ Diag(TemplateName.getSourceRange().getBegin(),
+ diag::err_id_after_template_in_nested_name_spec)
+ << TemplateName.getSourceRange();
+ TPA.Commit();
+ break;
+ }
+ } else {
+ TPA.Revert();
+ break;
+ }
+
+ // If the next token is not '<', we have a qualified-id that refers
+ // to a template name, such as T::template apply, but is not a
+ // template-id.
+ if (Tok.isNot(tok::less)) {
+ TPA.Revert();
+ break;
+ }
+
+ // Commit to parsing the template-id.
+ TPA.Commit();
+ TemplateTy Template;
+ TemplateNameKind TNK = Actions.ActOnTemplateName(
+ getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
+ EnteringContext, Template, /*AllowInjectedClassName*/ true);
+ if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc,
+ TemplateName, false))
+ return true;
+
+ continue;
+ }
+
+ if (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) {
+ // We have
+ //
+ // template-id '::'
+ //
+ // So we need to check whether the template-id is a simple-template-id of
+ // the right kind (it should name a type or be dependent), and then
+ // convert it into a type within the nested-name-specifier.
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) {
+ *MayBePseudoDestructor = true;
+ return false;
+ }
+
+ if (LastII)
+ *LastII = TemplateId->Name;
+
+ // Consume the template-id token.
+ ConsumeAnnotationToken();
+
+ assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
+ SourceLocation CCLoc = ConsumeToken();
+
+ HasScopeSpecifier = true;
+
+ ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
+ TemplateId->NumArgs);
+
+ if (TemplateId->isInvalid() ||
+ Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
+ SS,
+ TemplateId->TemplateKWLoc,
+ TemplateId->Template,
+ TemplateId->TemplateNameLoc,
+ TemplateId->LAngleLoc,
+ TemplateArgsPtr,
+ TemplateId->RAngleLoc,
+ CCLoc,
+ EnteringContext)) {
+ SourceLocation StartLoc
+ = SS.getBeginLoc().isValid()? SS.getBeginLoc()
+ : TemplateId->TemplateNameLoc;
+ SS.SetInvalid(SourceRange(StartLoc, CCLoc));
+ }
+
+ continue;
+ }
+
+ // The rest of the nested-name-specifier possibilities start with
+ // tok::identifier.
+ if (Tok.isNot(tok::identifier))
+ break;
+
+ IdentifierInfo &II = *Tok.getIdentifierInfo();
+
+ // nested-name-specifier:
+ // type-name '::'
+ // namespace-name '::'
+ // nested-name-specifier identifier '::'
+ Token Next = NextToken();
+ Sema::NestedNameSpecInfo IdInfo(&II, Tok.getLocation(), Next.getLocation(),
+ ObjectType);
+
+ // If we get foo:bar, this is almost certainly a typo for foo::bar. Recover
+ // and emit a fixit hint for it.
+ if (Next.is(tok::colon) && !ColonIsSacred) {
+ if (Actions.IsInvalidUnlessNestedName(getCurScope(), SS, IdInfo,
+ EnteringContext) &&
+ // If the token after the colon isn't an identifier, it's still an
+ // error, but they probably meant something else strange so don't
+ // recover like this.
+ PP.LookAhead(1).is(tok::identifier)) {
+ Diag(Next, diag::err_unexpected_colon_in_nested_name_spec)
+ << FixItHint::CreateReplacement(Next.getLocation(), "::");
+ // Recover as if the user wrote '::'.
+ Next.setKind(tok::coloncolon);
+ }
+ }
+
+ if (Next.is(tok::coloncolon) && GetLookAheadToken(2).is(tok::l_brace)) {
+ // It is invalid to have :: {, consume the scope qualifier and pretend
+ // like we never saw it.
+ Token Identifier = Tok; // Stash away the identifier.
+ ConsumeToken(); // Eat the identifier, current token is now '::'.
+ Diag(PP.getLocForEndOfToken(ConsumeToken()), diag::err_expected)
+ << tok::identifier;
+ UnconsumeToken(Identifier); // Stick the identifier back.
+ Next = NextToken(); // Point Next at the '{' token.
+ }
+
+ if (Next.is(tok::coloncolon)) {
+ if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) {
+ *MayBePseudoDestructor = true;
+ return false;
+ }
+
+ if (ColonIsSacred) {
+ const Token &Next2 = GetLookAheadToken(2);
+ if (Next2.is(tok::kw_private) || Next2.is(tok::kw_protected) ||
+ Next2.is(tok::kw_public) || Next2.is(tok::kw_virtual)) {
+ Diag(Next2, diag::err_unexpected_token_in_nested_name_spec)
+ << Next2.getName()
+ << FixItHint::CreateReplacement(Next.getLocation(), ":");
+ Token ColonColon;
+ PP.Lex(ColonColon);
+ ColonColon.setKind(tok::colon);
+ PP.EnterToken(ColonColon, /*IsReinject*/ true);
+ break;
+ }
+ }
+
+ if (LastII)
+ *LastII = &II;
+
+ // We have an identifier followed by a '::'. Lookup this name
+ // as the name in a nested-name-specifier.
+ Token Identifier = Tok;
+ SourceLocation IdLoc = ConsumeToken();
+ assert(Tok.isOneOf(tok::coloncolon, tok::colon) &&
+ "NextToken() not working properly!");
+ Token ColonColon = Tok;
+ SourceLocation CCLoc = ConsumeToken();
+
+ bool IsCorrectedToColon = false;
+ bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr;
+ if (Actions.ActOnCXXNestedNameSpecifier(
+ getCurScope(), IdInfo, EnteringContext, SS, CorrectionFlagPtr,
+ OnlyNamespace)) {
+ // Identifier is not recognized as a nested name, but we can have
+ // mistyped '::' instead of ':'.
+ if (CorrectionFlagPtr && IsCorrectedToColon) {
+ ColonColon.setKind(tok::colon);
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+ PP.EnterToken(ColonColon, /*IsReinject*/ true);
+ Tok = Identifier;
+ break;
+ }
+ SS.SetInvalid(SourceRange(IdLoc, CCLoc));
+ }
+ HasScopeSpecifier = true;
+ continue;
+ }
+
+ CheckForTemplateAndDigraph(Next, ObjectType, EnteringContext, II, SS);
+
+ // nested-name-specifier:
+ // type-name '<'
+ if (Next.is(tok::less)) {
+
+ TemplateTy Template;
+ UnqualifiedId TemplateName;
+ TemplateName.setIdentifier(&II, Tok.getLocation());
+ bool MemberOfUnknownSpecialization;
+ if (TemplateNameKind TNK = Actions.isTemplateName(getCurScope(), SS,
+ /*hasTemplateKeyword=*/false,
+ TemplateName,
+ ObjectType,
+ EnteringContext,
+ Template,
+ MemberOfUnknownSpecialization)) {
+ // If lookup didn't find anything, we treat the name as a template-name
+ // anyway. C++20 requires this, and in prior language modes it improves
+ // error recovery. But before we commit to this, check that we actually
+ // have something that looks like a template-argument-list next.
+ if (!IsTypename && TNK == TNK_Undeclared_template &&
+ isTemplateArgumentList(1) == TPResult::False)
+ break;
+
+ // We have found a template name, so annotate this token
+ // with a template-id annotation. We do not permit the
+ // template-id to be translated into a type annotation,
+ // because some clients (e.g., the parsing of class template
+ // specializations) still want to see the original template-id
+ // token, and it might not be a type at all (e.g. a concept name in a
+ // type-constraint).
+ ConsumeToken();
+ if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
+ TemplateName, false))
+ return true;
+ continue;
+ }
+
+ if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) &&
+ (IsTypename || isTemplateArgumentList(1) == TPResult::True)) {
+ // If we had errors before, ObjectType can be dependent even without any
+ // templates. Do not report missing template keyword in that case.
+ if (!ObjectHadErrors) {
+ // We have something like t::getAs<T>, where getAs is a
+ // member of an unknown specialization. However, this will only
+ // parse correctly as a template, so suggest the keyword 'template'
+ // before 'getAs' and treat this as a dependent template name.
+ unsigned DiagID = diag::err_missing_dependent_template_keyword;
+ if (getLangOpts().MicrosoftExt)
+ DiagID = diag::warn_missing_dependent_template_keyword;
+
+ Diag(Tok.getLocation(), DiagID)
+ << II.getName()
+ << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
+ }
+
+ SourceLocation TemplateNameLoc = ConsumeToken();
+
+ TemplateNameKind TNK = Actions.ActOnTemplateName(
+ getCurScope(), SS, TemplateNameLoc, TemplateName, ObjectType,
+ EnteringContext, Template, /*AllowInjectedClassName*/ true);
+ if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
+ TemplateName, false))
+ return true;
+
+ continue;
+ }
+ }
+
+ // We don't have any tokens that form the beginning of a
+ // nested-name-specifier, so we're done.
+ break;
+ }
+
+ // Even if we didn't see any pieces of a nested-name-specifier, we
+ // still check whether there is a tilde in this position, which
+ // indicates a potential pseudo-destructor.
+ if (CheckForDestructor && !HasScopeSpecifier && Tok.is(tok::tilde))
+ *MayBePseudoDestructor = true;
+
+ return false;
+}
+
+ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
+ bool isAddressOfOperand,
+ Token &Replacement) {
+ ExprResult E;
+
+ // We may have already annotated this id-expression.
+ switch (Tok.getKind()) {
+ case tok::annot_non_type: {
+ NamedDecl *ND = getNonTypeAnnotation(Tok);
+ SourceLocation Loc = ConsumeAnnotationToken();
+ E = Actions.ActOnNameClassifiedAsNonType(getCurScope(), SS, ND, Loc, Tok);
+ break;
+ }
+
+ case tok::annot_non_type_dependent: {
+ IdentifierInfo *II = getIdentifierAnnotation(Tok);
+ SourceLocation Loc = ConsumeAnnotationToken();
+
+ // This is only the direct operand of an & operator if it is not
+ // followed by a postfix-expression suffix.
+ if (isAddressOfOperand && isPostfixExpressionSuffixStart())
+ isAddressOfOperand = false;
+
+ E = Actions.ActOnNameClassifiedAsDependentNonType(SS, II, Loc,
+ isAddressOfOperand);
+ break;
+ }
+
+ case tok::annot_non_type_undeclared: {
+ assert(SS.isEmpty() &&
+ "undeclared non-type annotation should be unqualified");
+ IdentifierInfo *II = getIdentifierAnnotation(Tok);
+ SourceLocation Loc = ConsumeAnnotationToken();
+ E = Actions.ActOnNameClassifiedAsUndeclaredNonType(II, Loc);
+ break;
+ }
+
+ default:
+ SourceLocation TemplateKWLoc;
+ UnqualifiedId Name;
+ if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false,
+ /*AllowDestructorName=*/false,
+ /*AllowConstructorName=*/false,
+ /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name))
+ return ExprError();
+
+ // This is only the direct operand of an & operator if it is not
+ // followed by a postfix-expression suffix.
+ if (isAddressOfOperand && isPostfixExpressionSuffixStart())
+ isAddressOfOperand = false;
+
+ E = Actions.ActOnIdExpression(
+ getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
+ isAddressOfOperand, /*CCC=*/nullptr, /*IsInlineAsmIdentifier=*/false,
+ &Replacement);
+ break;
+ }
+
+ if (!E.isInvalid() && !E.isUnset() && Tok.is(tok::less))
+ checkPotentialAngleBracket(E);
+ return E;
+}
+
+/// ParseCXXIdExpression - Handle id-expression.
+///
+/// id-expression:
+/// unqualified-id
+/// qualified-id
+///
+/// qualified-id:
+/// '::'[opt] nested-name-specifier 'template'[opt] unqualified-id
+/// '::' identifier
+/// '::' operator-function-id
+/// '::' template-id
+///
+/// NOTE: The standard specifies that, for qualified-id, the parser does not
+/// expect:
+///
+/// '::' conversion-function-id
+/// '::' '~' class-name
+///
+/// This may cause a slight inconsistency on diagnostics:
+///
+/// class C {};
+/// namespace A {}
+/// void f() {
+/// :: A :: ~ C(); // Some Sema error about using destructor with a
+/// // namespace.
+/// :: ~ C(); // Some Parser error like 'unexpected ~'.
+/// }
+///
+/// We simplify the parser a bit and make it work like:
+///
+/// qualified-id:
+/// '::'[opt] nested-name-specifier 'template'[opt] unqualified-id
+/// '::' unqualified-id
+///
+/// That way Sema can handle and report similar errors for namespaces and the
+/// global scope.
+///
+/// The isAddressOfOperand parameter indicates that this id-expression is a
+/// direct operand of the address-of operator. This is, besides member contexts,
+/// the only place where a qualified-id naming a non-static class member may
+/// appear.
+///
+ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
+ // qualified-id:
+ // '::'[opt] nested-name-specifier 'template'[opt] unqualified-id
+ // '::' unqualified-id
+ //
+ CXXScopeSpec SS;
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false);
+
+ Token Replacement;
+ ExprResult Result =
+ tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement);
+ if (Result.isUnset()) {
+ // If the ExprResult is valid but null, then typo correction suggested a
+ // keyword replacement that needs to be reparsed.
+ UnconsumeToken(Replacement);
+ Result = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement);
+ }
+ assert(!Result.isUnset() && "Typo correction suggested a keyword replacement "
+ "for a previous keyword suggestion");
+ return Result;
+}
+
+/// ParseLambdaExpression - Parse a C++11 lambda expression.
+///
+/// lambda-expression:
+/// lambda-introducer lambda-declarator compound-statement
+/// lambda-introducer '<' template-parameter-list '>'
+/// requires-clause[opt] lambda-declarator compound-statement
+///
+/// lambda-introducer:
+/// '[' lambda-capture[opt] ']'
+///
+/// lambda-capture:
+/// capture-default
+/// capture-list
+/// capture-default ',' capture-list
+///
+/// capture-default:
+/// '&'
+/// '='
+///
+/// capture-list:
+/// capture
+/// capture-list ',' capture
+///
+/// capture:
+/// simple-capture
+/// init-capture [C++1y]
+///
+/// simple-capture:
+/// identifier
+/// '&' identifier
+/// 'this'
+///
+/// init-capture: [C++1y]
+/// identifier initializer
+/// '&' identifier initializer
+///
+/// lambda-declarator:
+/// lambda-specifiers [C++2b]
+/// '(' parameter-declaration-clause ')' lambda-specifiers
+/// requires-clause[opt]
+///
+/// lambda-specifiers:
+/// decl-specifier-seq[opt] noexcept-specifier[opt]
+/// attribute-specifier-seq[opt] trailing-return-type[opt]
+///
+ExprResult Parser::ParseLambdaExpression() {
+ // Parse lambda-introducer.
+ LambdaIntroducer Intro;
+ if (ParseLambdaIntroducer(Intro)) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ SkipUntil(tok::l_brace, StopAtSemi);
+ SkipUntil(tok::r_brace, StopAtSemi);
+ return ExprError();
+ }
+
+ return ParseLambdaExpressionAfterIntroducer(Intro);
+}
+
+/// Use lookahead and potentially tentative parsing to determine if we are
+/// looking at a C++11 lambda expression, and parse it if we are.
+///
+/// If we are not looking at a lambda expression, returns ExprError().
+ExprResult Parser::TryParseLambdaExpression() {
+ assert(getLangOpts().CPlusPlus11
+ && Tok.is(tok::l_square)
+ && "Not at the start of a possible lambda expression.");
+
+ const Token Next = NextToken();
+ if (Next.is(tok::eof)) // Nothing else to lookup here...
+ return ExprEmpty();
+
+ const Token After = GetLookAheadToken(2);
+ // If lookahead indicates this is a lambda...
+ if (Next.is(tok::r_square) || // []
+ Next.is(tok::equal) || // [=
+ (Next.is(tok::amp) && // [&] or [&,
+ After.isOneOf(tok::r_square, tok::comma)) ||
+ (Next.is(tok::identifier) && // [identifier]
+ After.is(tok::r_square)) ||
+ Next.is(tok::ellipsis)) { // [...
+ return ParseLambdaExpression();
+ }
+
+ // If lookahead indicates an ObjC message send...
+ // [identifier identifier
+ if (Next.is(tok::identifier) && After.is(tok::identifier))
+ return ExprEmpty();
+
+ // Here, we're stuck: lambda introducers and Objective-C message sends are
+ // unambiguous, but it requires arbitrary lookhead. [a,b,c,d,e,f,g] is a
+ // lambda, and [a,b,c,d,e,f,g h] is a Objective-C message send. Instead of
+ // writing two routines to parse a lambda introducer, just try to parse
+ // a lambda introducer first, and fall back if that fails.
+ LambdaIntroducer Intro;
+ {
+ TentativeParsingAction TPA(*this);
+ LambdaIntroducerTentativeParse Tentative;
+ if (ParseLambdaIntroducer(Intro, &Tentative)) {
+ TPA.Commit();
+ return ExprError();
+ }
+
+ switch (Tentative) {
+ case LambdaIntroducerTentativeParse::Success:
+ TPA.Commit();
+ break;
+
+ case LambdaIntroducerTentativeParse::Incomplete:
+ // Didn't fully parse the lambda-introducer, try again with a
+ // non-tentative parse.
+ TPA.Revert();
+ Intro = LambdaIntroducer();
+ if (ParseLambdaIntroducer(Intro))
+ return ExprError();
+ break;
+
+ case LambdaIntroducerTentativeParse::MessageSend:
+ case LambdaIntroducerTentativeParse::Invalid:
+ // Not a lambda-introducer, might be a message send.
+ TPA.Revert();
+ return ExprEmpty();
+ }
+ }
+
+ return ParseLambdaExpressionAfterIntroducer(Intro);
+}
+
+/// Parse a lambda introducer.
+/// \param Intro A LambdaIntroducer filled in with information about the
+/// contents of the lambda-introducer.
+/// \param Tentative If non-null, we are disambiguating between a
+/// lambda-introducer and some other construct. In this mode, we do not
+/// produce any diagnostics or take any other irreversible action unless
+/// we're sure that this is a lambda-expression.
+/// \return \c true if parsing (or disambiguation) failed with a diagnostic and
+/// the caller should bail out / recover.
+bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
+ LambdaIntroducerTentativeParse *Tentative) {
+ if (Tentative)
+ *Tentative = LambdaIntroducerTentativeParse::Success;
+
+ assert(Tok.is(tok::l_square) && "Lambda expressions begin with '['.");
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+
+ Intro.Range.setBegin(T.getOpenLocation());
+
+ bool First = true;
+
+ // Produce a diagnostic if we're not tentatively parsing; otherwise track
+ // that our parse has failed.
+ auto Invalid = [&](llvm::function_ref<void()> Action) {
+ if (Tentative) {
+ *Tentative = LambdaIntroducerTentativeParse::Invalid;
+ return false;
+ }
+ Action();
+ return true;
+ };
+
+ // Perform some irreversible action if this is a non-tentative parse;
+ // otherwise note that our actions were incomplete.
+ auto NonTentativeAction = [&](llvm::function_ref<void()> Action) {
+ if (Tentative)
+ *Tentative = LambdaIntroducerTentativeParse::Incomplete;
+ else
+ Action();
+ };
+
+ // Parse capture-default.
+ if (Tok.is(tok::amp) &&
+ (NextToken().is(tok::comma) || NextToken().is(tok::r_square))) {
+ Intro.Default = LCD_ByRef;
+ Intro.DefaultLoc = ConsumeToken();
+ First = false;
+ if (!Tok.getIdentifierInfo()) {
+ // This can only be a lambda; no need for tentative parsing any more.
+ // '[[and]]' can still be an attribute, though.
+ Tentative = nullptr;
+ }
+ } else if (Tok.is(tok::equal)) {
+ Intro.Default = LCD_ByCopy;
+ Intro.DefaultLoc = ConsumeToken();
+ First = false;
+ Tentative = nullptr;
+ }
+
+ while (Tok.isNot(tok::r_square)) {
+ if (!First) {
+ if (Tok.isNot(tok::comma)) {
+ // Provide a completion for a lambda introducer here. Except
+ // in Objective-C, where this is Almost Surely meant to be a message
+ // send. In that case, fail here and let the ObjC message
+ // expression parser perform the completion.
+ if (Tok.is(tok::code_completion) &&
+ !(getLangOpts().ObjC && Tentative)) {
+ cutOffParsing();
+ Actions.CodeCompleteLambdaIntroducer(getCurScope(), Intro,
+ /*AfterAmpersand=*/false);
+ break;
+ }
+
+ return Invalid([&] {
+ Diag(Tok.getLocation(), diag::err_expected_comma_or_rsquare);
+ });
+ }
+ ConsumeToken();
+ }
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ // If we're in Objective-C++ and we have a bare '[', then this is more
+ // likely to be a message receiver.
+ if (getLangOpts().ObjC && Tentative && First)
+ Actions.CodeCompleteObjCMessageReceiver(getCurScope());
+ else
+ Actions.CodeCompleteLambdaIntroducer(getCurScope(), Intro,
+ /*AfterAmpersand=*/false);
+ break;
+ }
+
+ First = false;
+
+ // Parse capture.
+ LambdaCaptureKind Kind = LCK_ByCopy;
+ LambdaCaptureInitKind InitKind = LambdaCaptureInitKind::NoInit;
+ SourceLocation Loc;
+ IdentifierInfo *Id = nullptr;
+ SourceLocation EllipsisLocs[4];
+ ExprResult Init;
+ SourceLocation LocStart = Tok.getLocation();
+
+ if (Tok.is(tok::star)) {
+ Loc = ConsumeToken();
+ if (Tok.is(tok::kw_this)) {
+ ConsumeToken();
+ Kind = LCK_StarThis;
+ } else {
+ return Invalid([&] {
+ Diag(Tok.getLocation(), diag::err_expected_star_this_capture);
+ });
+ }
+ } else if (Tok.is(tok::kw_this)) {
+ Kind = LCK_This;
+ Loc = ConsumeToken();
+ } else if (Tok.isOneOf(tok::amp, tok::equal) &&
+ NextToken().isOneOf(tok::comma, tok::r_square) &&
+ Intro.Default == LCD_None) {
+ // We have a lone "&" or "=" which is either a misplaced capture-default
+ // or the start of a capture (in the "&" case) with the rest of the
+ // capture missing. Both are an error but a misplaced capture-default
+ // is more likely if we don't already have a capture default.
+ return Invalid(
+ [&] { Diag(Tok.getLocation(), diag::err_capture_default_first); });
+ } else {
+ TryConsumeToken(tok::ellipsis, EllipsisLocs[0]);
+
+ if (Tok.is(tok::amp)) {
+ Kind = LCK_ByRef;
+ ConsumeToken();
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteLambdaIntroducer(getCurScope(), Intro,
+ /*AfterAmpersand=*/true);
+ break;
+ }
+ }
+
+ TryConsumeToken(tok::ellipsis, EllipsisLocs[1]);
+
+ if (Tok.is(tok::identifier)) {
+ Id = Tok.getIdentifierInfo();
+ Loc = ConsumeToken();
+ } else if (Tok.is(tok::kw_this)) {
+ return Invalid([&] {
+ // FIXME: Suggest a fixit here.
+ Diag(Tok.getLocation(), diag::err_this_captured_by_reference);
+ });
+ } else {
+ return Invalid([&] {
+ Diag(Tok.getLocation(), diag::err_expected_capture);
+ });
+ }
+
+ TryConsumeToken(tok::ellipsis, EllipsisLocs[2]);
+
+ if (Tok.is(tok::l_paren)) {
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ Parens.consumeOpen();
+
+ InitKind = LambdaCaptureInitKind::DirectInit;
+
+ ExprVector Exprs;
+ if (Tentative) {
+ Parens.skipToEnd();
+ *Tentative = LambdaIntroducerTentativeParse::Incomplete;
+ } else if (ParseExpressionList(Exprs)) {
+ Parens.skipToEnd();
+ Init = ExprError();
+ } else {
+ Parens.consumeClose();
+ Init = Actions.ActOnParenListExpr(Parens.getOpenLocation(),
+ Parens.getCloseLocation(),
+ Exprs);
+ }
+ } else if (Tok.isOneOf(tok::l_brace, tok::equal)) {
+ // Each lambda init-capture forms its own full expression, which clears
+ // Actions.MaybeODRUseExprs. So create an expression evaluation context
+ // to save the necessary state, and restore it later.
+ EnterExpressionEvaluationContext EC(
+ Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
+
+ if (TryConsumeToken(tok::equal))
+ InitKind = LambdaCaptureInitKind::CopyInit;
+ else
+ InitKind = LambdaCaptureInitKind::ListInit;
+
+ if (!Tentative) {
+ Init = ParseInitializer();
+ } else if (Tok.is(tok::l_brace)) {
+ BalancedDelimiterTracker Braces(*this, tok::l_brace);
+ Braces.consumeOpen();
+ Braces.skipToEnd();
+ *Tentative = LambdaIntroducerTentativeParse::Incomplete;
+ } else {
+ // We're disambiguating this:
+ //
+ // [..., x = expr
+ //
+ // We need to find the end of the following expression in order to
+ // determine whether this is an Obj-C message send's receiver, a
+ // C99 designator, or a lambda init-capture.
+ //
+ // Parse the expression to find where it ends, and annotate it back
+ // onto the tokens. We would have parsed this expression the same way
+ // in either case: both the RHS of an init-capture and the RHS of an
+ // assignment expression are parsed as an initializer-clause, and in
+ // neither case can anything be added to the scope between the '[' and
+ // here.
+ //
+ // FIXME: This is horrible. Adding a mechanism to skip an expression
+ // would be much cleaner.
+ // FIXME: If there is a ',' before the next ']' or ':', we can skip to
+ // that instead. (And if we see a ':' with no matching '?', we can
+ // classify this as an Obj-C message send.)
+ SourceLocation StartLoc = Tok.getLocation();
+ InMessageExpressionRAIIObject MaybeInMessageExpression(*this, true);
+ Init = ParseInitializer();
+ if (!Init.isInvalid())
+ Init = Actions.CorrectDelayedTyposInExpr(Init.get());
+
+ if (Tok.getLocation() != StartLoc) {
+ // Back out the lexing of the token after the initializer.
+ PP.RevertCachedTokens(1);
+
+ // Replace the consumed tokens with an appropriate annotation.
+ Tok.setLocation(StartLoc);
+ Tok.setKind(tok::annot_primary_expr);
+ setExprAnnotation(Tok, Init);
+ Tok.setAnnotationEndLoc(PP.getLastCachedTokenLocation());
+ PP.AnnotateCachedTokens(Tok);
+
+ // Consume the annotated initializer.
+ ConsumeAnnotationToken();
+ }
+ }
+ }
+
+ TryConsumeToken(tok::ellipsis, EllipsisLocs[3]);
+ }
+
+ // Check if this is a message send before we act on a possible init-capture.
+ if (Tentative && Tok.is(tok::identifier) &&
+ NextToken().isOneOf(tok::colon, tok::r_square)) {
+ // This can only be a message send. We're done with disambiguation.
+ *Tentative = LambdaIntroducerTentativeParse::MessageSend;
+ return false;
+ }
+
+ // Ensure that any ellipsis was in the right place.
+ SourceLocation EllipsisLoc;
+ if (llvm::any_of(EllipsisLocs,
+ [](SourceLocation Loc) { return Loc.isValid(); })) {
+ // The '...' should appear before the identifier in an init-capture, and
+ // after the identifier otherwise.
+ bool InitCapture = InitKind != LambdaCaptureInitKind::NoInit;
+ SourceLocation *ExpectedEllipsisLoc =
+ !InitCapture ? &EllipsisLocs[2] :
+ Kind == LCK_ByRef ? &EllipsisLocs[1] :
+ &EllipsisLocs[0];
+ EllipsisLoc = *ExpectedEllipsisLoc;
+
+ unsigned DiagID = 0;
+ if (EllipsisLoc.isInvalid()) {
+ DiagID = diag::err_lambda_capture_misplaced_ellipsis;
+ for (SourceLocation Loc : EllipsisLocs) {
+ if (Loc.isValid())
+ EllipsisLoc = Loc;
+ }
+ } else {
+ unsigned NumEllipses = std::accumulate(
+ std::begin(EllipsisLocs), std::end(EllipsisLocs), 0,
+ [](int N, SourceLocation Loc) { return N + Loc.isValid(); });
+ if (NumEllipses > 1)
+ DiagID = diag::err_lambda_capture_multiple_ellipses;
+ }
+ if (DiagID) {
+ NonTentativeAction([&] {
+ // Point the diagnostic at the first misplaced ellipsis.
+ SourceLocation DiagLoc;
+ for (SourceLocation &Loc : EllipsisLocs) {
+ if (&Loc != ExpectedEllipsisLoc && Loc.isValid()) {
+ DiagLoc = Loc;
+ break;
+ }
+ }
+ assert(DiagLoc.isValid() && "no location for diagnostic");
+
+ // Issue the diagnostic and produce fixits showing where the ellipsis
+ // should have been written.
+ auto &&D = Diag(DiagLoc, DiagID);
+ if (DiagID == diag::err_lambda_capture_misplaced_ellipsis) {
+ SourceLocation ExpectedLoc =
+ InitCapture ? Loc
+ : Lexer::getLocForEndOfToken(
+ Loc, 0, PP.getSourceManager(), getLangOpts());
+ D << InitCapture << FixItHint::CreateInsertion(ExpectedLoc, "...");
+ }
+ for (SourceLocation &Loc : EllipsisLocs) {
+ if (&Loc != ExpectedEllipsisLoc && Loc.isValid())
+ D << FixItHint::CreateRemoval(Loc);
+ }
+ });
+ }
+ }
+
+ // Process the init-capture initializers now rather than delaying until we
+ // form the lambda-expression so that they can be handled in the context
+ // enclosing the lambda-expression, rather than in the context of the
+ // lambda-expression itself.
+ ParsedType InitCaptureType;
+ if (Init.isUsable())
+ Init = Actions.CorrectDelayedTyposInExpr(Init.get());
+ if (Init.isUsable()) {
+ NonTentativeAction([&] {
+ // Get the pointer and store it in an lvalue, so we can use it as an
+ // out argument.
+ Expr *InitExpr = Init.get();
+ // This performs any lvalue-to-rvalue conversions if necessary, which
+ // can affect what gets captured in the containing decl-context.
+ InitCaptureType = Actions.actOnLambdaInitCaptureInitialization(
+ Loc, Kind == LCK_ByRef, EllipsisLoc, Id, InitKind, InitExpr);
+ Init = InitExpr;
+ });
+ }
+
+ SourceLocation LocEnd = PrevTokLocation;
+
+ Intro.addCapture(Kind, Loc, Id, EllipsisLoc, InitKind, Init,
+ InitCaptureType, SourceRange(LocStart, LocEnd));
+ }
+
+ T.consumeClose();
+ Intro.Range.setEnd(T.getCloseLocation());
+ return false;
+}
+
+static void tryConsumeLambdaSpecifierToken(Parser &P,
+ SourceLocation &MutableLoc,
+ SourceLocation &StaticLoc,
+ SourceLocation &ConstexprLoc,
+ SourceLocation &ConstevalLoc,
+ SourceLocation &DeclEndLoc) {
+ assert(MutableLoc.isInvalid());
+ assert(StaticLoc.isInvalid());
+ assert(ConstexprLoc.isInvalid());
+ assert(ConstevalLoc.isInvalid());
+ // Consume constexpr-opt mutable-opt in any sequence, and set the DeclEndLoc
+ // to the final of those locations. Emit an error if we have multiple
+ // copies of those keywords and recover.
+
+ auto ConsumeLocation = [&P, &DeclEndLoc](SourceLocation &SpecifierLoc,
+ int DiagIndex) {
+ if (SpecifierLoc.isValid()) {
+ P.Diag(P.getCurToken().getLocation(),
+ diag::err_lambda_decl_specifier_repeated)
+ << DiagIndex
+ << FixItHint::CreateRemoval(P.getCurToken().getLocation());
+ }
+ SpecifierLoc = P.ConsumeToken();
+ DeclEndLoc = SpecifierLoc;
+ };
+
+ while (true) {
+ switch (P.getCurToken().getKind()) {
+ case tok::kw_mutable:
+ ConsumeLocation(MutableLoc, 0);
+ break;
+ case tok::kw_static:
+ ConsumeLocation(StaticLoc, 1);
+ break;
+ case tok::kw_constexpr:
+ ConsumeLocation(ConstexprLoc, 2);
+ break;
+ case tok::kw_consteval:
+ ConsumeLocation(ConstevalLoc, 3);
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+static void addStaticToLambdaDeclSpecifier(Parser &P, SourceLocation StaticLoc,
+ DeclSpec &DS) {
+ if (StaticLoc.isValid()) {
+ P.Diag(StaticLoc, !P.getLangOpts().CPlusPlus2b
+ ? diag::err_static_lambda
+ : diag::warn_cxx20_compat_static_lambda);
+ const char *PrevSpec = nullptr;
+ unsigned DiagID = 0;
+ DS.SetStorageClassSpec(P.getActions(), DeclSpec::SCS_static, StaticLoc,
+ PrevSpec, DiagID,
+ P.getActions().getASTContext().getPrintingPolicy());
+ assert(PrevSpec == nullptr && DiagID == 0 &&
+ "Static cannot have been set previously!");
+ }
+}
+
+static void
+addConstexprToLambdaDeclSpecifier(Parser &P, SourceLocation ConstexprLoc,
+ DeclSpec &DS) {
+ if (ConstexprLoc.isValid()) {
+ P.Diag(ConstexprLoc, !P.getLangOpts().CPlusPlus17
+ ? diag::ext_constexpr_on_lambda_cxx17
+ : diag::warn_cxx14_compat_constexpr_on_lambda);
+ const char *PrevSpec = nullptr;
+ unsigned DiagID = 0;
+ DS.SetConstexprSpec(ConstexprSpecKind::Constexpr, ConstexprLoc, PrevSpec,
+ DiagID);
+ assert(PrevSpec == nullptr && DiagID == 0 &&
+ "Constexpr cannot have been set previously!");
+ }
+}
+
+static void addConstevalToLambdaDeclSpecifier(Parser &P,
+ SourceLocation ConstevalLoc,
+ DeclSpec &DS) {
+ if (ConstevalLoc.isValid()) {
+ P.Diag(ConstevalLoc, diag::warn_cxx20_compat_consteval);
+ const char *PrevSpec = nullptr;
+ unsigned DiagID = 0;
+ DS.SetConstexprSpec(ConstexprSpecKind::Consteval, ConstevalLoc, PrevSpec,
+ DiagID);
+ if (DiagID != 0)
+ P.Diag(ConstevalLoc, DiagID) << PrevSpec;
+ }
+}
+
+static void DiagnoseStaticSpecifierRestrictions(Parser &P,
+ SourceLocation StaticLoc,
+ SourceLocation MutableLoc,
+ const LambdaIntroducer &Intro) {
+ if (StaticLoc.isInvalid())
+ return;
+
+ // [expr.prim.lambda.general] p4
+ // The lambda-specifier-seq shall not contain both mutable and static.
+ // If the lambda-specifier-seq contains static, there shall be no
+ // lambda-capture.
+ if (MutableLoc.isValid())
+ P.Diag(StaticLoc, diag::err_static_mutable_lambda);
+ if (Intro.hasLambdaCapture()) {
+ P.Diag(StaticLoc, diag::err_static_lambda_captures);
+ }
+}
+
+/// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda
+/// expression.
+ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
+ LambdaIntroducer &Intro) {
+ SourceLocation LambdaBeginLoc = Intro.Range.getBegin();
+ Diag(LambdaBeginLoc, diag::warn_cxx98_compat_lambda);
+
+ PrettyStackTraceLoc CrashInfo(PP.getSourceManager(), LambdaBeginLoc,
+ "lambda expression parsing");
+
+
+
+ // FIXME: Call into Actions to add any init-capture declarations to the
+ // scope while parsing the lambda-declarator and compound-statement.
+
+ // Parse lambda-declarator[opt].
+ DeclSpec DS(AttrFactory);
+ Declarator D(DS, ParsedAttributesView::none(), DeclaratorContext::LambdaExpr);
+ TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
+ Actions.PushLambdaScope();
+
+ ParsedAttributes Attr(AttrFactory);
+ if (getLangOpts().CUDA) {
+ // In CUDA code, GNU attributes are allowed to appear immediately after the
+ // "[...]", even if there is no "(...)" before the lambda body.
+ //
+ // Note that we support __noinline__ as a keyword in this mode and thus
+ // it has to be separately handled.
+ while (true) {
+ if (Tok.is(tok::kw___noinline__)) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ Attr.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
+ } else if (Tok.is(tok::kw___attribute))
+ ParseGNUAttributes(Attr, nullptr, &D);
+ else
+ break;
+ }
+
+ D.takeAttributes(Attr);
+ }
+
+ // Helper to emit a warning if we see a CUDA host/device/global attribute
+ // after '(...)'. nvcc doesn't accept this.
+ auto WarnIfHasCUDATargetAttr = [&] {
+ if (getLangOpts().CUDA)
+ for (const ParsedAttr &A : Attr)
+ if (A.getKind() == ParsedAttr::AT_CUDADevice ||
+ A.getKind() == ParsedAttr::AT_CUDAHost ||
+ A.getKind() == ParsedAttr::AT_CUDAGlobal)
+ Diag(A.getLoc(), diag::warn_cuda_attr_lambda_position)
+ << A.getAttrName()->getName();
+ };
+
+ MultiParseScope TemplateParamScope(*this);
+ if (Tok.is(tok::less)) {
+ Diag(Tok, getLangOpts().CPlusPlus20
+ ? diag::warn_cxx17_compat_lambda_template_parameter_list
+ : diag::ext_lambda_template_parameter_list);
+
+ SmallVector<NamedDecl*, 4> TemplateParams;
+ SourceLocation LAngleLoc, RAngleLoc;
+ if (ParseTemplateParameters(TemplateParamScope,
+ CurTemplateDepthTracker.getDepth(),
+ TemplateParams, LAngleLoc, RAngleLoc)) {
+ Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
+ return ExprError();
+ }
+
+ if (TemplateParams.empty()) {
+ Diag(RAngleLoc,
+ diag::err_lambda_template_parameter_list_empty);
+ } else {
+ ExprResult RequiresClause;
+ if (TryConsumeToken(tok::kw_requires)) {
+ RequiresClause =
+ Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression(
+ /*IsTrailingRequiresClause=*/false));
+ if (RequiresClause.isInvalid())
+ SkipUntil({tok::l_brace, tok::l_paren}, StopAtSemi | StopBeforeMatch);
+ }
+
+ Actions.ActOnLambdaExplicitTemplateParameterList(
+ LAngleLoc, TemplateParams, RAngleLoc, RequiresClause);
+ ++CurTemplateDepthTracker;
+ }
+ }
+
+ // Implement WG21 P2173, which allows attributes immediately before the
+ // lambda declarator and applies them to the corresponding function operator
+ // or operator template declaration. We accept this as a conforming extension
+ // in all language modes that support lambdas.
+ if (isCXX11AttributeSpecifier()) {
+ Diag(Tok, getLangOpts().CPlusPlus2b
+ ? diag::warn_cxx20_compat_decl_attrs_on_lambda
+ : diag::ext_decl_attrs_on_lambda);
+ MaybeParseCXX11Attributes(D);
+ }
+
+ TypeResult TrailingReturnType;
+ SourceLocation TrailingReturnTypeLoc;
+
+ auto ParseLambdaSpecifiers =
+ [&](SourceLocation LParenLoc, SourceLocation RParenLoc,
+ MutableArrayRef<DeclaratorChunk::ParamInfo> ParamInfo,
+ SourceLocation EllipsisLoc) {
+ SourceLocation DeclEndLoc = RParenLoc;
+
+ // GNU-style attributes must be parsed before the mutable specifier to
+ // be compatible with GCC. MSVC-style attributes must be parsed before
+ // the mutable specifier to be compatible with MSVC.
+ MaybeParseAttributes(PAKM_GNU | PAKM_Declspec, Attr);
+
+ // Parse lambda specifiers and update the DeclEndLoc.
+ SourceLocation MutableLoc;
+ SourceLocation StaticLoc;
+ SourceLocation ConstexprLoc;
+ SourceLocation ConstevalLoc;
+ tryConsumeLambdaSpecifierToken(*this, MutableLoc, StaticLoc,
+ ConstexprLoc, ConstevalLoc, DeclEndLoc);
+
+ DiagnoseStaticSpecifierRestrictions(*this, StaticLoc, MutableLoc,
+ Intro);
+
+ addStaticToLambdaDeclSpecifier(*this, StaticLoc, DS);
+ addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS);
+ addConstevalToLambdaDeclSpecifier(*this, ConstevalLoc, DS);
+ // Parse exception-specification[opt].
+ ExceptionSpecificationType ESpecType = EST_None;
+ SourceRange ESpecRange;
+ SmallVector<ParsedType, 2> DynamicExceptions;
+ SmallVector<SourceRange, 2> DynamicExceptionRanges;
+ ExprResult NoexceptExpr;
+ CachedTokens *ExceptionSpecTokens;
+ ESpecType = tryParseExceptionSpecification(
+ /*Delayed=*/false, ESpecRange, DynamicExceptions,
+ DynamicExceptionRanges, NoexceptExpr, ExceptionSpecTokens);
+
+ if (ESpecType != EST_None)
+ DeclEndLoc = ESpecRange.getEnd();
+
+ // Parse attribute-specifier[opt].
+ if (MaybeParseCXX11Attributes(Attr))
+ DeclEndLoc = Attr.Range.getEnd();
+
+ // Parse OpenCL addr space attribute.
+ if (Tok.isOneOf(tok::kw___private, tok::kw___global, tok::kw___local,
+ tok::kw___constant, tok::kw___generic)) {
+ ParseOpenCLQualifiers(DS.getAttributes());
+ ConsumeToken();
+ }
+
+ SourceLocation FunLocalRangeEnd = DeclEndLoc;
+
+ // Parse trailing-return-type[opt].
+ if (Tok.is(tok::arrow)) {
+ FunLocalRangeEnd = Tok.getLocation();
+ SourceRange Range;
+ TrailingReturnType = ParseTrailingReturnType(
+ Range, /*MayBeFollowedByDirectInit*/ false);
+ TrailingReturnTypeLoc = Range.getBegin();
+ if (Range.getEnd().isValid())
+ DeclEndLoc = Range.getEnd();
+ }
+
+ SourceLocation NoLoc;
+ D.AddTypeInfo(
+ DeclaratorChunk::getFunction(
+ /*HasProto=*/true,
+ /*IsAmbiguous=*/false, LParenLoc, ParamInfo.data(),
+ ParamInfo.size(), EllipsisLoc, RParenLoc,
+ /*RefQualifierIsLvalueRef=*/true,
+ /*RefQualifierLoc=*/NoLoc, MutableLoc, ESpecType, ESpecRange,
+ DynamicExceptions.data(), DynamicExceptionRanges.data(),
+ DynamicExceptions.size(),
+ NoexceptExpr.isUsable() ? NoexceptExpr.get() : nullptr,
+ /*ExceptionSpecTokens*/ nullptr,
+ /*DeclsInPrototype=*/std::nullopt, LParenLoc, FunLocalRangeEnd,
+ D, TrailingReturnType, TrailingReturnTypeLoc, &DS),
+ std::move(Attr), DeclEndLoc);
+ };
+
+ if (Tok.is(tok::l_paren)) {
+ ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
+ Scope::FunctionDeclarationScope |
+ Scope::DeclScope);
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ SourceLocation LParenLoc = T.getOpenLocation();
+
+ // Parse parameter-declaration-clause.
+ SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
+ SourceLocation EllipsisLoc;
+
+ if (Tok.isNot(tok::r_paren)) {
+ Actions.RecordParsingTemplateParameterDepth(
+ CurTemplateDepthTracker.getOriginalDepth());
+
+ ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc);
+ // For a generic lambda, each 'auto' within the parameter declaration
+ // clause creates a template type parameter, so increment the depth.
+ // If we've parsed any explicit template parameters, then the depth will
+ // have already been incremented. So we make sure that at most a single
+ // depth level is added.
+ if (Actions.getCurGenericLambda())
+ CurTemplateDepthTracker.setAddedDepth(1);
+ }
+
+ T.consumeClose();
+
+ // Parse lambda-specifiers.
+ ParseLambdaSpecifiers(LParenLoc, /*DeclEndLoc=*/T.getCloseLocation(),
+ ParamInfo, EllipsisLoc);
+
+ // Parse requires-clause[opt].
+ if (Tok.is(tok::kw_requires))
+ ParseTrailingRequiresClause(D);
+ } else if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute,
+ tok::kw_constexpr, tok::kw_consteval, tok::kw_static,
+ tok::kw___private, tok::kw___global, tok::kw___local,
+ tok::kw___constant, tok::kw___generic,
+ tok::kw_groupshared, tok::kw_requires,
+ tok::kw_noexcept) ||
+ (Tok.is(tok::l_square) && NextToken().is(tok::l_square))) {
+ if (!getLangOpts().CPlusPlus2b)
+ // It's common to forget that one needs '()' before 'mutable', an
+ // attribute specifier, the result type, or the requires clause. Deal with
+ // this.
+ Diag(Tok, diag::ext_lambda_missing_parens)
+ << FixItHint::CreateInsertion(Tok.getLocation(), "() ");
+
+ SourceLocation NoLoc;
+ // Parse lambda-specifiers.
+ std::vector<DeclaratorChunk::ParamInfo> EmptyParamInfo;
+ ParseLambdaSpecifiers(/*LParenLoc=*/NoLoc, /*RParenLoc=*/NoLoc,
+ EmptyParamInfo, /*EllipsisLoc=*/NoLoc);
+ }
+
+ WarnIfHasCUDATargetAttr();
+
+ // FIXME: Rename BlockScope -> ClosureScope if we decide to continue using
+ // it.
+ unsigned ScopeFlags = Scope::BlockScope | Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope;
+ ParseScope BodyScope(this, ScopeFlags);
+
+ Actions.ActOnStartOfLambdaDefinition(Intro, D, getCurScope());
+
+ // Parse compound-statement.
+ if (!Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::err_expected_lambda_body);
+ Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
+ return ExprError();
+ }
+
+ StmtResult Stmt(ParseCompoundStatementBody());
+ BodyScope.Exit();
+ TemplateParamScope.Exit();
+
+ if (!Stmt.isInvalid() && !TrailingReturnType.isInvalid())
+ return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get(), getCurScope());
+
+ Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
+ return ExprError();
+}
+
+/// ParseCXXCasts - This handles the various ways to cast expressions to another
+/// type.
+///
+/// postfix-expression: [C++ 5.2p1]
+/// 'dynamic_cast' '<' type-name '>' '(' expression ')'
+/// 'static_cast' '<' type-name '>' '(' expression ')'
+/// 'reinterpret_cast' '<' type-name '>' '(' expression ')'
+/// 'const_cast' '<' type-name '>' '(' expression ')'
+///
+/// C++ for OpenCL s2.3.1 adds:
+/// 'addrspace_cast' '<' type-name '>' '(' expression ')'
+ExprResult Parser::ParseCXXCasts() {
+ tok::TokenKind Kind = Tok.getKind();
+ const char *CastName = nullptr; // For error messages
+
+ switch (Kind) {
+ default: llvm_unreachable("Unknown C++ cast!");
+ case tok::kw_addrspace_cast: CastName = "addrspace_cast"; break;
+ case tok::kw_const_cast: CastName = "const_cast"; break;
+ case tok::kw_dynamic_cast: CastName = "dynamic_cast"; break;
+ case tok::kw_reinterpret_cast: CastName = "reinterpret_cast"; break;
+ case tok::kw_static_cast: CastName = "static_cast"; break;
+ }
+
+ SourceLocation OpLoc = ConsumeToken();
+ SourceLocation LAngleBracketLoc = Tok.getLocation();
+
+ // Check for "<::" which is parsed as "[:". If found, fix token stream,
+ // diagnose error, suggest fix, and recover parsing.
+ if (Tok.is(tok::l_square) && Tok.getLength() == 2) {
+ Token Next = NextToken();
+ if (Next.is(tok::colon) && areTokensAdjacent(Tok, Next))
+ FixDigraph(*this, PP, Tok, Next, Kind, /*AtDigraph*/true);
+ }
+
+ if (ExpectAndConsume(tok::less, diag::err_expected_less_after, CastName))
+ return ExprError();
+
+ // Parse the common declaration-specifiers piece.
+ DeclSpec DS(AttrFactory);
+ ParseSpecifierQualifierList(DS, /*AccessSpecifier=*/AS_none,
+ DeclSpecContext::DSC_type_specifier);
+
+ // Parse the abstract-declarator, if present.
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ ParseDeclarator(DeclaratorInfo);
+
+ SourceLocation RAngleBracketLoc = Tok.getLocation();
+
+ if (ExpectAndConsume(tok::greater))
+ return ExprError(Diag(LAngleBracketLoc, diag::note_matching) << tok::less);
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+
+ if (T.expectAndConsume(diag::err_expected_lparen_after, CastName))
+ return ExprError();
+
+ ExprResult Result = ParseExpression();
+
+ // Match the ')'.
+ T.consumeClose();
+
+ if (!Result.isInvalid() && !DeclaratorInfo.isInvalidType())
+ Result = Actions.ActOnCXXNamedCast(OpLoc, Kind,
+ LAngleBracketLoc, DeclaratorInfo,
+ RAngleBracketLoc,
+ T.getOpenLocation(), Result.get(),
+ T.getCloseLocation());
+
+ return Result;
+}
+
+/// ParseCXXTypeid - This handles the C++ typeid expression.
+///
+/// postfix-expression: [C++ 5.2p1]
+/// 'typeid' '(' expression ')'
+/// 'typeid' '(' type-id ')'
+///
+ExprResult Parser::ParseCXXTypeid() {
+ assert(Tok.is(tok::kw_typeid) && "Not 'typeid'!");
+
+ SourceLocation OpLoc = ConsumeToken();
+ SourceLocation LParenLoc, RParenLoc;
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+
+ // typeid expressions are always parenthesized.
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "typeid"))
+ return ExprError();
+ LParenLoc = T.getOpenLocation();
+
+ ExprResult Result;
+
+ // C++0x [expr.typeid]p3:
+ // When typeid is applied to an expression other than an lvalue of a
+ // polymorphic class type [...] The expression is an unevaluated
+ // operand (Clause 5).
+ //
+ // Note that we can't tell whether the expression is an lvalue of a
+ // polymorphic class type until after we've parsed the expression; we
+ // speculatively assume the subexpression is unevaluated, and fix it up
+ // later.
+ //
+ // We enter the unevaluated context before trying to determine whether we
+ // have a type-id, because the tentative parse logic will try to resolve
+ // names, and must treat them as unevaluated.
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated,
+ Sema::ReuseLambdaContextDecl);
+
+ if (isTypeIdInParens()) {
+ TypeResult Ty = ParseTypeName();
+
+ // Match the ')'.
+ T.consumeClose();
+ RParenLoc = T.getCloseLocation();
+ if (Ty.isInvalid() || RParenLoc.isInvalid())
+ return ExprError();
+
+ Result = Actions.ActOnCXXTypeid(OpLoc, LParenLoc, /*isType=*/true,
+ Ty.get().getAsOpaquePtr(), RParenLoc);
+ } else {
+ Result = ParseExpression();
+
+ // Match the ')'.
+ if (Result.isInvalid())
+ SkipUntil(tok::r_paren, StopAtSemi);
+ else {
+ T.consumeClose();
+ RParenLoc = T.getCloseLocation();
+ if (RParenLoc.isInvalid())
+ return ExprError();
+
+ Result = Actions.ActOnCXXTypeid(OpLoc, LParenLoc, /*isType=*/false,
+ Result.get(), RParenLoc);
+ }
+ }
+
+ return Result;
+}
+
+/// ParseCXXUuidof - This handles the Microsoft C++ __uuidof expression.
+///
+/// '__uuidof' '(' expression ')'
+/// '__uuidof' '(' type-id ')'
+///
+ExprResult Parser::ParseCXXUuidof() {
+ assert(Tok.is(tok::kw___uuidof) && "Not '__uuidof'!");
+
+ SourceLocation OpLoc = ConsumeToken();
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+
+ // __uuidof expressions are always parenthesized.
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "__uuidof"))
+ return ExprError();
+
+ ExprResult Result;
+
+ if (isTypeIdInParens()) {
+ TypeResult Ty = ParseTypeName();
+
+ // Match the ')'.
+ T.consumeClose();
+
+ if (Ty.isInvalid())
+ return ExprError();
+
+ Result = Actions.ActOnCXXUuidof(OpLoc, T.getOpenLocation(), /*isType=*/true,
+ Ty.get().getAsOpaquePtr(),
+ T.getCloseLocation());
+ } else {
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+ Result = ParseExpression();
+
+ // Match the ')'.
+ if (Result.isInvalid())
+ SkipUntil(tok::r_paren, StopAtSemi);
+ else {
+ T.consumeClose();
+
+ Result = Actions.ActOnCXXUuidof(OpLoc, T.getOpenLocation(),
+ /*isType=*/false,
+ Result.get(), T.getCloseLocation());
+ }
+ }
+
+ return Result;
+}
+
+/// Parse a C++ pseudo-destructor expression after the base,
+/// . or -> operator, and nested-name-specifier have already been
+/// parsed. We're handling this fragment of the grammar:
+///
+/// postfix-expression: [C++2a expr.post]
+/// postfix-expression . template[opt] id-expression
+/// postfix-expression -> template[opt] id-expression
+///
+/// id-expression:
+/// qualified-id
+/// unqualified-id
+///
+/// qualified-id:
+/// nested-name-specifier template[opt] unqualified-id
+///
+/// nested-name-specifier:
+/// type-name ::
+/// decltype-specifier :: FIXME: not implemented, but probably only
+/// allowed in C++ grammar by accident
+/// nested-name-specifier identifier ::
+/// nested-name-specifier template[opt] simple-template-id ::
+/// [...]
+///
+/// unqualified-id:
+/// ~ type-name
+/// ~ decltype-specifier
+/// [...]
+///
+/// ... where the all but the last component of the nested-name-specifier
+/// has already been parsed, and the base expression is not of a non-dependent
+/// class type.
+ExprResult
+Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
+ tok::TokenKind OpKind,
+ CXXScopeSpec &SS,
+ ParsedType ObjectType) {
+ // If the last component of the (optional) nested-name-specifier is
+ // template[opt] simple-template-id, it has already been annotated.
+ UnqualifiedId FirstTypeName;
+ SourceLocation CCLoc;
+ if (Tok.is(tok::identifier)) {
+ FirstTypeName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ ConsumeToken();
+ assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
+ CCLoc = ConsumeToken();
+ } else if (Tok.is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ // FIXME: Carry on and build an AST representation for tooling.
+ if (TemplateId->isInvalid())
+ return ExprError();
+ FirstTypeName.setTemplateId(TemplateId);
+ ConsumeAnnotationToken();
+ assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
+ CCLoc = ConsumeToken();
+ } else {
+ assert(SS.isEmpty() && "missing last component of nested name specifier");
+ FirstTypeName.setIdentifier(nullptr, SourceLocation());
+ }
+
+ // Parse the tilde.
+ assert(Tok.is(tok::tilde) && "ParseOptionalCXXScopeSpecifier fail");
+ SourceLocation TildeLoc = ConsumeToken();
+
+ if (Tok.is(tok::kw_decltype) && !FirstTypeName.isValid()) {
+ DeclSpec DS(AttrFactory);
+ ParseDecltypeSpecifier(DS);
+ if (DS.getTypeSpecType() == TST_error)
+ return ExprError();
+ return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind,
+ TildeLoc, DS);
+ }
+
+ if (!Tok.is(tok::identifier)) {
+ Diag(Tok, diag::err_destructor_tilde_identifier);
+ return ExprError();
+ }
+
+ // Parse the second type.
+ UnqualifiedId SecondTypeName;
+ IdentifierInfo *Name = Tok.getIdentifierInfo();
+ SourceLocation NameLoc = ConsumeToken();
+ SecondTypeName.setIdentifier(Name, NameLoc);
+
+ // If there is a '<', the second type name is a template-id. Parse
+ // it as such.
+ //
+ // FIXME: This is not a context in which a '<' is assumed to start a template
+ // argument list. This affects examples such as
+ // void f(auto *p) { p->~X<int>(); }
+ // ... but there's no ambiguity, and nowhere to write 'template' in such an
+ // example, so we accept it anyway.
+ if (Tok.is(tok::less) &&
+ ParseUnqualifiedIdTemplateId(
+ SS, ObjectType, Base && Base->containsErrors(), SourceLocation(),
+ Name, NameLoc, false, SecondTypeName,
+ /*AssumeTemplateId=*/true))
+ return ExprError();
+
+ return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind,
+ SS, FirstTypeName, CCLoc, TildeLoc,
+ SecondTypeName);
+}
+
+/// ParseCXXBoolLiteral - This handles the C++ Boolean literals.
+///
+/// boolean-literal: [C++ 2.13.5]
+/// 'true'
+/// 'false'
+ExprResult Parser::ParseCXXBoolLiteral() {
+ tok::TokenKind Kind = Tok.getKind();
+ return Actions.ActOnCXXBoolLiteral(ConsumeToken(), Kind);
+}
+
+/// ParseThrowExpression - This handles the C++ throw expression.
+///
+/// throw-expression: [C++ 15]
+/// 'throw' assignment-expression[opt]
+ExprResult Parser::ParseThrowExpression() {
+ assert(Tok.is(tok::kw_throw) && "Not throw!");
+ SourceLocation ThrowLoc = ConsumeToken(); // Eat the throw token.
+
+ // If the current token isn't the start of an assignment-expression,
+ // then the expression is not present. This handles things like:
+ // "C ? throw : (void)42", which is crazy but legal.
+ switch (Tok.getKind()) { // FIXME: move this predicate somewhere common.
+ case tok::semi:
+ case tok::r_paren:
+ case tok::r_square:
+ case tok::r_brace:
+ case tok::colon:
+ case tok::comma:
+ return Actions.ActOnCXXThrow(getCurScope(), ThrowLoc, nullptr);
+
+ default:
+ ExprResult Expr(ParseAssignmentExpression());
+ if (Expr.isInvalid()) return Expr;
+ return Actions.ActOnCXXThrow(getCurScope(), ThrowLoc, Expr.get());
+ }
+}
+
+/// Parse the C++ Coroutines co_yield expression.
+///
+/// co_yield-expression:
+/// 'co_yield' assignment-expression[opt]
+ExprResult Parser::ParseCoyieldExpression() {
+ assert(Tok.is(tok::kw_co_yield) && "Not co_yield!");
+
+ SourceLocation Loc = ConsumeToken();
+ ExprResult Expr = Tok.is(tok::l_brace) ? ParseBraceInitializer()
+ : ParseAssignmentExpression();
+ if (!Expr.isInvalid())
+ Expr = Actions.ActOnCoyieldExpr(getCurScope(), Loc, Expr.get());
+ return Expr;
+}
+
+/// ParseCXXThis - This handles the C++ 'this' pointer.
+///
+/// C++ 9.3.2: In the body of a non-static member function, the keyword this is
+/// a non-lvalue expression whose value is the address of the object for which
+/// the function is called.
+ExprResult Parser::ParseCXXThis() {
+ assert(Tok.is(tok::kw_this) && "Not 'this'!");
+ SourceLocation ThisLoc = ConsumeToken();
+ return Actions.ActOnCXXThis(ThisLoc);
+}
+
+/// ParseCXXTypeConstructExpression - Parse construction of a specified type.
+/// Can be interpreted either as function-style casting ("int(x)")
+/// or class type construction ("ClassType(x,y,z)")
+/// or creation of a value-initialized type ("int()").
+/// See [C++ 5.2.3].
+///
+/// postfix-expression: [C++ 5.2p1]
+/// simple-type-specifier '(' expression-list[opt] ')'
+/// [C++0x] simple-type-specifier braced-init-list
+/// typename-specifier '(' expression-list[opt] ')'
+/// [C++0x] typename-specifier braced-init-list
+///
+/// In C++1z onwards, the type specifier can also be a template-name.
+ExprResult
+Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) {
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::FunctionalCast);
+ ParsedType TypeRep = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo).get();
+
+ assert((Tok.is(tok::l_paren) ||
+ (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)))
+ && "Expected '(' or '{'!");
+
+ if (Tok.is(tok::l_brace)) {
+ PreferredType.enterTypeCast(Tok.getLocation(), TypeRep.get());
+ ExprResult Init = ParseBraceInitializer();
+ if (Init.isInvalid())
+ return Init;
+ Expr *InitList = Init.get();
+ return Actions.ActOnCXXTypeConstructExpr(
+ TypeRep, InitList->getBeginLoc(), MultiExprArg(&InitList, 1),
+ InitList->getEndLoc(), /*ListInitialization=*/true);
+ } else {
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ PreferredType.enterTypeCast(Tok.getLocation(), TypeRep.get());
+
+ ExprVector Exprs;
+
+ auto RunSignatureHelp = [&]() {
+ QualType PreferredType;
+ if (TypeRep)
+ PreferredType = Actions.ProduceConstructorSignatureHelp(
+ TypeRep.get()->getCanonicalTypeInternal(), DS.getEndLoc(), Exprs,
+ T.getOpenLocation(), /*Braced=*/false);
+ CalledSignatureHelp = true;
+ return PreferredType;
+ };
+
+ if (Tok.isNot(tok::r_paren)) {
+ if (ParseExpressionList(Exprs, [&] {
+ PreferredType.enterFunctionArgument(Tok.getLocation(),
+ RunSignatureHelp);
+ })) {
+ if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
+ RunSignatureHelp();
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+ }
+
+ // Match the ')'.
+ T.consumeClose();
+
+ // TypeRep could be null, if it references an invalid typedef.
+ if (!TypeRep)
+ return ExprError();
+
+ return Actions.ActOnCXXTypeConstructExpr(TypeRep, T.getOpenLocation(),
+ Exprs, T.getCloseLocation(),
+ /*ListInitialization=*/false);
+ }
+}
+
+Parser::DeclGroupPtrTy
+Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
+ ParsedAttributes &Attrs) {
+ assert(Tok.is(tok::kw_using) && "Expected using");
+ assert((Context == DeclaratorContext::ForInit ||
+ Context == DeclaratorContext::SelectionInit) &&
+ "Unexpected Declarator Context");
+ DeclGroupPtrTy DG;
+ SourceLocation DeclStart = ConsumeToken(), DeclEnd;
+
+ DG = ParseUsingDeclaration(Context, {}, DeclStart, DeclEnd, Attrs, AS_none);
+ if (!DG)
+ return DG;
+
+ Diag(DeclStart, !getLangOpts().CPlusPlus2b
+ ? diag::ext_alias_in_init_statement
+ : diag::warn_cxx20_alias_in_init_statement)
+ << SourceRange(DeclStart, DeclEnd);
+
+ return DG;
+}
+
+/// ParseCXXCondition - if/switch/while condition expression.
+///
+/// condition:
+/// expression
+/// type-specifier-seq declarator '=' assignment-expression
+/// [C++11] type-specifier-seq declarator '=' initializer-clause
+/// [C++11] type-specifier-seq declarator braced-init-list
+/// [Clang] type-specifier-seq ref-qualifier[opt] '[' identifier-list ']'
+/// brace-or-equal-initializer
+/// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt]
+/// '=' assignment-expression
+///
+/// In C++1z, a condition may in some contexts be preceded by an
+/// optional init-statement. This function will parse that too.
+///
+/// \param InitStmt If non-null, an init-statement is permitted, and if present
+/// will be parsed and stored here.
+///
+/// \param Loc The location of the start of the statement that requires this
+/// condition, e.g., the "for" in a for loop.
+///
+/// \param MissingOK Whether an empty condition is acceptable here. Otherwise
+/// it is considered an error to be recovered from.
+///
+/// \param FRI If non-null, a for range declaration is permitted, and if
+/// present will be parsed and stored here, and a null result will be returned.
+///
+/// \param EnterForConditionScope If true, enter a continue/break scope at the
+/// appropriate moment for a 'for' loop.
+///
+/// \returns The parsed condition.
+Sema::ConditionResult
+Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
+ Sema::ConditionKind CK, bool MissingOK,
+ ForRangeInfo *FRI, bool EnterForConditionScope) {
+ // Helper to ensure we always enter a continue/break scope if requested.
+ struct ForConditionScopeRAII {
+ Scope *S;
+ void enter(bool IsConditionVariable) {
+ if (S) {
+ S->AddFlags(Scope::BreakScope | Scope::ContinueScope);
+ S->setIsConditionVarScope(IsConditionVariable);
+ }
+ }
+ ~ForConditionScopeRAII() {
+ if (S)
+ S->setIsConditionVarScope(false);
+ }
+ } ForConditionScope{EnterForConditionScope ? getCurScope() : nullptr};
+
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+ PreferredType.enterCondition(Actions, Tok.getLocation());
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Condition);
+ return Sema::ConditionError();
+ }
+
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseCXX11Attributes(attrs);
+
+ const auto WarnOnInit = [this, &CK] {
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
+ ? diag::warn_cxx14_compat_init_statement
+ : diag::ext_init_statement)
+ << (CK == Sema::ConditionKind::Switch);
+ };
+
+ // Determine what kind of thing we have.
+ switch (isCXXConditionDeclarationOrInitStatement(InitStmt, FRI)) {
+ case ConditionOrInitStatement::Expression: {
+ // If this is a for loop, we're entering its condition.
+ ForConditionScope.enter(/*IsConditionVariable=*/false);
+
+ ProhibitAttributes(attrs);
+
+ // We can have an empty expression here.
+ // if (; true);
+ if (InitStmt && Tok.is(tok::semi)) {
+ WarnOnInit();
+ SourceLocation SemiLoc = Tok.getLocation();
+ if (!Tok.hasLeadingEmptyMacro() && !SemiLoc.isMacroID()) {
+ Diag(SemiLoc, diag::warn_empty_init_statement)
+ << (CK == Sema::ConditionKind::Switch)
+ << FixItHint::CreateRemoval(SemiLoc);
+ }
+ ConsumeToken();
+ *InitStmt = Actions.ActOnNullStmt(SemiLoc);
+ return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+ }
+
+ // Parse the expression.
+ ExprResult Expr = ParseExpression(); // expression
+ if (Expr.isInvalid())
+ return Sema::ConditionError();
+
+ if (InitStmt && Tok.is(tok::semi)) {
+ WarnOnInit();
+ *InitStmt = Actions.ActOnExprStmt(Expr.get());
+ ConsumeToken();
+ return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+ }
+
+ return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK,
+ MissingOK);
+ }
+
+ case ConditionOrInitStatement::InitStmtDecl: {
+ WarnOnInit();
+ DeclGroupPtrTy DG;
+ SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
+ if (Tok.is(tok::kw_using))
+ DG = ParseAliasDeclarationInInitStatement(
+ DeclaratorContext::SelectionInit, attrs);
+ else {
+ ParsedAttributes DeclSpecAttrs(AttrFactory);
+ DG = ParseSimpleDeclaration(DeclaratorContext::SelectionInit, DeclEnd,
+ attrs, DeclSpecAttrs, /*RequireSemi=*/true);
+ }
+ *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
+ return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+ }
+
+ case ConditionOrInitStatement::ForRangeDecl: {
+ // This is 'for (init-stmt; for-range-decl : range-expr)'.
+ // We're not actually in a for loop yet, so 'break' and 'continue' aren't
+ // permitted here.
+ assert(FRI && "should not parse a for range declaration here");
+ SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
+ ParsedAttributes DeclSpecAttrs(AttrFactory);
+ DeclGroupPtrTy DG = ParseSimpleDeclaration(
+ DeclaratorContext::ForInit, DeclEnd, attrs, DeclSpecAttrs, false, FRI);
+ FRI->LoopVar = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation());
+ assert((FRI->ColonLoc.isValid() || !DG) &&
+ "cannot find for range declaration");
+ return Sema::ConditionResult();
+ }
+
+ case ConditionOrInitStatement::ConditionDecl:
+ case ConditionOrInitStatement::Error:
+ break;
+ }
+
+ // If this is a for loop, we're entering its condition.
+ ForConditionScope.enter(/*IsConditionVariable=*/true);
+
+ // type-specifier-seq
+ DeclSpec DS(AttrFactory);
+ ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_condition);
+
+ // declarator
+ Declarator DeclaratorInfo(DS, attrs, DeclaratorContext::Condition);
+ ParseDeclarator(DeclaratorInfo);
+
+ // simple-asm-expr[opt]
+ if (Tok.is(tok::kw_asm)) {
+ SourceLocation Loc;
+ ExprResult AsmLabel(ParseSimpleAsm(/*ForAsmLabel*/ true, &Loc));
+ if (AsmLabel.isInvalid()) {
+ SkipUntil(tok::semi, StopAtSemi);
+ return Sema::ConditionError();
+ }
+ DeclaratorInfo.setAsmLabel(AsmLabel.get());
+ DeclaratorInfo.SetRangeEnd(Loc);
+ }
+
+ // If attributes are present, parse them.
+ MaybeParseGNUAttributes(DeclaratorInfo);
+
+ // Type-check the declaration itself.
+ DeclResult Dcl = Actions.ActOnCXXConditionDeclaration(getCurScope(),
+ DeclaratorInfo);
+ if (Dcl.isInvalid())
+ return Sema::ConditionError();
+ Decl *DeclOut = Dcl.get();
+
+ // '=' assignment-expression
+ // If a '==' or '+=' is found, suggest a fixit to '='.
+ bool CopyInitialization = isTokenEqualOrEqualTypo();
+ if (CopyInitialization)
+ ConsumeToken();
+
+ ExprResult InitExpr = ExprError();
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ Diag(Tok.getLocation(),
+ diag::warn_cxx98_compat_generalized_initializer_lists);
+ InitExpr = ParseBraceInitializer();
+ } else if (CopyInitialization) {
+ PreferredType.enterVariableInit(Tok.getLocation(), DeclOut);
+ InitExpr = ParseAssignmentExpression();
+ } else if (Tok.is(tok::l_paren)) {
+ // This was probably an attempt to initialize the variable.
+ SourceLocation LParen = ConsumeParen(), RParen = LParen;
+ if (SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch))
+ RParen = ConsumeParen();
+ Diag(DeclOut->getLocation(),
+ diag::err_expected_init_in_condition_lparen)
+ << SourceRange(LParen, RParen);
+ } else {
+ Diag(DeclOut->getLocation(), diag::err_expected_init_in_condition);
+ }
+
+ if (!InitExpr.isInvalid())
+ Actions.AddInitializerToDecl(DeclOut, InitExpr.get(), !CopyInitialization);
+ else
+ Actions.ActOnInitializerError(DeclOut);
+
+ Actions.FinalizeDeclaration(DeclOut);
+ return Actions.ActOnConditionVariable(DeclOut, Loc, CK);
+}
+
+/// ParseCXXSimpleTypeSpecifier - [C++ 7.1.5.2] Simple type specifiers.
+/// This should only be called when the current token is known to be part of
+/// simple-type-specifier.
+///
+/// simple-type-specifier:
+/// '::'[opt] nested-name-specifier[opt] type-name
+/// '::'[opt] nested-name-specifier 'template' simple-template-id [TODO]
+/// char
+/// wchar_t
+/// bool
+/// short
+/// int
+/// long
+/// signed
+/// unsigned
+/// float
+/// double
+/// void
+/// [GNU] typeof-specifier
+/// [C++0x] auto [TODO]
+///
+/// type-name:
+/// class-name
+/// enum-name
+/// typedef-name
+///
+void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
+ DS.SetRangeStart(Tok.getLocation());
+ const char *PrevSpec;
+ unsigned DiagID;
+ SourceLocation Loc = Tok.getLocation();
+ const clang::PrintingPolicy &Policy =
+ Actions.getASTContext().getPrintingPolicy();
+
+ switch (Tok.getKind()) {
+ case tok::identifier: // foo::bar
+ case tok::coloncolon: // ::foo::bar
+ llvm_unreachable("Annotation token should already be formed!");
+ default:
+ llvm_unreachable("Not a simple-type-specifier token!");
+
+ // type-name
+ case tok::annot_typename: {
+ DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID,
+ getTypeAnnotation(Tok), Policy);
+ DS.SetRangeEnd(Tok.getAnnotationEndLoc());
+ ConsumeAnnotationToken();
+
+ DS.Finish(Actions, Policy);
+ return;
+ }
+
+ case tok::kw__ExtInt:
+ case tok::kw__BitInt: {
+ DiagnoseBitIntUse(Tok);
+ ExprResult ER = ParseExtIntegerArgument();
+ if (ER.isInvalid())
+ DS.SetTypeSpecError();
+ else
+ DS.SetBitIntType(Loc, ER.get(), PrevSpec, DiagID, Policy);
+
+ // Do this here because we have already consumed the close paren.
+ DS.SetRangeEnd(PrevTokLocation);
+ DS.Finish(Actions, Policy);
+ return;
+ }
+
+ // builtin types
+ case tok::kw_short:
+ DS.SetTypeSpecWidth(TypeSpecifierWidth::Short, Loc, PrevSpec, DiagID,
+ Policy);
+ break;
+ case tok::kw_long:
+ DS.SetTypeSpecWidth(TypeSpecifierWidth::Long, Loc, PrevSpec, DiagID,
+ Policy);
+ break;
+ case tok::kw___int64:
+ DS.SetTypeSpecWidth(TypeSpecifierWidth::LongLong, Loc, PrevSpec, DiagID,
+ Policy);
+ break;
+ case tok::kw_signed:
+ DS.SetTypeSpecSign(TypeSpecifierSign::Signed, Loc, PrevSpec, DiagID);
+ break;
+ case tok::kw_unsigned:
+ DS.SetTypeSpecSign(TypeSpecifierSign::Unsigned, Loc, PrevSpec, DiagID);
+ break;
+ case tok::kw_void:
+ DS.SetTypeSpecType(DeclSpec::TST_void, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_auto:
+ DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_char:
+ DS.SetTypeSpecType(DeclSpec::TST_char, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_int:
+ DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw___int128:
+ DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw___bf16:
+ DS.SetTypeSpecType(DeclSpec::TST_BFloat16, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_half:
+ DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_float:
+ DS.SetTypeSpecType(DeclSpec::TST_float, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_double:
+ DS.SetTypeSpecType(DeclSpec::TST_double, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw__Float16:
+ DS.SetTypeSpecType(DeclSpec::TST_float16, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw___float128:
+ DS.SetTypeSpecType(DeclSpec::TST_float128, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw___ibm128:
+ DS.SetTypeSpecType(DeclSpec::TST_ibm128, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_wchar_t:
+ DS.SetTypeSpecType(DeclSpec::TST_wchar, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_char8_t:
+ DS.SetTypeSpecType(DeclSpec::TST_char8, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_char16_t:
+ DS.SetTypeSpecType(DeclSpec::TST_char16, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_char32_t:
+ DS.SetTypeSpecType(DeclSpec::TST_char32, Loc, PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_bool:
+ DS.SetTypeSpecType(DeclSpec::TST_bool, Loc, PrevSpec, DiagID, Policy);
+ break;
+#define GENERIC_IMAGE_TYPE(ImgType, Id) \
+ case tok::kw_##ImgType##_t: \
+ DS.SetTypeSpecType(DeclSpec::TST_##ImgType##_t, Loc, PrevSpec, DiagID, \
+ Policy); \
+ break;
+#include "clang/Basic/OpenCLImageTypes.def"
+
+ case tok::annot_decltype:
+ case tok::kw_decltype:
+ DS.SetRangeEnd(ParseDecltypeSpecifier(DS));
+ return DS.Finish(Actions, Policy);
+
+ // GNU typeof support.
+ case tok::kw_typeof:
+ ParseTypeofSpecifier(DS);
+ DS.Finish(Actions, Policy);
+ return;
+ }
+ ConsumeAnyToken();
+ DS.SetRangeEnd(PrevTokLocation);
+ DS.Finish(Actions, Policy);
+}
+
+/// ParseCXXTypeSpecifierSeq - Parse a C++ type-specifier-seq (C++
+/// [dcl.name]), which is a non-empty sequence of type-specifiers,
+/// e.g., "const short int". Note that the DeclSpec is *not* finished
+/// by parsing the type-specifier-seq, because these sequences are
+/// typically followed by some form of declarator. Returns true and
+/// emits diagnostics if this is not a type-specifier-seq, false
+/// otherwise.
+///
+/// type-specifier-seq: [C++ 8.1]
+/// type-specifier type-specifier-seq[opt]
+///
+bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context) {
+ ParseSpecifierQualifierList(DS, AS_none,
+ getDeclSpecContextFromDeclaratorContext(Context));
+ DS.Finish(Actions, Actions.getASTContext().getPrintingPolicy());
+ return false;
+}
+
+/// Finish parsing a C++ unqualified-id that is a template-id of
+/// some form.
+///
+/// This routine is invoked when a '<' is encountered after an identifier or
+/// operator-function-id is parsed by \c ParseUnqualifiedId() to determine
+/// whether the unqualified-id is actually a template-id. This routine will
+/// then parse the template arguments and form the appropriate template-id to
+/// return to the caller.
+///
+/// \param SS the nested-name-specifier that precedes this template-id, if
+/// we're actually parsing a qualified-id.
+///
+/// \param ObjectType if this unqualified-id occurs within a member access
+/// expression, the type of the base object whose member is being accessed.
+///
+/// \param ObjectHadErrors this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+///
+/// \param Name for constructor and destructor names, this is the actual
+/// identifier that may be a template-name.
+///
+/// \param NameLoc the location of the class-name in a constructor or
+/// destructor.
+///
+/// \param EnteringContext whether we're entering the scope of the
+/// nested-name-specifier.
+///
+/// \param Id as input, describes the template-name or operator-function-id
+/// that precedes the '<'. If template arguments were parsed successfully,
+/// will be updated with the template-id.
+///
+/// \param AssumeTemplateId When true, this routine will assume that the name
+/// refers to a template without performing name lookup to verify.
+///
+/// \returns true if a parse error occurred, false otherwise.
+bool Parser::ParseUnqualifiedIdTemplateId(
+ CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+ SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc,
+ bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) {
+ assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");
+
+ TemplateTy Template;
+ TemplateNameKind TNK = TNK_Non_template;
+ switch (Id.getKind()) {
+ case UnqualifiedIdKind::IK_Identifier:
+ case UnqualifiedIdKind::IK_OperatorFunctionId:
+ case UnqualifiedIdKind::IK_LiteralOperatorId:
+ if (AssumeTemplateId) {
+ // We defer the injected-class-name checks until we've found whether
+ // this template-id is used to form a nested-name-specifier or not.
+ TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Id,
+ ObjectType, EnteringContext, Template,
+ /*AllowInjectedClassName*/ true);
+ } else {
+ bool MemberOfUnknownSpecialization;
+ TNK = Actions.isTemplateName(getCurScope(), SS,
+ TemplateKWLoc.isValid(), Id,
+ ObjectType, EnteringContext, Template,
+ MemberOfUnknownSpecialization);
+ // If lookup found nothing but we're assuming that this is a template
+ // name, double-check that makes sense syntactically before committing
+ // to it.
+ if (TNK == TNK_Undeclared_template &&
+ isTemplateArgumentList(0) == TPResult::False)
+ return false;
+
+ if (TNK == TNK_Non_template && MemberOfUnknownSpecialization &&
+ ObjectType && isTemplateArgumentList(0) == TPResult::True) {
+ // If we had errors before, ObjectType can be dependent even without any
+ // templates, do not report missing template keyword in that case.
+ if (!ObjectHadErrors) {
+ // We have something like t->getAs<T>(), where getAs is a
+ // member of an unknown specialization. However, this will only
+ // parse correctly as a template, so suggest the keyword 'template'
+ // before 'getAs' and treat this as a dependent template name.
+ std::string Name;
+ if (Id.getKind() == UnqualifiedIdKind::IK_Identifier)
+ Name = std::string(Id.Identifier->getName());
+ else {
+ Name = "operator ";
+ if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId)
+ Name += getOperatorSpelling(Id.OperatorFunctionId.Operator);
+ else
+ Name += Id.Identifier->getName();
+ }
+ Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
+ << Name
+ << FixItHint::CreateInsertion(Id.StartLocation, "template ");
+ }
+ TNK = Actions.ActOnTemplateName(
+ getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
+ Template, /*AllowInjectedClassName*/ true);
+ } else if (TNK == TNK_Non_template) {
+ return false;
+ }
+ }
+ break;
+
+ case UnqualifiedIdKind::IK_ConstructorName: {
+ UnqualifiedId TemplateName;
+ bool MemberOfUnknownSpecialization;
+ TemplateName.setIdentifier(Name, NameLoc);
+ TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(),
+ TemplateName, ObjectType,
+ EnteringContext, Template,
+ MemberOfUnknownSpecialization);
+ if (TNK == TNK_Non_template)
+ return false;
+ break;
+ }
+
+ case UnqualifiedIdKind::IK_DestructorName: {
+ UnqualifiedId TemplateName;
+ bool MemberOfUnknownSpecialization;
+ TemplateName.setIdentifier(Name, NameLoc);
+ if (ObjectType) {
+ TNK = Actions.ActOnTemplateName(
+ getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
+ EnteringContext, Template, /*AllowInjectedClassName*/ true);
+ } else {
+ TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(),
+ TemplateName, ObjectType,
+ EnteringContext, Template,
+ MemberOfUnknownSpecialization);
+
+ if (TNK == TNK_Non_template && !Id.DestructorName.get()) {
+ Diag(NameLoc, diag::err_destructor_template_id)
+ << Name << SS.getRange();
+ // Carry on to parse the template arguments before bailing out.
+ }
+ }
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ // Parse the enclosed template argument list.
+ SourceLocation LAngleLoc, RAngleLoc;
+ TemplateArgList TemplateArgs;
+ if (ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs, RAngleLoc,
+ Template))
+ return true;
+
+ // If this is a non-template, we already issued a diagnostic.
+ if (TNK == TNK_Non_template)
+ return true;
+
+ if (Id.getKind() == UnqualifiedIdKind::IK_Identifier ||
+ Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId ||
+ Id.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) {
+ // Form a parsed representation of the template-id to be stored in the
+ // UnqualifiedId.
+
+ // FIXME: Store name for literal operator too.
+ IdentifierInfo *TemplateII =
+ Id.getKind() == UnqualifiedIdKind::IK_Identifier ? Id.Identifier
+ : nullptr;
+ OverloadedOperatorKind OpKind =
+ Id.getKind() == UnqualifiedIdKind::IK_Identifier
+ ? OO_None
+ : Id.OperatorFunctionId.Operator;
+
+ TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create(
+ TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK,
+ LAngleLoc, RAngleLoc, TemplateArgs, /*ArgsInvalid*/false, TemplateIds);
+
+ Id.setTemplateId(TemplateId);
+ return false;
+ }
+
+ // Bundle the template arguments together.
+ ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs);
+
+ // Constructor and destructor names.
+ TypeResult Type = Actions.ActOnTemplateIdType(
+ getCurScope(), SS, TemplateKWLoc, Template, Name, NameLoc, LAngleLoc,
+ TemplateArgsPtr, RAngleLoc, /*IsCtorOrDtorName=*/true);
+ if (Type.isInvalid())
+ return true;
+
+ if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName)
+ Id.setConstructorName(Type.get(), NameLoc, RAngleLoc);
+ else
+ Id.setDestructorName(Id.StartLocation, Type.get(), RAngleLoc);
+
+ return false;
+}
+
+/// Parse an operator-function-id or conversion-function-id as part
+/// of a C++ unqualified-id.
+///
+/// This routine is responsible only for parsing the operator-function-id or
+/// conversion-function-id; it does not handle template arguments in any way.
+///
+/// \code
+/// operator-function-id: [C++ 13.5]
+/// 'operator' operator
+///
+/// operator: one of
+/// new delete new[] delete[]
+/// + - * / % ^ & | ~
+/// ! = < > += -= *= /= %=
+/// ^= &= |= << >> >>= <<= == !=
+/// <= >= && || ++ -- , ->* ->
+/// () [] <=>
+///
+/// conversion-function-id: [C++ 12.3.2]
+/// operator conversion-type-id
+///
+/// conversion-type-id:
+/// type-specifier-seq conversion-declarator[opt]
+///
+/// conversion-declarator:
+/// ptr-operator conversion-declarator[opt]
+/// \endcode
+///
+/// \param SS The nested-name-specifier that preceded this unqualified-id. If
+/// non-empty, then we are parsing the unqualified-id of a qualified-id.
+///
+/// \param EnteringContext whether we are entering the scope of the
+/// nested-name-specifier.
+///
+/// \param ObjectType if this unqualified-id occurs within a member access
+/// expression, the type of the base object whose member is being accessed.
+///
+/// \param Result on a successful parse, contains the parsed unqualified-id.
+///
+/// \returns true if parsing fails, false otherwise.
+bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
+ ParsedType ObjectType,
+ UnqualifiedId &Result) {
+ assert(Tok.is(tok::kw_operator) && "Expected 'operator' keyword");
+
+ // Consume the 'operator' keyword.
+ SourceLocation KeywordLoc = ConsumeToken();
+
+ // Determine what kind of operator name we have.
+ unsigned SymbolIdx = 0;
+ SourceLocation SymbolLocations[3];
+ OverloadedOperatorKind Op = OO_None;
+ switch (Tok.getKind()) {
+ case tok::kw_new:
+ case tok::kw_delete: {
+ bool isNew = Tok.getKind() == tok::kw_new;
+ // Consume the 'new' or 'delete'.
+ SymbolLocations[SymbolIdx++] = ConsumeToken();
+ // Check for array new/delete.
+ if (Tok.is(tok::l_square) &&
+ (!getLangOpts().CPlusPlus11 || NextToken().isNot(tok::l_square))) {
+ // Consume the '[' and ']'.
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid())
+ return true;
+
+ SymbolLocations[SymbolIdx++] = T.getOpenLocation();
+ SymbolLocations[SymbolIdx++] = T.getCloseLocation();
+ Op = isNew? OO_Array_New : OO_Array_Delete;
+ } else {
+ Op = isNew? OO_New : OO_Delete;
+ }
+ break;
+ }
+
+#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \
+ case tok::Token: \
+ SymbolLocations[SymbolIdx++] = ConsumeToken(); \
+ Op = OO_##Name; \
+ break;
+#define OVERLOADED_OPERATOR_MULTI(Name,Spelling,Unary,Binary,MemberOnly)
+#include "clang/Basic/OperatorKinds.def"
+
+ case tok::l_paren: {
+ // Consume the '(' and ')'.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid())
+ return true;
+
+ SymbolLocations[SymbolIdx++] = T.getOpenLocation();
+ SymbolLocations[SymbolIdx++] = T.getCloseLocation();
+ Op = OO_Call;
+ break;
+ }
+
+ case tok::l_square: {
+ // Consume the '[' and ']'.
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid())
+ return true;
+
+ SymbolLocations[SymbolIdx++] = T.getOpenLocation();
+ SymbolLocations[SymbolIdx++] = T.getCloseLocation();
+ Op = OO_Subscript;
+ break;
+ }
+
+ case tok::code_completion: {
+ // Don't try to parse any further.
+ cutOffParsing();
+ // Code completion for the operator name.
+ Actions.CodeCompleteOperatorName(getCurScope());
+ return true;
+ }
+
+ default:
+ break;
+ }
+
+ if (Op != OO_None) {
+ // We have parsed an operator-function-id.
+ Result.setOperatorFunctionId(KeywordLoc, Op, SymbolLocations);
+ return false;
+ }
+
+ // Parse a literal-operator-id.
+ //
+ // literal-operator-id: C++11 [over.literal]
+ // operator string-literal identifier
+ // operator user-defined-string-literal
+
+ if (getLangOpts().CPlusPlus11 && isTokenStringLiteral()) {
+ Diag(Tok.getLocation(), diag::warn_cxx98_compat_literal_operator);
+
+ SourceLocation DiagLoc;
+ unsigned DiagId = 0;
+
+ // We're past translation phase 6, so perform string literal concatenation
+ // before checking for "".
+ SmallVector<Token, 4> Toks;
+ SmallVector<SourceLocation, 4> TokLocs;
+ while (isTokenStringLiteral()) {
+ if (!Tok.is(tok::string_literal) && !DiagId) {
+ // C++11 [over.literal]p1:
+ // The string-literal or user-defined-string-literal in a
+ // literal-operator-id shall have no encoding-prefix [...].
+ DiagLoc = Tok.getLocation();
+ DiagId = diag::err_literal_operator_string_prefix;
+ }
+ Toks.push_back(Tok);
+ TokLocs.push_back(ConsumeStringToken());
+ }
+
+ StringLiteralParser Literal(Toks, PP);
+ if (Literal.hadError)
+ return true;
+
+ // Grab the literal operator's suffix, which will be either the next token
+ // or a ud-suffix from the string literal.
+ bool IsUDSuffix = !Literal.getUDSuffix().empty();
+ IdentifierInfo *II = nullptr;
+ SourceLocation SuffixLoc;
+ if (IsUDSuffix) {
+ II = &PP.getIdentifierTable().get(Literal.getUDSuffix());
+ SuffixLoc =
+ Lexer::AdvanceToTokenCharacter(TokLocs[Literal.getUDSuffixToken()],
+ Literal.getUDSuffixOffset(),
+ PP.getSourceManager(), getLangOpts());
+ } else if (Tok.is(tok::identifier)) {
+ II = Tok.getIdentifierInfo();
+ SuffixLoc = ConsumeToken();
+ TokLocs.push_back(SuffixLoc);
+ } else {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
+ return true;
+ }
+
+ // The string literal must be empty.
+ if (!Literal.GetString().empty() || Literal.Pascal) {
+ // C++11 [over.literal]p1:
+ // The string-literal or user-defined-string-literal in a
+ // literal-operator-id shall [...] contain no characters
+ // other than the implicit terminating '\0'.
+ DiagLoc = TokLocs.front();
+ DiagId = diag::err_literal_operator_string_not_empty;
+ }
+
+ if (DiagId) {
+ // This isn't a valid literal-operator-id, but we think we know
+ // what the user meant. Tell them what they should have written.
+ SmallString<32> Str;
+ Str += "\"\"";
+ Str += II->getName();
+ Diag(DiagLoc, DiagId) << FixItHint::CreateReplacement(
+ SourceRange(TokLocs.front(), TokLocs.back()), Str);
+ }
+
+ Result.setLiteralOperatorId(II, KeywordLoc, SuffixLoc);
+
+ return Actions.checkLiteralOperatorId(SS, Result, IsUDSuffix);
+ }
+
+ // Parse a conversion-function-id.
+ //
+ // conversion-function-id: [C++ 12.3.2]
+ // operator conversion-type-id
+ //
+ // conversion-type-id:
+ // type-specifier-seq conversion-declarator[opt]
+ //
+ // conversion-declarator:
+ // ptr-operator conversion-declarator[opt]
+
+ // Parse the type-specifier-seq.
+ DeclSpec DS(AttrFactory);
+ if (ParseCXXTypeSpecifierSeq(
+ DS, DeclaratorContext::ConversionId)) // FIXME: ObjectType?
+ return true;
+
+ // Parse the conversion-declarator, which is merely a sequence of
+ // ptr-operators.
+ Declarator D(DS, ParsedAttributesView::none(),
+ DeclaratorContext::ConversionId);
+ ParseDeclaratorInternal(D, /*DirectDeclParser=*/nullptr);
+
+ // Finish up the type.
+ TypeResult Ty = Actions.ActOnTypeName(getCurScope(), D);
+ if (Ty.isInvalid())
+ return true;
+
+ // Note that this is a conversion-function-id.
+ Result.setConversionFunctionId(KeywordLoc, Ty.get(),
+ D.getSourceRange().getEnd());
+ return false;
+}
+
+/// Parse a C++ unqualified-id (or a C identifier), which describes the
+/// name of an entity.
+///
+/// \code
+/// unqualified-id: [C++ expr.prim.general]
+/// identifier
+/// operator-function-id
+/// conversion-function-id
+/// [C++0x] literal-operator-id [TODO]
+/// ~ class-name
+/// template-id
+///
+/// \endcode
+///
+/// \param SS The nested-name-specifier that preceded this unqualified-id. If
+/// non-empty, then we are parsing the unqualified-id of a qualified-id.
+///
+/// \param ObjectType if this unqualified-id occurs within a member access
+/// expression, the type of the base object whose member is being accessed.
+///
+/// \param ObjectHadErrors if this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+/// When true, diagnostics for missing 'template' keyword will be supressed.
+///
+/// \param EnteringContext whether we are entering the scope of the
+/// nested-name-specifier.
+///
+/// \param AllowDestructorName whether we allow parsing of a destructor name.
+///
+/// \param AllowConstructorName whether we allow parsing a constructor name.
+///
+/// \param AllowDeductionGuide whether we allow parsing a deduction guide name.
+///
+/// \param Result on a successful parse, contains the parsed unqualified-id.
+///
+/// \returns true if parsing fails, false otherwise.
+bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
+ bool ObjectHadErrors, bool EnteringContext,
+ bool AllowDestructorName,
+ bool AllowConstructorName,
+ bool AllowDeductionGuide,
+ SourceLocation *TemplateKWLoc,
+ UnqualifiedId &Result) {
+ if (TemplateKWLoc)
+ *TemplateKWLoc = SourceLocation();
+
+ // Handle 'A::template B'. This is for template-ids which have not
+ // already been annotated by ParseOptionalCXXScopeSpecifier().
+ bool TemplateSpecified = false;
+ if (Tok.is(tok::kw_template)) {
+ if (TemplateKWLoc && (ObjectType || SS.isSet())) {
+ TemplateSpecified = true;
+ *TemplateKWLoc = ConsumeToken();
+ } else {
+ SourceLocation TemplateLoc = ConsumeToken();
+ Diag(TemplateLoc, diag::err_unexpected_template_in_unqualified_id)
+ << FixItHint::CreateRemoval(TemplateLoc);
+ }
+ }
+
+ // unqualified-id:
+ // identifier
+ // template-id (when it hasn't already been annotated)
+ if (Tok.is(tok::identifier)) {
+ ParseIdentifier:
+ // Consume the identifier.
+ IdentifierInfo *Id = Tok.getIdentifierInfo();
+ SourceLocation IdLoc = ConsumeToken();
+
+ if (!getLangOpts().CPlusPlus) {
+ // If we're not in C++, only identifiers matter. Record the
+ // identifier and return.
+ Result.setIdentifier(Id, IdLoc);
+ return false;
+ }
+
+ ParsedTemplateTy TemplateName;
+ if (AllowConstructorName &&
+ Actions.isCurrentClassName(*Id, getCurScope(), &SS)) {
+ // We have parsed a constructor name.
+ ParsedType Ty = Actions.getConstructorName(*Id, IdLoc, getCurScope(), SS,
+ EnteringContext);
+ if (!Ty)
+ return true;
+ Result.setConstructorName(Ty, IdLoc, IdLoc);
+ } else if (getLangOpts().CPlusPlus17 &&
+ AllowDeductionGuide && SS.isEmpty() &&
+ Actions.isDeductionGuideName(getCurScope(), *Id, IdLoc,
+ &TemplateName)) {
+ // We have parsed a template-name naming a deduction guide.
+ Result.setDeductionGuideName(TemplateName, IdLoc);
+ } else {
+ // We have parsed an identifier.
+ Result.setIdentifier(Id, IdLoc);
+ }
+
+ // If the next token is a '<', we may have a template.
+ TemplateTy Template;
+ if (Tok.is(tok::less))
+ return ParseUnqualifiedIdTemplateId(
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
+ EnteringContext, Result, TemplateSpecified);
+ else if (TemplateSpecified &&
+ Actions.ActOnTemplateName(
+ getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
+ EnteringContext, Template,
+ /*AllowInjectedClassName*/ true) == TNK_Non_template)
+ return true;
+
+ return false;
+ }
+
+ // unqualified-id:
+ // template-id (already parsed and annotated)
+ if (Tok.is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+
+ // FIXME: Consider passing invalid template-ids on to callers; they may
+ // be able to recover better than we can.
+ if (TemplateId->isInvalid()) {
+ ConsumeAnnotationToken();
+ return true;
+ }
+
+ // If the template-name names the current class, then this is a constructor
+ if (AllowConstructorName && TemplateId->Name &&
+ Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS)) {
+ if (SS.isSet()) {
+ // C++ [class.qual]p2 specifies that a qualified template-name
+ // is taken as the constructor name where a constructor can be
+ // declared. Thus, the template arguments are extraneous, so
+ // complain about them and remove them entirely.
+ Diag(TemplateId->TemplateNameLoc,
+ diag::err_out_of_line_constructor_template_id)
+ << TemplateId->Name
+ << FixItHint::CreateRemoval(
+ SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc));
+ ParsedType Ty = Actions.getConstructorName(
+ *TemplateId->Name, TemplateId->TemplateNameLoc, getCurScope(), SS,
+ EnteringContext);
+ if (!Ty)
+ return true;
+ Result.setConstructorName(Ty, TemplateId->TemplateNameLoc,
+ TemplateId->RAngleLoc);
+ ConsumeAnnotationToken();
+ return false;
+ }
+
+ Result.setConstructorTemplateId(TemplateId);
+ ConsumeAnnotationToken();
+ return false;
+ }
+
+ // We have already parsed a template-id; consume the annotation token as
+ // our unqualified-id.
+ Result.setTemplateId(TemplateId);
+ SourceLocation TemplateLoc = TemplateId->TemplateKWLoc;
+ if (TemplateLoc.isValid()) {
+ if (TemplateKWLoc && (ObjectType || SS.isSet()))
+ *TemplateKWLoc = TemplateLoc;
+ else
+ Diag(TemplateLoc, diag::err_unexpected_template_in_unqualified_id)
+ << FixItHint::CreateRemoval(TemplateLoc);
+ }
+ ConsumeAnnotationToken();
+ return false;
+ }
+
+ // unqualified-id:
+ // operator-function-id
+ // conversion-function-id
+ if (Tok.is(tok::kw_operator)) {
+ if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType, Result))
+ return true;
+
+ // If we have an operator-function-id or a literal-operator-id and the next
+ // token is a '<', we may have a
+ //
+ // template-id:
+ // operator-function-id < template-argument-list[opt] >
+ TemplateTy Template;
+ if ((Result.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId ||
+ Result.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) &&
+ Tok.is(tok::less))
+ return ParseUnqualifiedIdTemplateId(
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
+ SourceLocation(), EnteringContext, Result, TemplateSpecified);
+ else if (TemplateSpecified &&
+ Actions.ActOnTemplateName(
+ getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
+ EnteringContext, Template,
+ /*AllowInjectedClassName*/ true) == TNK_Non_template)
+ return true;
+
+ return false;
+ }
+
+ if (getLangOpts().CPlusPlus &&
+ (AllowDestructorName || SS.isSet()) && Tok.is(tok::tilde)) {
+ // C++ [expr.unary.op]p10:
+ // There is an ambiguity in the unary-expression ~X(), where X is a
+ // class-name. The ambiguity is resolved in favor of treating ~ as a
+ // unary complement rather than treating ~X as referring to a destructor.
+
+ // Parse the '~'.
+ SourceLocation TildeLoc = ConsumeToken();
+
+ if (TemplateSpecified) {
+ // C++ [temp.names]p3:
+ // A name prefixed by the keyword template shall be a template-id [...]
+ //
+ // A template-id cannot begin with a '~' token. This would never work
+ // anyway: x.~A<int>() would specify that the destructor is a template,
+ // not that 'A' is a template.
+ //
+ // FIXME: Suggest replacing the attempted destructor name with a correct
+ // destructor name and recover. (This is not trivial if this would become
+ // a pseudo-destructor name).
+ Diag(*TemplateKWLoc, diag::err_unexpected_template_in_destructor_name)
+ << Tok.getLocation();
+ return true;
+ }
+
+ if (SS.isEmpty() && Tok.is(tok::kw_decltype)) {
+ DeclSpec DS(AttrFactory);
+ SourceLocation EndLoc = ParseDecltypeSpecifier(DS);
+ if (ParsedType Type =
+ Actions.getDestructorTypeForDecltype(DS, ObjectType)) {
+ Result.setDestructorName(TildeLoc, Type, EndLoc);
+ return false;
+ }
+ return true;
+ }
+
+ // Parse the class-name.
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_destructor_tilde_identifier);
+ return true;
+ }
+
+ // If the user wrote ~T::T, correct it to T::~T.
+ DeclaratorScopeObj DeclScopeObj(*this, SS);
+ if (NextToken().is(tok::coloncolon)) {
+ // Don't let ParseOptionalCXXScopeSpecifier() "correct"
+ // `int A; struct { ~A::A(); };` to `int A; struct { ~A:A(); };`,
+ // it will confuse this recovery logic.
+ ColonProtectionRAIIObject ColonRAII(*this, false);
+
+ if (SS.isSet()) {
+ AnnotateScopeToken(SS, /*NewAnnotation*/true);
+ SS.clear();
+ }
+ if (ParseOptionalCXXScopeSpecifier(SS, ObjectType, ObjectHadErrors,
+ EnteringContext))
+ return true;
+ if (SS.isNotEmpty())
+ ObjectType = nullptr;
+ if (Tok.isNot(tok::identifier) || NextToken().is(tok::coloncolon) ||
+ !SS.isSet()) {
+ Diag(TildeLoc, diag::err_destructor_tilde_scope);
+ return true;
+ }
+
+ // Recover as if the tilde had been written before the identifier.
+ Diag(TildeLoc, diag::err_destructor_tilde_scope)
+ << FixItHint::CreateRemoval(TildeLoc)
+ << FixItHint::CreateInsertion(Tok.getLocation(), "~");
+
+ // Temporarily enter the scope for the rest of this function.
+ if (Actions.ShouldEnterDeclaratorScope(getCurScope(), SS))
+ DeclScopeObj.EnterDeclaratorScope();
+ }
+
+ // Parse the class-name (or template-name in a simple-template-id).
+ IdentifierInfo *ClassName = Tok.getIdentifierInfo();
+ SourceLocation ClassNameLoc = ConsumeToken();
+
+ if (Tok.is(tok::less)) {
+ Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc);
+ return ParseUnqualifiedIdTemplateId(
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
+ ClassNameLoc, EnteringContext, Result, TemplateSpecified);
+ }
+
+ // Note that this is a destructor name.
+ ParsedType Ty = Actions.getDestructorName(TildeLoc, *ClassName,
+ ClassNameLoc, getCurScope(),
+ SS, ObjectType,
+ EnteringContext);
+ if (!Ty)
+ return true;
+
+ Result.setDestructorName(TildeLoc, Ty, ClassNameLoc);
+ return false;
+ }
+
+ switch (Tok.getKind()) {
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
+ if (!NextToken().is(tok::l_paren)) {
+ Tok.setKind(tok::identifier);
+ Diag(Tok, diag::ext_keyword_as_ident)
+ << Tok.getIdentifierInfo()->getName() << 0;
+ goto ParseIdentifier;
+ }
+ [[fallthrough]];
+ default:
+ Diag(Tok, diag::err_expected_unqualified_id) << getLangOpts().CPlusPlus;
+ return true;
+ }
+}
+
+/// ParseCXXNewExpression - Parse a C++ new-expression. New is used to allocate
+/// memory in a typesafe manner and call constructors.
+///
+/// This method is called to parse the new expression after the optional :: has
+/// been already parsed. If the :: was present, "UseGlobal" is true and "Start"
+/// is its location. Otherwise, "Start" is the location of the 'new' token.
+///
+/// new-expression:
+/// '::'[opt] 'new' new-placement[opt] new-type-id
+/// new-initializer[opt]
+/// '::'[opt] 'new' new-placement[opt] '(' type-id ')'
+/// new-initializer[opt]
+///
+/// new-placement:
+/// '(' expression-list ')'
+///
+/// new-type-id:
+/// type-specifier-seq new-declarator[opt]
+/// [GNU] attributes type-specifier-seq new-declarator[opt]
+///
+/// new-declarator:
+/// ptr-operator new-declarator[opt]
+/// direct-new-declarator
+///
+/// new-initializer:
+/// '(' expression-list[opt] ')'
+/// [C++0x] braced-init-list
+///
+ExprResult
+Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
+ assert(Tok.is(tok::kw_new) && "expected 'new' token");
+ ConsumeToken(); // Consume 'new'
+
+ // A '(' now can be a new-placement or the '(' wrapping the type-id in the
+ // second form of new-expression. It can't be a new-type-id.
+
+ ExprVector PlacementArgs;
+ SourceLocation PlacementLParen, PlacementRParen;
+
+ SourceRange TypeIdParens;
+ DeclSpec DS(AttrFactory);
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::CXXNew);
+ if (Tok.is(tok::l_paren)) {
+ // If it turns out to be a placement, we change the type location.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ PlacementLParen = T.getOpenLocation();
+ if (ParseExpressionListOrTypeId(PlacementArgs, DeclaratorInfo)) {
+ SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
+ return ExprError();
+ }
+
+ T.consumeClose();
+ PlacementRParen = T.getCloseLocation();
+ if (PlacementRParen.isInvalid()) {
+ SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
+ return ExprError();
+ }
+
+ if (PlacementArgs.empty()) {
+ // Reset the placement locations. There was no placement.
+ TypeIdParens = T.getRange();
+ PlacementLParen = PlacementRParen = SourceLocation();
+ } else {
+ // We still need the type.
+ if (Tok.is(tok::l_paren)) {
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ MaybeParseGNUAttributes(DeclaratorInfo);
+ ParseSpecifierQualifierList(DS);
+ DeclaratorInfo.SetSourceRange(DS.getSourceRange());
+ ParseDeclarator(DeclaratorInfo);
+ T.consumeClose();
+ TypeIdParens = T.getRange();
+ } else {
+ MaybeParseGNUAttributes(DeclaratorInfo);
+ if (ParseCXXTypeSpecifierSeq(DS))
+ DeclaratorInfo.setInvalidType(true);
+ else {
+ DeclaratorInfo.SetSourceRange(DS.getSourceRange());
+ ParseDeclaratorInternal(DeclaratorInfo,
+ &Parser::ParseDirectNewDeclarator);
+ }
+ }
+ }
+ } else {
+ // A new-type-id is a simplified type-id, where essentially the
+ // direct-declarator is replaced by a direct-new-declarator.
+ MaybeParseGNUAttributes(DeclaratorInfo);
+ if (ParseCXXTypeSpecifierSeq(DS))
+ DeclaratorInfo.setInvalidType(true);
+ else {
+ DeclaratorInfo.SetSourceRange(DS.getSourceRange());
+ ParseDeclaratorInternal(DeclaratorInfo,
+ &Parser::ParseDirectNewDeclarator);
+ }
+ }
+ if (DeclaratorInfo.isInvalidType()) {
+ SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
+ return ExprError();
+ }
+
+ ExprResult Initializer;
+
+ if (Tok.is(tok::l_paren)) {
+ SourceLocation ConstructorLParen, ConstructorRParen;
+ ExprVector ConstructorArgs;
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ ConstructorLParen = T.getOpenLocation();
+ if (Tok.isNot(tok::r_paren)) {
+ auto RunSignatureHelp = [&]() {
+ ParsedType TypeRep =
+ Actions.ActOnTypeName(getCurScope(), DeclaratorInfo).get();
+ QualType PreferredType;
+ // ActOnTypeName might adjust DeclaratorInfo and return a null type even
+ // the passing DeclaratorInfo is valid, e.g. running SignatureHelp on
+ // `new decltype(invalid) (^)`.
+ if (TypeRep)
+ PreferredType = Actions.ProduceConstructorSignatureHelp(
+ TypeRep.get()->getCanonicalTypeInternal(),
+ DeclaratorInfo.getEndLoc(), ConstructorArgs, ConstructorLParen,
+ /*Braced=*/false);
+ CalledSignatureHelp = true;
+ return PreferredType;
+ };
+ if (ParseExpressionList(ConstructorArgs, [&] {
+ PreferredType.enterFunctionArgument(Tok.getLocation(),
+ RunSignatureHelp);
+ })) {
+ if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
+ RunSignatureHelp();
+ SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
+ return ExprError();
+ }
+ }
+ T.consumeClose();
+ ConstructorRParen = T.getCloseLocation();
+ if (ConstructorRParen.isInvalid()) {
+ SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
+ return ExprError();
+ }
+ Initializer = Actions.ActOnParenListExpr(ConstructorLParen,
+ ConstructorRParen,
+ ConstructorArgs);
+ } else if (Tok.is(tok::l_brace) && getLangOpts().CPlusPlus11) {
+ Diag(Tok.getLocation(),
+ diag::warn_cxx98_compat_generalized_initializer_lists);
+ Initializer = ParseBraceInitializer();
+ }
+ if (Initializer.isInvalid())
+ return Initializer;
+
+ return Actions.ActOnCXXNew(Start, UseGlobal, PlacementLParen,
+ PlacementArgs, PlacementRParen,
+ TypeIdParens, DeclaratorInfo, Initializer.get());
+}
+
+/// ParseDirectNewDeclarator - Parses a direct-new-declarator. Intended to be
+/// passed to ParseDeclaratorInternal.
+///
+/// direct-new-declarator:
+/// '[' expression[opt] ']'
+/// direct-new-declarator '[' constant-expression ']'
+///
+void Parser::ParseDirectNewDeclarator(Declarator &D) {
+ // Parse the array dimensions.
+ bool First = true;
+ while (Tok.is(tok::l_square)) {
+ // An array-size expression can't start with a lambda.
+ if (CheckProhibitedCXX11Attribute())
+ continue;
+
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+
+ ExprResult Size =
+ First ? (Tok.is(tok::r_square) ? ExprResult() : ParseExpression())
+ : ParseConstantExpression();
+ if (Size.isInvalid()) {
+ // Recover
+ SkipUntil(tok::r_square, StopAtSemi);
+ return;
+ }
+ First = false;
+
+ T.consumeClose();
+
+ // Attributes here appertain to the array type. C++11 [expr.new]p5.
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+
+ D.AddTypeInfo(DeclaratorChunk::getArray(0,
+ /*isStatic=*/false, /*isStar=*/false,
+ Size.get(), T.getOpenLocation(),
+ T.getCloseLocation()),
+ std::move(Attrs), T.getCloseLocation());
+
+ if (T.getCloseLocation().isInvalid())
+ return;
+ }
+}
+
+/// ParseExpressionListOrTypeId - Parse either an expression-list or a type-id.
+/// This ambiguity appears in the syntax of the C++ new operator.
+///
+/// new-expression:
+/// '::'[opt] 'new' new-placement[opt] '(' type-id ')'
+/// new-initializer[opt]
+///
+/// new-placement:
+/// '(' expression-list ')'
+///
+bool Parser::ParseExpressionListOrTypeId(
+ SmallVectorImpl<Expr*> &PlacementArgs,
+ Declarator &D) {
+ // The '(' was already consumed.
+ if (isTypeIdInParens()) {
+ ParseSpecifierQualifierList(D.getMutableDeclSpec());
+ D.SetSourceRange(D.getDeclSpec().getSourceRange());
+ ParseDeclarator(D);
+ return D.isInvalidType();
+ }
+
+ // It's not a type, it has to be an expression list.
+ return ParseExpressionList(PlacementArgs);
+}
+
+/// ParseCXXDeleteExpression - Parse a C++ delete-expression. Delete is used
+/// to free memory allocated by new.
+///
+/// This method is called to parse the 'delete' expression after the optional
+/// '::' has been already parsed. If the '::' was present, "UseGlobal" is true
+/// and "Start" is its location. Otherwise, "Start" is the location of the
+/// 'delete' token.
+///
+/// delete-expression:
+/// '::'[opt] 'delete' cast-expression
+/// '::'[opt] 'delete' '[' ']' cast-expression
+ExprResult
+Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) {
+ assert(Tok.is(tok::kw_delete) && "Expected 'delete' keyword");
+ ConsumeToken(); // Consume 'delete'
+
+ // Array delete?
+ bool ArrayDelete = false;
+ if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) {
+ // C++11 [expr.delete]p1:
+ // Whenever the delete keyword is followed by empty square brackets, it
+ // shall be interpreted as [array delete].
+ // [Footnote: A lambda expression with a lambda-introducer that consists
+ // of empty square brackets can follow the delete keyword if
+ // the lambda expression is enclosed in parentheses.]
+
+ const Token Next = GetLookAheadToken(2);
+
+ // Basic lookahead to check if we have a lambda expression.
+ if (Next.isOneOf(tok::l_brace, tok::less) ||
+ (Next.is(tok::l_paren) &&
+ (GetLookAheadToken(3).is(tok::r_paren) ||
+ (GetLookAheadToken(3).is(tok::identifier) &&
+ GetLookAheadToken(4).is(tok::identifier))))) {
+ TentativeParsingAction TPA(*this);
+ SourceLocation LSquareLoc = Tok.getLocation();
+ SourceLocation RSquareLoc = NextToken().getLocation();
+
+ // SkipUntil can't skip pairs of </*...*/>; don't emit a FixIt in this
+ // case.
+ SkipUntil({tok::l_brace, tok::less}, StopBeforeMatch);
+ SourceLocation RBraceLoc;
+ bool EmitFixIt = false;
+ if (Tok.is(tok::l_brace)) {
+ ConsumeBrace();
+ SkipUntil(tok::r_brace, StopBeforeMatch);
+ RBraceLoc = Tok.getLocation();
+ EmitFixIt = true;
+ }
+
+ TPA.Revert();
+
+ if (EmitFixIt)
+ Diag(Start, diag::err_lambda_after_delete)
+ << SourceRange(Start, RSquareLoc)
+ << FixItHint::CreateInsertion(LSquareLoc, "(")
+ << FixItHint::CreateInsertion(
+ Lexer::getLocForEndOfToken(
+ RBraceLoc, 0, Actions.getSourceManager(), getLangOpts()),
+ ")");
+ else
+ Diag(Start, diag::err_lambda_after_delete)
+ << SourceRange(Start, RSquareLoc);
+
+ // Warn that the non-capturing lambda isn't surrounded by parentheses
+ // to disambiguate it from 'delete[]'.
+ ExprResult Lambda = ParseLambdaExpression();
+ if (Lambda.isInvalid())
+ return ExprError();
+
+ // Evaluate any postfix expressions used on the lambda.
+ Lambda = ParsePostfixExpressionSuffix(Lambda);
+ if (Lambda.isInvalid())
+ return ExprError();
+ return Actions.ActOnCXXDelete(Start, UseGlobal, /*ArrayForm=*/false,
+ Lambda.get());
+ }
+
+ ArrayDelete = true;
+ BalancedDelimiterTracker T(*this, tok::l_square);
+
+ T.consumeOpen();
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid())
+ return ExprError();
+ }
+
+ ExprResult Operand(ParseCastExpression(AnyCastExpr));
+ if (Operand.isInvalid())
+ return Operand;
+
+ return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get());
+}
+
+/// ParseRequiresExpression - Parse a C++2a requires-expression.
+/// C++2a [expr.prim.req]p1
+/// A requires-expression provides a concise way to express requirements on
+/// template arguments. A requirement is one that can be checked by name
+/// lookup (6.4) or by checking properties of types and expressions.
+///
+/// requires-expression:
+/// 'requires' requirement-parameter-list[opt] requirement-body
+///
+/// requirement-parameter-list:
+/// '(' parameter-declaration-clause[opt] ')'
+///
+/// requirement-body:
+/// '{' requirement-seq '}'
+///
+/// requirement-seq:
+/// requirement
+/// requirement-seq requirement
+///
+/// requirement:
+/// simple-requirement
+/// type-requirement
+/// compound-requirement
+/// nested-requirement
+ExprResult Parser::ParseRequiresExpression() {
+ assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword");
+ SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires'
+
+ llvm::SmallVector<ParmVarDecl *, 2> LocalParameterDecls;
+ if (Tok.is(tok::l_paren)) {
+ // requirement parameter list is present.
+ ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope |
+ Scope::DeclScope);
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ Parens.consumeOpen();
+ if (!Tok.is(tok::r_paren)) {
+ ParsedAttributes FirstArgAttrs(getAttrFactory());
+ SourceLocation EllipsisLoc;
+ llvm::SmallVector<DeclaratorChunk::ParamInfo, 2> LocalParameters;
+ ParseParameterDeclarationClause(DeclaratorContext::RequiresExpr,
+ FirstArgAttrs, LocalParameters,
+ EllipsisLoc);
+ if (EllipsisLoc.isValid())
+ Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis);
+ for (auto &ParamInfo : LocalParameters)
+ LocalParameterDecls.push_back(cast<ParmVarDecl>(ParamInfo.Param));
+ }
+ Parens.consumeClose();
+ }
+
+ BalancedDelimiterTracker Braces(*this, tok::l_brace);
+ if (Braces.expectAndConsume())
+ return ExprError();
+
+ // Start of requirement list
+ llvm::SmallVector<concepts::Requirement *, 2> Requirements;
+
+ // C++2a [expr.prim.req]p2
+ // Expressions appearing within a requirement-body are unevaluated operands.
+ EnterExpressionEvaluationContext Ctx(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+
+ ParseScope BodyScope(this, Scope::DeclScope);
+ // Create a separate diagnostic pool for RequiresExprBodyDecl.
+ // Dependent diagnostics are attached to this Decl and non-depenedent
+ // diagnostics are surfaced after this parse.
+ ParsingDeclRAIIObject ParsingBodyDecl(*this, ParsingDeclRAIIObject::NoParent);
+ RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr(
+ RequiresKWLoc, LocalParameterDecls, getCurScope());
+
+ if (Tok.is(tok::r_brace)) {
+ // Grammar does not allow an empty body.
+ // requirement-body:
+ // { requirement-seq }
+ // requirement-seq:
+ // requirement
+ // requirement-seq requirement
+ Diag(Tok, diag::err_empty_requires_expr);
+ // Continue anyway and produce a requires expr with no requirements.
+ } else {
+ while (!Tok.is(tok::r_brace)) {
+ switch (Tok.getKind()) {
+ case tok::l_brace: {
+ // Compound requirement
+ // C++ [expr.prim.req.compound]
+ // compound-requirement:
+ // '{' expression '}' 'noexcept'[opt]
+ // return-type-requirement[opt] ';'
+ // return-type-requirement:
+ // trailing-return-type
+ // '->' cv-qualifier-seq[opt] constrained-parameter
+ // cv-qualifier-seq[opt] abstract-declarator[opt]
+ BalancedDelimiterTracker ExprBraces(*this, tok::l_brace);
+ ExprBraces.consumeOpen();
+ ExprResult Expression =
+ Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ if (!Expression.isUsable()) {
+ ExprBraces.skipToEnd();
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ if (ExprBraces.consumeClose())
+ ExprBraces.skipToEnd();
+
+ concepts::Requirement *Req = nullptr;
+ SourceLocation NoexceptLoc;
+ TryConsumeToken(tok::kw_noexcept, NoexceptLoc);
+ if (Tok.is(tok::semi)) {
+ Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc);
+ if (Req)
+ Requirements.push_back(Req);
+ break;
+ }
+ if (!TryConsumeToken(tok::arrow))
+ // User probably forgot the arrow, remind them and try to continue.
+ Diag(Tok, diag::err_requires_expr_missing_arrow)
+ << FixItHint::CreateInsertion(Tok.getLocation(), "->");
+ // Try to parse a 'type-constraint'
+ if (TryAnnotateTypeConstraint()) {
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ if (!isTypeConstraintAnnotation()) {
+ Diag(Tok, diag::err_requires_expr_expected_type_constraint);
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ CXXScopeSpec SS;
+ if (Tok.is(tok::annot_cxxscope)) {
+ Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
+ Tok.getAnnotationRange(),
+ SS);
+ ConsumeAnnotationToken();
+ }
+
+ Req = Actions.ActOnCompoundRequirement(
+ Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok),
+ TemplateParameterDepth);
+ ConsumeAnnotationToken();
+ if (Req)
+ Requirements.push_back(Req);
+ break;
+ }
+ default: {
+ bool PossibleRequiresExprInSimpleRequirement = false;
+ if (Tok.is(tok::kw_requires)) {
+ auto IsNestedRequirement = [&] {
+ RevertingTentativeParsingAction TPA(*this);
+ ConsumeToken(); // 'requires'
+ if (Tok.is(tok::l_brace))
+ // This is a requires expression
+ // requires (T t) {
+ // requires { t++; };
+ // ... ^
+ // }
+ return false;
+ if (Tok.is(tok::l_paren)) {
+ // This might be the parameter list of a requires expression
+ ConsumeParen();
+ auto Res = TryParseParameterDeclarationClause();
+ if (Res != TPResult::False) {
+ // Skip to the closing parenthesis
+ // FIXME: Don't traverse these tokens twice (here and in
+ // TryParseParameterDeclarationClause).
+ unsigned Depth = 1;
+ while (Depth != 0) {
+ if (Tok.is(tok::l_paren))
+ Depth++;
+ else if (Tok.is(tok::r_paren))
+ Depth--;
+ ConsumeAnyToken();
+ }
+ // requires (T t) {
+ // requires () ?
+ // ... ^
+ // - OR -
+ // requires (int x) ?
+ // ... ^
+ // }
+ if (Tok.is(tok::l_brace))
+ // requires (...) {
+ // ^ - a requires expression as a
+ // simple-requirement.
+ return false;
+ }
+ }
+ return true;
+ };
+ if (IsNestedRequirement()) {
+ ConsumeToken();
+ // Nested requirement
+ // C++ [expr.prim.req.nested]
+ // nested-requirement:
+ // 'requires' constraint-expression ';'
+ ExprResult ConstraintExpr =
+ Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression());
+ if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) {
+ SkipUntil(tok::semi, tok::r_brace,
+ SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ if (auto *Req =
+ Actions.ActOnNestedRequirement(ConstraintExpr.get()))
+ Requirements.push_back(Req);
+ else {
+ SkipUntil(tok::semi, tok::r_brace,
+ SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ break;
+ } else
+ PossibleRequiresExprInSimpleRequirement = true;
+ } else if (Tok.is(tok::kw_typename)) {
+ // This might be 'typename T::value_type;' (a type requirement) or
+ // 'typename T::value_type{};' (a simple requirement).
+ TentativeParsingAction TPA(*this);
+
+ // We need to consume the typename to allow 'requires { typename a; }'
+ SourceLocation TypenameKWLoc = ConsumeToken();
+ if (TryAnnotateOptionalCXXScopeToken()) {
+ TPA.Commit();
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ CXXScopeSpec SS;
+ if (Tok.is(tok::annot_cxxscope)) {
+ Actions.RestoreNestedNameSpecifierAnnotation(
+ Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS);
+ ConsumeAnnotationToken();
+ }
+
+ if (Tok.isOneOf(tok::identifier, tok::annot_template_id) &&
+ !NextToken().isOneOf(tok::l_brace, tok::l_paren)) {
+ TPA.Commit();
+ SourceLocation NameLoc = Tok.getLocation();
+ IdentifierInfo *II = nullptr;
+ TemplateIdAnnotation *TemplateId = nullptr;
+ if (Tok.is(tok::identifier)) {
+ II = Tok.getIdentifierInfo();
+ ConsumeToken();
+ } else {
+ TemplateId = takeTemplateIdAnnotation(Tok);
+ ConsumeAnnotationToken();
+ if (TemplateId->isInvalid())
+ break;
+ }
+
+ if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS,
+ NameLoc, II,
+ TemplateId)) {
+ Requirements.push_back(Req);
+ }
+ break;
+ }
+ TPA.Revert();
+ }
+ // Simple requirement
+ // C++ [expr.prim.req.simple]
+ // simple-requirement:
+ // expression ';'
+ SourceLocation StartLoc = Tok.getLocation();
+ ExprResult Expression =
+ Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ if (!Expression.isUsable()) {
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement)
+ Diag(StartLoc, diag::err_requires_expr_in_simple_requirement)
+ << FixItHint::CreateInsertion(StartLoc, "requires");
+ if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get()))
+ Requirements.push_back(Req);
+ else {
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ // User may have tried to put some compound requirement stuff here
+ if (Tok.is(tok::kw_noexcept)) {
+ Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept)
+ << FixItHint::CreateInsertion(StartLoc, "{")
+ << FixItHint::CreateInsertion(Tok.getLocation(), "}");
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ break;
+ }
+ }
+ if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) {
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ TryConsumeToken(tok::semi);
+ break;
+ }
+ }
+ if (Requirements.empty()) {
+ // Don't emit an empty requires expr here to avoid confusing the user with
+ // other diagnostics quoting an empty requires expression they never
+ // wrote.
+ Braces.consumeClose();
+ Actions.ActOnFinishRequiresExpr();
+ return ExprError();
+ }
+ }
+ Braces.consumeClose();
+ Actions.ActOnFinishRequiresExpr();
+ ParsingBodyDecl.complete(Body);
+ return Actions.ActOnRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls,
+ Requirements, Braces.getCloseLocation());
+}
+
+static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) {
+ switch (kind) {
+ default: llvm_unreachable("Not a known type trait");
+#define TYPE_TRAIT_1(Spelling, Name, Key) \
+case tok::kw_ ## Spelling: return UTT_ ## Name;
+#define TYPE_TRAIT_2(Spelling, Name, Key) \
+case tok::kw_ ## Spelling: return BTT_ ## Name;
+#include "clang/Basic/TokenKinds.def"
+#define TYPE_TRAIT_N(Spelling, Name, Key) \
+ case tok::kw_ ## Spelling: return TT_ ## Name;
+#include "clang/Basic/TokenKinds.def"
+ }
+}
+
+static ArrayTypeTrait ArrayTypeTraitFromTokKind(tok::TokenKind kind) {
+ switch (kind) {
+ default:
+ llvm_unreachable("Not a known array type trait");
+#define ARRAY_TYPE_TRAIT(Spelling, Name, Key) \
+ case tok::kw_##Spelling: \
+ return ATT_##Name;
+#include "clang/Basic/TokenKinds.def"
+ }
+}
+
+static ExpressionTrait ExpressionTraitFromTokKind(tok::TokenKind kind) {
+ switch (kind) {
+ default:
+ llvm_unreachable("Not a known unary expression trait.");
+#define EXPRESSION_TRAIT(Spelling, Name, Key) \
+ case tok::kw_##Spelling: \
+ return ET_##Name;
+#include "clang/Basic/TokenKinds.def"
+ }
+}
+
+/// Parse the built-in type-trait pseudo-functions that allow
+/// implementation of the TR1/C++11 type traits templates.
+///
+/// primary-expression:
+/// unary-type-trait '(' type-id ')'
+/// binary-type-trait '(' type-id ',' type-id ')'
+/// type-trait '(' type-id-seq ')'
+///
+/// type-id-seq:
+/// type-id ...[opt] type-id-seq[opt]
+///
+ExprResult Parser::ParseTypeTrait() {
+ tok::TokenKind Kind = Tok.getKind();
+
+ SourceLocation Loc = ConsumeToken();
+
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ if (Parens.expectAndConsume())
+ return ExprError();
+
+ SmallVector<ParsedType, 2> Args;
+ do {
+ // Parse the next type.
+ TypeResult Ty = ParseTypeName();
+ if (Ty.isInvalid()) {
+ Parens.skipToEnd();
+ return ExprError();
+ }
+
+ // Parse the ellipsis, if present.
+ if (Tok.is(tok::ellipsis)) {
+ Ty = Actions.ActOnPackExpansion(Ty.get(), ConsumeToken());
+ if (Ty.isInvalid()) {
+ Parens.skipToEnd();
+ return ExprError();
+ }
+ }
+
+ // Add this type to the list of arguments.
+ Args.push_back(Ty.get());
+ } while (TryConsumeToken(tok::comma));
+
+ if (Parens.consumeClose())
+ return ExprError();
+
+ SourceLocation EndLoc = Parens.getCloseLocation();
+
+ return Actions.ActOnTypeTrait(TypeTraitFromTokKind(Kind), Loc, Args, EndLoc);
+}
+
+/// ParseArrayTypeTrait - Parse the built-in array type-trait
+/// pseudo-functions.
+///
+/// primary-expression:
+/// [Embarcadero] '__array_rank' '(' type-id ')'
+/// [Embarcadero] '__array_extent' '(' type-id ',' expression ')'
+///
+ExprResult Parser::ParseArrayTypeTrait() {
+ ArrayTypeTrait ATT = ArrayTypeTraitFromTokKind(Tok.getKind());
+ SourceLocation Loc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume())
+ return ExprError();
+
+ TypeResult Ty = ParseTypeName();
+ if (Ty.isInvalid()) {
+ SkipUntil(tok::comma, StopAtSemi);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ switch (ATT) {
+ case ATT_ArrayRank: {
+ T.consumeClose();
+ return Actions.ActOnArrayTypeTrait(ATT, Loc, Ty.get(), nullptr,
+ T.getCloseLocation());
+ }
+ case ATT_ArrayExtent: {
+ if (ExpectAndConsume(tok::comma)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ ExprResult DimExpr = ParseExpression();
+ T.consumeClose();
+
+ return Actions.ActOnArrayTypeTrait(ATT, Loc, Ty.get(), DimExpr.get(),
+ T.getCloseLocation());
+ }
+ }
+ llvm_unreachable("Invalid ArrayTypeTrait!");
+}
+
+/// ParseExpressionTrait - Parse built-in expression-trait
+/// pseudo-functions like __is_lvalue_expr( xxx ).
+///
+/// primary-expression:
+/// [Embarcadero] expression-trait '(' expression ')'
+///
+ExprResult Parser::ParseExpressionTrait() {
+ ExpressionTrait ET = ExpressionTraitFromTokKind(Tok.getKind());
+ SourceLocation Loc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume())
+ return ExprError();
+
+ ExprResult Expr = ParseExpression();
+
+ T.consumeClose();
+
+ return Actions.ActOnExpressionTrait(ET, Loc, Expr.get(),
+ T.getCloseLocation());
+}
+
+
+/// ParseCXXAmbiguousParenExpression - We have parsed the left paren of a
+/// parenthesized ambiguous type-id. This uses tentative parsing to disambiguate
+/// based on the context past the parens.
+ExprResult
+Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType,
+ ParsedType &CastTy,
+ BalancedDelimiterTracker &Tracker,
+ ColonProtectionRAIIObject &ColonProt) {
+ assert(getLangOpts().CPlusPlus && "Should only be called for C++!");
+ assert(ExprType == CastExpr && "Compound literals are not ambiguous!");
+ assert(isTypeIdInParens() && "Not a type-id!");
+
+ ExprResult Result(true);
+ CastTy = nullptr;
+
+ // We need to disambiguate a very ugly part of the C++ syntax:
+ //
+ // (T())x; - type-id
+ // (T())*x; - type-id
+ // (T())/x; - expression
+ // (T()); - expression
+ //
+ // The bad news is that we cannot use the specialized tentative parser, since
+ // it can only verify that the thing inside the parens can be parsed as
+ // type-id, it is not useful for determining the context past the parens.
+ //
+ // The good news is that the parser can disambiguate this part without
+ // making any unnecessary Action calls.
+ //
+ // It uses a scheme similar to parsing inline methods. The parenthesized
+ // tokens are cached, the context that follows is determined (possibly by
+ // parsing a cast-expression), and then we re-introduce the cached tokens
+ // into the token stream and parse them appropriately.
+
+ ParenParseOption ParseAs;
+ CachedTokens Toks;
+
+ // Store the tokens of the parentheses. We will parse them after we determine
+ // the context that follows them.
+ if (!ConsumeAndStoreUntil(tok::r_paren, Toks)) {
+ // We didn't find the ')' we expected.
+ Tracker.consumeClose();
+ return ExprError();
+ }
+
+ if (Tok.is(tok::l_brace)) {
+ ParseAs = CompoundLiteral;
+ } else {
+ bool NotCastExpr;
+ if (Tok.is(tok::l_paren) && NextToken().is(tok::r_paren)) {
+ NotCastExpr = true;
+ } else {
+ // Try parsing the cast-expression that may follow.
+ // If it is not a cast-expression, NotCastExpr will be true and no token
+ // will be consumed.
+ ColonProt.restore();
+ Result = ParseCastExpression(AnyCastExpr,
+ false/*isAddressofOperand*/,
+ NotCastExpr,
+ // type-id has priority.
+ IsTypeCast);
+ }
+
+ // If we parsed a cast-expression, it's really a type-id, otherwise it's
+ // an expression.
+ ParseAs = NotCastExpr ? SimpleExpr : CastExpr;
+ }
+
+ // Create a fake EOF to mark end of Toks buffer.
+ Token AttrEnd;
+ AttrEnd.startToken();
+ AttrEnd.setKind(tok::eof);
+ AttrEnd.setLocation(Tok.getLocation());
+ AttrEnd.setEofData(Toks.data());
+ Toks.push_back(AttrEnd);
+
+ // The current token should go after the cached tokens.
+ Toks.push_back(Tok);
+ // Re-enter the stored parenthesized tokens into the token stream, so we may
+ // parse them now.
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion*/ true,
+ /*IsReinject*/ true);
+ // Drop the current token and bring the first cached one. It's the same token
+ // as when we entered this function.
+ ConsumeAnyToken();
+
+ if (ParseAs >= CompoundLiteral) {
+ // Parse the type declarator.
+ DeclSpec DS(AttrFactory);
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ {
+ ColonProtectionRAIIObject InnerColonProtection(*this);
+ ParseSpecifierQualifierList(DS);
+ ParseDeclarator(DeclaratorInfo);
+ }
+
+ // Match the ')'.
+ Tracker.consumeClose();
+ ColonProt.restore();
+
+ // Consume EOF marker for Toks buffer.
+ assert(Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData());
+ ConsumeAnyToken();
+
+ if (ParseAs == CompoundLiteral) {
+ ExprType = CompoundLiteral;
+ if (DeclaratorInfo.isInvalidType())
+ return ExprError();
+
+ TypeResult Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ return ParseCompoundLiteralExpression(Ty.get(),
+ Tracker.getOpenLocation(),
+ Tracker.getCloseLocation());
+ }
+
+ // We parsed '(' type-id ')' and the thing after it wasn't a '{'.
+ assert(ParseAs == CastExpr);
+
+ if (DeclaratorInfo.isInvalidType())
+ return ExprError();
+
+ // Result is what ParseCastExpression returned earlier.
+ if (!Result.isInvalid())
+ Result = Actions.ActOnCastExpr(getCurScope(), Tracker.getOpenLocation(),
+ DeclaratorInfo, CastTy,
+ Tracker.getCloseLocation(), Result.get());
+ return Result;
+ }
+
+ // Not a compound literal, and not followed by a cast-expression.
+ assert(ParseAs == SimpleExpr);
+
+ ExprType = SimpleExpr;
+ Result = ParseExpression();
+ if (!Result.isInvalid() && Tok.is(tok::r_paren))
+ Result = Actions.ActOnParenExpr(Tracker.getOpenLocation(),
+ Tok.getLocation(), Result.get());
+
+ // Match the ')'.
+ if (Result.isInvalid()) {
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+ assert(Tok.getEofData() == AttrEnd.getEofData());
+ ConsumeAnyToken();
+ return ExprError();
+ }
+
+ Tracker.consumeClose();
+ // Consume EOF marker for Toks buffer.
+ assert(Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData());
+ ConsumeAnyToken();
+ return Result;
+}
+
+/// Parse a __builtin_bit_cast(T, E).
+ExprResult Parser::ParseBuiltinBitCast() {
+ SourceLocation KWLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "__builtin_bit_cast"))
+ return ExprError();
+
+ // Parse the common declaration-specifiers piece.
+ DeclSpec DS(AttrFactory);
+ ParseSpecifierQualifierList(DS);
+
+ // Parse the abstract-declarator, if present.
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ ParseDeclarator(DeclaratorInfo);
+
+ if (ExpectAndConsume(tok::comma)) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::comma;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ ExprResult Operand = ParseExpression();
+
+ if (T.consumeClose())
+ return ExprError();
+
+ if (Operand.isInvalid() || DeclaratorInfo.isInvalidType())
+ return ExprError();
+
+ return Actions.ActOnBuiltinBitCastExpr(KWLoc, DeclaratorInfo, Operand,
+ T.getCloseLocation());
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseHLSL.cpp b/contrib/libs/clang16/lib/Parse/ParseHLSL.cpp
new file mode 100644
index 0000000000..ebda84de6a
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseHLSL.cpp
@@ -0,0 +1,200 @@
+//===--- ParseHLSL.cpp - HLSL-specific parsing support --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the parsing logic for HLSL language features.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Attr.h"
+#include "clang/Basic/AttributeCommonInfo.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+
+using namespace clang;
+
+static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG,
+ SourceLocation BufferLoc,
+ bool IsCBuffer, Parser &P) {
+ // The parse is failed, just return false.
+ if (!DG)
+ return false;
+ DeclGroupRef Decls = DG.get();
+ bool IsValid = true;
+ // Only allow function, variable, record decls inside HLSLBuffer.
+ for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
+ Decl *D = *I;
+ if (isa<CXXRecordDecl, RecordDecl, FunctionDecl, VarDecl>(D))
+ continue;
+
+ // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
+ if (isa<HLSLBufferDecl, NamespaceDecl>(D)) {
+ P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
+ << IsCBuffer;
+ IsValid = false;
+ continue;
+ }
+
+ IsValid = false;
+ P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
+ << IsCBuffer;
+ }
+ return IsValid;
+}
+
+Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
+ assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&
+ "Not a cbuffer or tbuffer!");
+ bool IsCBuffer = Tok.is(tok::kw_cbuffer);
+ SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
+
+ if (!Tok.is(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ return nullptr;
+ }
+
+ IdentifierInfo *Identifier = Tok.getIdentifierInfo();
+ SourceLocation IdentifierLoc = ConsumeToken();
+
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseHLSLSemantics(Attrs, nullptr);
+
+ ParseScope BufferScope(this, Scope::DeclScope);
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return nullptr;
+ }
+
+ Decl *D = Actions.ActOnStartHLSLBuffer(getCurScope(), IsCBuffer, BufferLoc,
+ Identifier, IdentifierLoc,
+ T.getOpenLocation());
+
+ while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
+ // FIXME: support attribute on constants inside cbuffer/tbuffer.
+ ParsedAttributes DeclAttrs(AttrFactory);
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+
+ DeclGroupPtrTy Result =
+ ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
+ if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
+ *this)) {
+ T.skipToEnd();
+ DeclEnd = T.getCloseLocation();
+ BufferScope.Exit();
+ Actions.ActOnFinishHLSLBuffer(D, DeclEnd);
+ return nullptr;
+ }
+ }
+
+ T.consumeClose();
+ DeclEnd = T.getCloseLocation();
+ BufferScope.Exit();
+ Actions.ActOnFinishHLSLBuffer(D, DeclEnd);
+
+ Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
+ return D;
+}
+
+static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
+ Token Tok, ArgsVector &ArgExprs,
+ Parser &P, ASTContext &Ctx,
+ Preprocessor &PP) {
+ StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength());
+ SourceLocation EndNumLoc = Tok.getEndLoc();
+
+ P.ConsumeToken(); // consume constant.
+ std::string FixedArg = ArgStr.str() + Num.str();
+ P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number)
+ << FixedArg
+ << FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);
+ ArgsUnion &Slot = ArgExprs.back();
+ Slot = IdentifierLoc::create(Ctx, ArgLoc, PP.getIdentifierInfo(FixedArg));
+}
+
+void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs,
+ SourceLocation *EndLoc) {
+ // FIXME: HLSLSemantic is shared for Semantic and resource binding which is
+ // confusing. Need a better name to avoid misunderstanding. Issue
+ // https://github.com/llvm/llvm-project/issues/57882
+ assert(Tok.is(tok::colon) && "Not a HLSL Semantic");
+ ConsumeToken();
+
+ IdentifierInfo *II = nullptr;
+ if (Tok.is(tok::kw_register))
+ II = PP.getIdentifierInfo("register");
+ else if (Tok.is(tok::identifier))
+ II = Tok.getIdentifierInfo();
+
+ if (!II) {
+ Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
+ return;
+ }
+
+ SourceLocation Loc = ConsumeToken();
+ if (EndLoc)
+ *EndLoc = Tok.getLocation();
+ ParsedAttr::Kind AttrKind =
+ ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLSemantic);
+
+ ArgsVector ArgExprs;
+ switch (AttrKind) {
+ case ParsedAttr::AT_HLSLResourceBinding: {
+ if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
+ SkipUntil(tok::r_paren, StopAtSemi); // skip through )
+ return;
+ }
+ if (!Tok.is(tok::identifier)) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_paren, StopAtSemi); // skip through )
+ return;
+ }
+ StringRef SlotStr = Tok.getIdentifierInfo()->getName();
+ SourceLocation SlotLoc = Tok.getLocation();
+ ArgExprs.push_back(ParseIdentifierLoc());
+
+ // Add numeric_constant for fix-it.
+ if (SlotStr.size() == 1 && Tok.is(tok::numeric_constant))
+ fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
+ Actions.Context, PP);
+
+ if (Tok.is(tok::comma)) {
+ ConsumeToken(); // consume comma
+ if (!Tok.is(tok::identifier)) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_paren, StopAtSemi); // skip through )
+ return;
+ }
+ StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
+ SourceLocation SpaceLoc = Tok.getLocation();
+ ArgExprs.push_back(ParseIdentifierLoc());
+
+ // Add numeric_constant for fix-it.
+ if (SpaceStr.equals("space") && Tok.is(tok::numeric_constant))
+ fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
+ Actions.Context, PP);
+ }
+ if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
+ SkipUntil(tok::r_paren, StopAtSemi); // skip through )
+ return;
+ }
+ } break;
+ case ParsedAttr::UnknownAttribute:
+ Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
+ return;
+ case ParsedAttr::AT_HLSLSV_GroupIndex:
+ case ParsedAttr::AT_HLSLSV_DispatchThreadID:
+ break;
+ default:
+ llvm_unreachable("invalid HLSL Semantic");
+ break;
+ }
+
+ Attrs.addNew(II, Loc, nullptr, SourceLocation(), ArgExprs.data(),
+ ArgExprs.size(), ParsedAttr::AS_HLSLSemantic);
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseInit.cpp b/contrib/libs/clang16/lib/Parse/ParseInit.cpp
new file mode 100644
index 0000000000..af0c3b4795
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseInit.cpp
@@ -0,0 +1,610 @@
+//===--- ParseInit.cpp - Initializer Parsing ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements initializer parsing as specified by C99 6.7.8.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/Designator.h"
+#include "clang/Sema/Ownership.h"
+#include "clang/Sema/Scope.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+using namespace clang;
+
+
+/// MayBeDesignationStart - Return true if the current token might be the start
+/// of a designator. If we can tell it is impossible that it is a designator,
+/// return false.
+bool Parser::MayBeDesignationStart() {
+ switch (Tok.getKind()) {
+ default:
+ return false;
+
+ case tok::period: // designator: '.' identifier
+ return true;
+
+ case tok::l_square: { // designator: array-designator
+ if (!PP.getLangOpts().CPlusPlus11)
+ return true;
+
+ // C++11 lambda expressions and C99 designators can be ambiguous all the
+ // way through the closing ']' and to the next character. Handle the easy
+ // cases here, and fall back to tentative parsing if those fail.
+ switch (PP.LookAhead(0).getKind()) {
+ case tok::equal:
+ case tok::ellipsis:
+ case tok::r_square:
+ // Definitely starts a lambda expression.
+ return false;
+
+ case tok::amp:
+ case tok::kw_this:
+ case tok::star:
+ case tok::identifier:
+ // We have to do additional analysis, because these could be the
+ // start of a constant expression or a lambda capture list.
+ break;
+
+ default:
+ // Anything not mentioned above cannot occur following a '[' in a
+ // lambda expression.
+ return true;
+ }
+
+ // Handle the complicated case below.
+ break;
+ }
+ case tok::identifier: // designation: identifier ':'
+ return PP.LookAhead(0).is(tok::colon);
+ }
+
+ // Parse up to (at most) the token after the closing ']' to determine
+ // whether this is a C99 designator or a lambda.
+ RevertingTentativeParsingAction Tentative(*this);
+
+ LambdaIntroducer Intro;
+ LambdaIntroducerTentativeParse ParseResult;
+ if (ParseLambdaIntroducer(Intro, &ParseResult)) {
+ // Hit and diagnosed an error in a lambda.
+ // FIXME: Tell the caller this happened so they can recover.
+ return true;
+ }
+
+ switch (ParseResult) {
+ case LambdaIntroducerTentativeParse::Success:
+ case LambdaIntroducerTentativeParse::Incomplete:
+ // Might be a lambda-expression. Keep looking.
+ // FIXME: If our tentative parse was not incomplete, parse the lambda from
+ // here rather than throwing away then reparsing the LambdaIntroducer.
+ break;
+
+ case LambdaIntroducerTentativeParse::MessageSend:
+ case LambdaIntroducerTentativeParse::Invalid:
+ // Can't be a lambda-expression. Treat it as a designator.
+ // FIXME: Should we disambiguate against a message-send?
+ return true;
+ }
+
+ // Once we hit the closing square bracket, we look at the next
+ // token. If it's an '=', this is a designator. Otherwise, it's a
+ // lambda expression. This decision favors lambdas over the older
+ // GNU designator syntax, which allows one to omit the '=', but is
+ // consistent with GCC.
+ return Tok.is(tok::equal);
+}
+
+static void CheckArrayDesignatorSyntax(Parser &P, SourceLocation Loc,
+ Designation &Desig) {
+ // If we have exactly one array designator, this used the GNU
+ // 'designation: array-designator' extension, otherwise there should be no
+ // designators at all!
+ if (Desig.getNumDesignators() == 1 &&
+ (Desig.getDesignator(0).isArrayDesignator() ||
+ Desig.getDesignator(0).isArrayRangeDesignator()))
+ P.Diag(Loc, diag::ext_gnu_missing_equal_designator);
+ else if (Desig.getNumDesignators() > 0)
+ P.Diag(Loc, diag::err_expected_equal_designator);
+}
+
+/// ParseInitializerWithPotentialDesignator - Parse the 'initializer' production
+/// checking to see if the token stream starts with a designator.
+///
+/// C99:
+///
+/// designation:
+/// designator-list '='
+/// [GNU] array-designator
+/// [GNU] identifier ':'
+///
+/// designator-list:
+/// designator
+/// designator-list designator
+///
+/// designator:
+/// array-designator
+/// '.' identifier
+///
+/// array-designator:
+/// '[' constant-expression ']'
+/// [GNU] '[' constant-expression '...' constant-expression ']'
+///
+/// C++20:
+///
+/// designated-initializer-list:
+/// designated-initializer-clause
+/// designated-initializer-list ',' designated-initializer-clause
+///
+/// designated-initializer-clause:
+/// designator brace-or-equal-initializer
+///
+/// designator:
+/// '.' identifier
+///
+/// We allow the C99 syntax extensions in C++20, but do not allow the C++20
+/// extension (a braced-init-list after the designator with no '=') in C99.
+///
+/// NOTE: [OBC] allows '[ objc-receiver objc-message-args ]' as an
+/// initializer (because it is an expression). We need to consider this case
+/// when parsing array designators.
+///
+/// \p CodeCompleteCB is called with Designation parsed so far.
+ExprResult Parser::ParseInitializerWithPotentialDesignator(
+ DesignatorCompletionInfo DesignatorCompletion) {
+ // If this is the old-style GNU extension:
+ // designation ::= identifier ':'
+ // Handle it as a field designator. Otherwise, this must be the start of a
+ // normal expression.
+ if (Tok.is(tok::identifier)) {
+ const IdentifierInfo *FieldName = Tok.getIdentifierInfo();
+
+ SmallString<256> NewSyntax;
+ llvm::raw_svector_ostream(NewSyntax) << '.' << FieldName->getName()
+ << " = ";
+
+ SourceLocation NameLoc = ConsumeToken(); // Eat the identifier.
+
+ assert(Tok.is(tok::colon) && "MayBeDesignationStart not working properly!");
+ SourceLocation ColonLoc = ConsumeToken();
+
+ Diag(NameLoc, diag::ext_gnu_old_style_field_designator)
+ << FixItHint::CreateReplacement(SourceRange(NameLoc, ColonLoc),
+ NewSyntax);
+
+ Designation D;
+ D.AddDesignator(Designator::getField(FieldName, SourceLocation(), NameLoc));
+ PreferredType.enterDesignatedInitializer(
+ Tok.getLocation(), DesignatorCompletion.PreferredBaseType, D);
+ return Actions.ActOnDesignatedInitializer(D, ColonLoc, true,
+ ParseInitializer());
+ }
+
+ // Desig - This is initialized when we see our first designator. We may have
+ // an objc message send with no designator, so we don't want to create this
+ // eagerly.
+ Designation Desig;
+
+ // Parse each designator in the designator list until we find an initializer.
+ while (Tok.is(tok::period) || Tok.is(tok::l_square)) {
+ if (Tok.is(tok::period)) {
+ // designator: '.' identifier
+ SourceLocation DotLoc = ConsumeToken();
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteDesignator(DesignatorCompletion.PreferredBaseType,
+ DesignatorCompletion.InitExprs, Desig);
+ return ExprError();
+ }
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok.getLocation(), diag::err_expected_field_designator);
+ return ExprError();
+ }
+
+ Desig.AddDesignator(Designator::getField(Tok.getIdentifierInfo(), DotLoc,
+ Tok.getLocation()));
+ ConsumeToken(); // Eat the identifier.
+ continue;
+ }
+
+ // We must have either an array designator now or an objc message send.
+ assert(Tok.is(tok::l_square) && "Unexpected token!");
+
+ // Handle the two forms of array designator:
+ // array-designator: '[' constant-expression ']'
+ // array-designator: '[' constant-expression '...' constant-expression ']'
+ //
+ // Also, we have to handle the case where the expression after the
+ // designator an an objc message send: '[' objc-message-expr ']'.
+ // Interesting cases are:
+ // [foo bar] -> objc message send
+ // [foo] -> array designator
+ // [foo ... bar] -> array designator
+ // [4][foo bar] -> obsolete GNU designation with objc message send.
+ //
+ // We do not need to check for an expression starting with [[ here. If it
+ // contains an Objective-C message send, then it is not an ill-formed
+ // attribute. If it is a lambda-expression within an array-designator, then
+ // it will be rejected because a constant-expression cannot begin with a
+ // lambda-expression.
+ InMessageExpressionRAIIObject InMessage(*this, true);
+
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+ SourceLocation StartLoc = T.getOpenLocation();
+
+ ExprResult Idx;
+
+ // If Objective-C is enabled and this is a typename (class message
+ // send) or send to 'super', parse this as a message send
+ // expression. We handle C++ and C separately, since C++ requires
+ // much more complicated parsing.
+ if (getLangOpts().ObjC && getLangOpts().CPlusPlus) {
+ // Send to 'super'.
+ if (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_super &&
+ NextToken().isNot(tok::period) &&
+ getCurScope()->isInObjcMethodScope()) {
+ CheckArrayDesignatorSyntax(*this, StartLoc, Desig);
+ return ParseAssignmentExprWithObjCMessageExprStart(
+ StartLoc, ConsumeToken(), nullptr, nullptr);
+ }
+
+ // Parse the receiver, which is either a type or an expression.
+ bool IsExpr;
+ void *TypeOrExpr;
+ if (ParseObjCXXMessageReceiver(IsExpr, TypeOrExpr)) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ // If the receiver was a type, we have a class message; parse
+ // the rest of it.
+ if (!IsExpr) {
+ CheckArrayDesignatorSyntax(*this, StartLoc, Desig);
+ return ParseAssignmentExprWithObjCMessageExprStart(StartLoc,
+ SourceLocation(),
+ ParsedType::getFromOpaquePtr(TypeOrExpr),
+ nullptr);
+ }
+
+ // If the receiver was an expression, we still don't know
+ // whether we have a message send or an array designator; just
+ // adopt the expression for further analysis below.
+ // FIXME: potentially-potentially evaluated expression above?
+ Idx = ExprResult(static_cast<Expr*>(TypeOrExpr));
+ } else if (getLangOpts().ObjC && Tok.is(tok::identifier)) {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ SourceLocation IILoc = Tok.getLocation();
+ ParsedType ReceiverType;
+ // Three cases. This is a message send to a type: [type foo]
+ // This is a message send to super: [super foo]
+ // This is a message sent to an expr: [super.bar foo]
+ switch (Actions.getObjCMessageKind(
+ getCurScope(), II, IILoc, II == Ident_super,
+ NextToken().is(tok::period), ReceiverType)) {
+ case Sema::ObjCSuperMessage:
+ CheckArrayDesignatorSyntax(*this, StartLoc, Desig);
+ return ParseAssignmentExprWithObjCMessageExprStart(
+ StartLoc, ConsumeToken(), nullptr, nullptr);
+
+ case Sema::ObjCClassMessage:
+ CheckArrayDesignatorSyntax(*this, StartLoc, Desig);
+ ConsumeToken(); // the identifier
+ if (!ReceiverType) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ // Parse type arguments and protocol qualifiers.
+ if (Tok.is(tok::less)) {
+ SourceLocation NewEndLoc;
+ TypeResult NewReceiverType
+ = parseObjCTypeArgsAndProtocolQualifiers(IILoc, ReceiverType,
+ /*consumeLastToken=*/true,
+ NewEndLoc);
+ if (!NewReceiverType.isUsable()) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ ReceiverType = NewReceiverType.get();
+ }
+
+ return ParseAssignmentExprWithObjCMessageExprStart(StartLoc,
+ SourceLocation(),
+ ReceiverType,
+ nullptr);
+
+ case Sema::ObjCInstanceMessage:
+ // Fall through; we'll just parse the expression and
+ // (possibly) treat this like an Objective-C message send
+ // later.
+ break;
+ }
+ }
+
+ // Parse the index expression, if we haven't already gotten one
+ // above (which can only happen in Objective-C++).
+ // Note that we parse this as an assignment expression, not a constant
+ // expression (allowing *=, =, etc) to handle the objc case. Sema needs
+ // to validate that the expression is a constant.
+ // FIXME: We also need to tell Sema that we're in a
+ // potentially-potentially evaluated context.
+ if (!Idx.get()) {
+ Idx = ParseAssignmentExpression();
+ if (Idx.isInvalid()) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return Idx;
+ }
+ }
+
+ // Given an expression, we could either have a designator (if the next
+ // tokens are '...' or ']' or an objc message send. If this is an objc
+ // message send, handle it now. An objc-message send is the start of
+ // an assignment-expression production.
+ if (getLangOpts().ObjC && Tok.isNot(tok::ellipsis) &&
+ Tok.isNot(tok::r_square)) {
+ CheckArrayDesignatorSyntax(*this, Tok.getLocation(), Desig);
+ return ParseAssignmentExprWithObjCMessageExprStart(
+ StartLoc, SourceLocation(), nullptr, Idx.get());
+ }
+
+ // If this is a normal array designator, remember it.
+ if (Tok.isNot(tok::ellipsis)) {
+ Desig.AddDesignator(Designator::getArray(Idx.get(), StartLoc));
+ } else {
+ // Handle the gnu array range extension.
+ Diag(Tok, diag::ext_gnu_array_range);
+ SourceLocation EllipsisLoc = ConsumeToken();
+
+ ExprResult RHS(ParseConstantExpression());
+ if (RHS.isInvalid()) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return RHS;
+ }
+ Desig.AddDesignator(Designator::getArrayRange(Idx.get(),
+ RHS.get(),
+ StartLoc, EllipsisLoc));
+ }
+
+ T.consumeClose();
+ Desig.getDesignator(Desig.getNumDesignators() - 1).setRBracketLoc(
+ T.getCloseLocation());
+ }
+
+ // Okay, we're done with the designator sequence. We know that there must be
+ // at least one designator, because the only case we can get into this method
+ // without a designator is when we have an objc message send. That case is
+ // handled and returned from above.
+ assert(!Desig.empty() && "Designator is empty?");
+
+ // Handle a normal designator sequence end, which is an equal.
+ if (Tok.is(tok::equal)) {
+ SourceLocation EqualLoc = ConsumeToken();
+ PreferredType.enterDesignatedInitializer(
+ Tok.getLocation(), DesignatorCompletion.PreferredBaseType, Desig);
+ return Actions.ActOnDesignatedInitializer(Desig, EqualLoc, false,
+ ParseInitializer());
+ }
+
+ // Handle a C++20 braced designated initialization, which results in
+ // direct-list-initialization of the aggregate element. We allow this as an
+ // extension from C++11 onwards (when direct-list-initialization was added).
+ if (Tok.is(tok::l_brace) && getLangOpts().CPlusPlus11) {
+ PreferredType.enterDesignatedInitializer(
+ Tok.getLocation(), DesignatorCompletion.PreferredBaseType, Desig);
+ return Actions.ActOnDesignatedInitializer(Desig, SourceLocation(), false,
+ ParseBraceInitializer());
+ }
+
+ // We read some number of designators and found something that isn't an = or
+ // an initializer. If we have exactly one array designator, this
+ // is the GNU 'designation: array-designator' extension. Otherwise, it is a
+ // parse error.
+ if (Desig.getNumDesignators() == 1 &&
+ (Desig.getDesignator(0).isArrayDesignator() ||
+ Desig.getDesignator(0).isArrayRangeDesignator())) {
+ Diag(Tok, diag::ext_gnu_missing_equal_designator)
+ << FixItHint::CreateInsertion(Tok.getLocation(), "= ");
+ return Actions.ActOnDesignatedInitializer(Desig, Tok.getLocation(),
+ true, ParseInitializer());
+ }
+
+ Diag(Tok, diag::err_expected_equal_designator);
+ return ExprError();
+}
+
+/// ParseBraceInitializer - Called when parsing an initializer that has a
+/// leading open brace.
+///
+/// initializer: [C99 6.7.8]
+/// '{' initializer-list '}'
+/// '{' initializer-list ',' '}'
+/// [GNU] '{' '}'
+///
+/// initializer-list:
+/// designation[opt] initializer ...[opt]
+/// initializer-list ',' designation[opt] initializer ...[opt]
+///
+ExprResult Parser::ParseBraceInitializer() {
+ InMessageExpressionRAIIObject InMessage(*this, false);
+
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+ SourceLocation LBraceLoc = T.getOpenLocation();
+
+ /// InitExprs - This is the actual list of expressions contained in the
+ /// initializer.
+ ExprVector InitExprs;
+
+ if (Tok.is(tok::r_brace)) {
+ // Empty initializers are a C++ feature and a GNU extension to C.
+ if (!getLangOpts().CPlusPlus)
+ Diag(LBraceLoc, diag::ext_gnu_empty_initializer);
+ // Match the '}'.
+ return Actions.ActOnInitList(LBraceLoc, std::nullopt, ConsumeBrace());
+ }
+
+ // Enter an appropriate expression evaluation context for an initializer list.
+ EnterExpressionEvaluationContext EnterContext(
+ Actions, EnterExpressionEvaluationContext::InitList);
+
+ bool InitExprsOk = true;
+ QualType LikelyType = PreferredType.get(T.getOpenLocation());
+ DesignatorCompletionInfo DesignatorCompletion{InitExprs, LikelyType};
+ bool CalledSignatureHelp = false;
+ auto RunSignatureHelp = [&] {
+ QualType PreferredType;
+ if (!LikelyType.isNull())
+ PreferredType = Actions.ProduceConstructorSignatureHelp(
+ LikelyType->getCanonicalTypeInternal(), T.getOpenLocation(),
+ InitExprs, T.getOpenLocation(), /*Braced=*/true);
+ CalledSignatureHelp = true;
+ return PreferredType;
+ };
+
+ while (true) {
+ PreferredType.enterFunctionArgument(Tok.getLocation(), RunSignatureHelp);
+
+ // Handle Microsoft __if_exists/if_not_exists if necessary.
+ if (getLangOpts().MicrosoftExt && (Tok.is(tok::kw___if_exists) ||
+ Tok.is(tok::kw___if_not_exists))) {
+ if (ParseMicrosoftIfExistsBraceInitializer(InitExprs, InitExprsOk)) {
+ if (Tok.isNot(tok::comma)) break;
+ ConsumeToken();
+ }
+ if (Tok.is(tok::r_brace)) break;
+ continue;
+ }
+
+ // Parse: designation[opt] initializer
+
+ // If we know that this cannot be a designation, just parse the nested
+ // initializer directly.
+ ExprResult SubElt;
+ if (MayBeDesignationStart())
+ SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion);
+ else
+ SubElt = ParseInitializer();
+
+ if (Tok.is(tok::ellipsis))
+ SubElt = Actions.ActOnPackExpansion(SubElt.get(), ConsumeToken());
+
+ SubElt = Actions.CorrectDelayedTyposInExpr(SubElt.get());
+
+ // If we couldn't parse the subelement, bail out.
+ if (SubElt.isUsable()) {
+ InitExprs.push_back(SubElt.get());
+ } else {
+ InitExprsOk = false;
+
+ // We have two ways to try to recover from this error: if the code looks
+ // grammatically ok (i.e. we have a comma coming up) try to continue
+ // parsing the rest of the initializer. This allows us to emit
+ // diagnostics for later elements that we find. If we don't see a comma,
+ // assume there is a parse error, and just skip to recover.
+ // FIXME: This comment doesn't sound right. If there is a r_brace
+ // immediately, it can't be an error, since there is no other way of
+ // leaving this loop except through this if.
+ if (Tok.isNot(tok::comma)) {
+ SkipUntil(tok::r_brace, StopBeforeMatch);
+ break;
+ }
+ }
+
+ // If we don't have a comma continued list, we're done.
+ if (Tok.isNot(tok::comma)) break;
+
+ // TODO: save comma locations if some client cares.
+ ConsumeToken();
+
+ // Handle trailing comma.
+ if (Tok.is(tok::r_brace)) break;
+ }
+
+ bool closed = !T.consumeClose();
+
+ if (InitExprsOk && closed)
+ return Actions.ActOnInitList(LBraceLoc, InitExprs,
+ T.getCloseLocation());
+
+ return ExprError(); // an error occurred.
+}
+
+
+// Return true if a comma (or closing brace) is necessary after the
+// __if_exists/if_not_exists statement.
+bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
+ bool &InitExprsOk) {
+ bool trailingComma = false;
+ IfExistsCondition Result;
+ if (ParseMicrosoftIfExistsCondition(Result))
+ return false;
+
+ BalancedDelimiterTracker Braces(*this, tok::l_brace);
+ if (Braces.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return false;
+ }
+
+ switch (Result.Behavior) {
+ case IEB_Parse:
+ // Parse the declarations below.
+ break;
+
+ case IEB_Dependent:
+ Diag(Result.KeywordLoc, diag::warn_microsoft_dependent_exists)
+ << Result.IsIfExists;
+ // Fall through to skip.
+ [[fallthrough]];
+
+ case IEB_Skip:
+ Braces.skipToEnd();
+ return false;
+ }
+
+ DesignatorCompletionInfo DesignatorCompletion{
+ InitExprs,
+ PreferredType.get(Braces.getOpenLocation()),
+ };
+ while (!isEofOrEom()) {
+ trailingComma = false;
+ // If we know that this cannot be a designation, just parse the nested
+ // initializer directly.
+ ExprResult SubElt;
+ if (MayBeDesignationStart())
+ SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion);
+ else
+ SubElt = ParseInitializer();
+
+ if (Tok.is(tok::ellipsis))
+ SubElt = Actions.ActOnPackExpansion(SubElt.get(), ConsumeToken());
+
+ // If we couldn't parse the subelement, bail out.
+ if (!SubElt.isInvalid())
+ InitExprs.push_back(SubElt.get());
+ else
+ InitExprsOk = false;
+
+ if (Tok.is(tok::comma)) {
+ ConsumeToken();
+ trailingComma = true;
+ }
+
+ if (Tok.is(tok::r_brace))
+ break;
+ }
+
+ Braces.consumeClose();
+
+ return !trailingComma;
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseObjc.cpp b/contrib/libs/clang16/lib/Parse/ParseObjc.cpp
new file mode 100644
index 0000000000..079bf9a9c0
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseObjc.cpp
@@ -0,0 +1,3756 @@
+//===--- ParseObjC.cpp - Objective C Parsing ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Objective-C portions of the Parser interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ODRDiagsEmitter.h"
+#include "clang/AST/PrettyDeclStackTrace.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/Scope.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace clang;
+
+/// Skips attributes after an Objective-C @ directive. Emits a diagnostic.
+void Parser::MaybeSkipAttributes(tok::ObjCKeywordKind Kind) {
+ ParsedAttributes attrs(AttrFactory);
+ if (Tok.is(tok::kw___attribute)) {
+ if (Kind == tok::objc_interface || Kind == tok::objc_protocol)
+ Diag(Tok, diag::err_objc_postfix_attribute_hint)
+ << (Kind == tok::objc_protocol);
+ else
+ Diag(Tok, diag::err_objc_postfix_attribute);
+ ParseGNUAttributes(attrs);
+ }
+}
+
+/// ParseObjCAtDirectives - Handle parts of the external-declaration production:
+/// external-declaration: [C99 6.9]
+/// [OBJC] objc-class-definition
+/// [OBJC] objc-class-declaration
+/// [OBJC] objc-alias-declaration
+/// [OBJC] objc-protocol-definition
+/// [OBJC] objc-method-definition
+/// [OBJC] '@' 'end'
+Parser::DeclGroupPtrTy
+Parser::ParseObjCAtDirectives(ParsedAttributes &DeclAttrs,
+ ParsedAttributes &DeclSpecAttrs) {
+ DeclAttrs.takeAllFrom(DeclSpecAttrs);
+
+ SourceLocation AtLoc = ConsumeToken(); // the "@"
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCAtDirective(getCurScope());
+ return nullptr;
+ }
+
+ switch (Tok.getObjCKeywordID()) {
+ case tok::objc_interface:
+ case tok::objc_protocol:
+ case tok::objc_implementation:
+ break;
+ default:
+ llvm::for_each(DeclAttrs, [this](const auto &Attr) {
+ if (Attr.isGNUAttribute())
+ Diag(Tok.getLocation(), diag::err_objc_unexpected_attr);
+ });
+ }
+
+ Decl *SingleDecl = nullptr;
+ switch (Tok.getObjCKeywordID()) {
+ case tok::objc_class:
+ return ParseObjCAtClassDeclaration(AtLoc);
+ case tok::objc_interface:
+ SingleDecl = ParseObjCAtInterfaceDeclaration(AtLoc, DeclAttrs);
+ break;
+ case tok::objc_protocol:
+ return ParseObjCAtProtocolDeclaration(AtLoc, DeclAttrs);
+ case tok::objc_implementation:
+ return ParseObjCAtImplementationDeclaration(AtLoc, DeclAttrs);
+ case tok::objc_end:
+ return ParseObjCAtEndDeclaration(AtLoc);
+ case tok::objc_compatibility_alias:
+ SingleDecl = ParseObjCAtAliasDeclaration(AtLoc);
+ break;
+ case tok::objc_synthesize:
+ SingleDecl = ParseObjCPropertySynthesize(AtLoc);
+ break;
+ case tok::objc_dynamic:
+ SingleDecl = ParseObjCPropertyDynamic(AtLoc);
+ break;
+ case tok::objc_import:
+ if (getLangOpts().Modules || getLangOpts().DebuggerSupport) {
+ Sema::ModuleImportState IS = Sema::ModuleImportState::NotACXX20Module;
+ SingleDecl = ParseModuleImport(AtLoc, IS);
+ break;
+ }
+ Diag(AtLoc, diag::err_atimport);
+ SkipUntil(tok::semi);
+ return Actions.ConvertDeclToDeclGroup(nullptr);
+ default:
+ Diag(AtLoc, diag::err_unexpected_at);
+ SkipUntil(tok::semi);
+ SingleDecl = nullptr;
+ break;
+ }
+ return Actions.ConvertDeclToDeclGroup(SingleDecl);
+}
+
+/// Class to handle popping type parameters when leaving the scope.
+class Parser::ObjCTypeParamListScope {
+ Sema &Actions;
+ Scope *S;
+ ObjCTypeParamList *Params;
+
+public:
+ ObjCTypeParamListScope(Sema &Actions, Scope *S)
+ : Actions(Actions), S(S), Params(nullptr) {}
+
+ ~ObjCTypeParamListScope() {
+ leave();
+ }
+
+ void enter(ObjCTypeParamList *P) {
+ assert(!Params);
+ Params = P;
+ }
+
+ void leave() {
+ if (Params)
+ Actions.popObjCTypeParamList(S, Params);
+ Params = nullptr;
+ }
+};
+
+///
+/// objc-class-declaration:
+/// '@' 'class' objc-class-forward-decl (',' objc-class-forward-decl)* ';'
+///
+/// objc-class-forward-decl:
+/// identifier objc-type-parameter-list[opt]
+///
+Parser::DeclGroupPtrTy
+Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) {
+ ConsumeToken(); // the identifier "class"
+ SmallVector<IdentifierInfo *, 8> ClassNames;
+ SmallVector<SourceLocation, 8> ClassLocs;
+ SmallVector<ObjCTypeParamList *, 8> ClassTypeParams;
+
+ while (true) {
+ MaybeSkipAttributes(tok::objc_class);
+ if (expectIdentifier()) {
+ SkipUntil(tok::semi);
+ return Actions.ConvertDeclToDeclGroup(nullptr);
+ }
+ ClassNames.push_back(Tok.getIdentifierInfo());
+ ClassLocs.push_back(Tok.getLocation());
+ ConsumeToken();
+
+ // Parse the optional objc-type-parameter-list.
+ ObjCTypeParamList *TypeParams = nullptr;
+ if (Tok.is(tok::less))
+ TypeParams = parseObjCTypeParamList();
+ ClassTypeParams.push_back(TypeParams);
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ // Consume the ';'.
+ if (ExpectAndConsume(tok::semi, diag::err_expected_after, "@class"))
+ return Actions.ConvertDeclToDeclGroup(nullptr);
+
+ return Actions.ActOnForwardClassDeclaration(atLoc, ClassNames.data(),
+ ClassLocs.data(),
+ ClassTypeParams,
+ ClassNames.size());
+}
+
+void Parser::CheckNestedObjCContexts(SourceLocation AtLoc)
+{
+ Sema::ObjCContainerKind ock = Actions.getObjCContainerKind();
+ if (ock == Sema::OCK_None)
+ return;
+
+ Decl *Decl = Actions.getObjCDeclContext();
+ if (CurParsedObjCImpl) {
+ CurParsedObjCImpl->finish(AtLoc);
+ } else {
+ Actions.ActOnAtEnd(getCurScope(), AtLoc);
+ }
+ Diag(AtLoc, diag::err_objc_missing_end)
+ << FixItHint::CreateInsertion(AtLoc, "@end\n");
+ if (Decl)
+ Diag(Decl->getBeginLoc(), diag::note_objc_container_start) << (int)ock;
+}
+
+///
+/// objc-interface:
+/// objc-class-interface-attributes[opt] objc-class-interface
+/// objc-category-interface
+///
+/// objc-class-interface:
+/// '@' 'interface' identifier objc-type-parameter-list[opt]
+/// objc-superclass[opt] objc-protocol-refs[opt]
+/// objc-class-instance-variables[opt]
+/// objc-interface-decl-list
+/// @end
+///
+/// objc-category-interface:
+/// '@' 'interface' identifier objc-type-parameter-list[opt]
+/// '(' identifier[opt] ')' objc-protocol-refs[opt]
+/// objc-interface-decl-list
+/// @end
+///
+/// objc-superclass:
+/// ':' identifier objc-type-arguments[opt]
+///
+/// objc-class-interface-attributes:
+/// __attribute__((visibility("default")))
+/// __attribute__((visibility("hidden")))
+/// __attribute__((deprecated))
+/// __attribute__((unavailable))
+/// __attribute__((objc_exception)) - used by NSException on 64-bit
+/// __attribute__((objc_root_class))
+///
+Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
+ ParsedAttributes &attrs) {
+ assert(Tok.isObjCAtKeyword(tok::objc_interface) &&
+ "ParseObjCAtInterfaceDeclaration(): Expected @interface");
+ CheckNestedObjCContexts(AtLoc);
+ ConsumeToken(); // the "interface" identifier
+
+ // Code completion after '@interface'.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCInterfaceDecl(getCurScope());
+ return nullptr;
+ }
+
+ MaybeSkipAttributes(tok::objc_interface);
+
+ if (expectIdentifier())
+ return nullptr; // missing class or category name.
+
+ // We have a class or category name - consume it.
+ IdentifierInfo *nameId = Tok.getIdentifierInfo();
+ SourceLocation nameLoc = ConsumeToken();
+
+ // Parse the objc-type-parameter-list or objc-protocol-refs. For the latter
+ // case, LAngleLoc will be valid and ProtocolIdents will capture the
+ // protocol references (that have not yet been resolved).
+ SourceLocation LAngleLoc, EndProtoLoc;
+ SmallVector<IdentifierLocPair, 8> ProtocolIdents;
+ ObjCTypeParamList *typeParameterList = nullptr;
+ ObjCTypeParamListScope typeParamScope(Actions, getCurScope());
+ if (Tok.is(tok::less))
+ typeParameterList = parseObjCTypeParamListOrProtocolRefs(
+ typeParamScope, LAngleLoc, ProtocolIdents, EndProtoLoc);
+
+ if (Tok.is(tok::l_paren) &&
+ !isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { // we have a category.
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ SourceLocation categoryLoc;
+ IdentifierInfo *categoryId = nullptr;
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCInterfaceCategory(getCurScope(), nameId, nameLoc);
+ return nullptr;
+ }
+
+ // For ObjC2, the category name is optional (not an error).
+ if (Tok.is(tok::identifier)) {
+ categoryId = Tok.getIdentifierInfo();
+ categoryLoc = ConsumeToken();
+ }
+ else if (!getLangOpts().ObjC) {
+ Diag(Tok, diag::err_expected)
+ << tok::identifier; // missing category name.
+ return nullptr;
+ }
+
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid())
+ return nullptr;
+
+ // Next, we need to check for any protocol references.
+ assert(LAngleLoc.isInvalid() && "Cannot have already parsed protocols");
+ SmallVector<Decl *, 8> ProtocolRefs;
+ SmallVector<SourceLocation, 8> ProtocolLocs;
+ if (Tok.is(tok::less) &&
+ ParseObjCProtocolReferences(ProtocolRefs, ProtocolLocs, true, true,
+ LAngleLoc, EndProtoLoc,
+ /*consumeLastToken=*/true))
+ return nullptr;
+
+ ObjCCategoryDecl *CategoryType = Actions.ActOnStartCategoryInterface(
+ AtLoc, nameId, nameLoc, typeParameterList, categoryId, categoryLoc,
+ ProtocolRefs.data(), ProtocolRefs.size(), ProtocolLocs.data(),
+ EndProtoLoc, attrs);
+
+ if (Tok.is(tok::l_brace))
+ ParseObjCClassInstanceVariables(CategoryType, tok::objc_private, AtLoc);
+
+ ParseObjCInterfaceDeclList(tok::objc_not_keyword, CategoryType);
+
+ return CategoryType;
+ }
+ // Parse a class interface.
+ IdentifierInfo *superClassId = nullptr;
+ SourceLocation superClassLoc;
+ SourceLocation typeArgsLAngleLoc;
+ SmallVector<ParsedType, 4> typeArgs;
+ SourceLocation typeArgsRAngleLoc;
+ SmallVector<Decl *, 4> protocols;
+ SmallVector<SourceLocation, 4> protocolLocs;
+ if (Tok.is(tok::colon)) { // a super class is specified.
+ ConsumeToken();
+
+ // Code completion of superclass names.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCSuperclass(getCurScope(), nameId, nameLoc);
+ return nullptr;
+ }
+
+ if (expectIdentifier())
+ return nullptr; // missing super class name.
+ superClassId = Tok.getIdentifierInfo();
+ superClassLoc = ConsumeToken();
+
+ // Type arguments for the superclass or protocol conformances.
+ if (Tok.is(tok::less)) {
+ parseObjCTypeArgsOrProtocolQualifiers(
+ nullptr, typeArgsLAngleLoc, typeArgs, typeArgsRAngleLoc, LAngleLoc,
+ protocols, protocolLocs, EndProtoLoc,
+ /*consumeLastToken=*/true,
+ /*warnOnIncompleteProtocols=*/true);
+ if (Tok.is(tok::eof))
+ return nullptr;
+ }
+ }
+
+ // Next, we need to check for any protocol references.
+ if (LAngleLoc.isValid()) {
+ if (!ProtocolIdents.empty()) {
+ // We already parsed the protocols named when we thought we had a
+ // type parameter list. Translate them into actual protocol references.
+ for (const auto &pair : ProtocolIdents) {
+ protocolLocs.push_back(pair.second);
+ }
+ Actions.FindProtocolDeclaration(/*WarnOnDeclarations=*/true,
+ /*ForObjCContainer=*/true,
+ ProtocolIdents, protocols);
+ }
+ } else if (protocols.empty() && Tok.is(tok::less) &&
+ ParseObjCProtocolReferences(protocols, protocolLocs, true, true,
+ LAngleLoc, EndProtoLoc,
+ /*consumeLastToken=*/true)) {
+ return nullptr;
+ }
+
+ if (Tok.isNot(tok::less))
+ Actions.ActOnTypedefedProtocols(protocols, protocolLocs,
+ superClassId, superClassLoc);
+
+ Sema::SkipBodyInfo SkipBody;
+ ObjCInterfaceDecl *ClsType = Actions.ActOnStartClassInterface(
+ getCurScope(), AtLoc, nameId, nameLoc, typeParameterList, superClassId,
+ superClassLoc, typeArgs,
+ SourceRange(typeArgsLAngleLoc, typeArgsRAngleLoc), protocols.data(),
+ protocols.size(), protocolLocs.data(), EndProtoLoc, attrs, &SkipBody);
+
+ if (Tok.is(tok::l_brace))
+ ParseObjCClassInstanceVariables(ClsType, tok::objc_protected, AtLoc);
+
+ ParseObjCInterfaceDeclList(tok::objc_interface, ClsType);
+
+ if (SkipBody.CheckSameAsPrevious) {
+ auto *PreviousDef = cast<ObjCInterfaceDecl>(SkipBody.Previous);
+ if (Actions.ActOnDuplicateODRHashDefinition(ClsType, PreviousDef)) {
+ ClsType->mergeDuplicateDefinitionWithCommon(PreviousDef->getDefinition());
+ } else {
+ ODRDiagsEmitter DiagsEmitter(Diags, Actions.getASTContext(),
+ getPreprocessor().getLangOpts());
+ DiagsEmitter.diagnoseMismatch(PreviousDef, ClsType);
+ ClsType->setInvalidDecl();
+ }
+ }
+
+ return ClsType;
+}
+
+/// Add an attribute for a context-sensitive type nullability to the given
+/// declarator.
+static void addContextSensitiveTypeNullability(Parser &P,
+ Declarator &D,
+ NullabilityKind nullability,
+ SourceLocation nullabilityLoc,
+ bool &addedToDeclSpec) {
+ // Create the attribute.
+ auto getNullabilityAttr = [&](AttributePool &Pool) -> ParsedAttr * {
+ return Pool.create(P.getNullabilityKeyword(nullability),
+ SourceRange(nullabilityLoc), nullptr, SourceLocation(),
+ nullptr, 0, ParsedAttr::AS_ContextSensitiveKeyword);
+ };
+
+ if (D.getNumTypeObjects() > 0) {
+ // Add the attribute to the declarator chunk nearest the declarator.
+ D.getTypeObject(0).getAttrs().addAtEnd(
+ getNullabilityAttr(D.getAttributePool()));
+ } else if (!addedToDeclSpec) {
+ // Otherwise, just put it on the declaration specifiers (if one
+ // isn't there already).
+ D.getMutableDeclSpec().getAttributes().addAtEnd(
+ getNullabilityAttr(D.getMutableDeclSpec().getAttributes().getPool()));
+ addedToDeclSpec = true;
+ }
+}
+
+/// Parse an Objective-C type parameter list, if present, or capture
+/// the locations of the protocol identifiers for a list of protocol
+/// references.
+///
+/// objc-type-parameter-list:
+/// '<' objc-type-parameter (',' objc-type-parameter)* '>'
+///
+/// objc-type-parameter:
+/// objc-type-parameter-variance? identifier objc-type-parameter-bound[opt]
+///
+/// objc-type-parameter-bound:
+/// ':' type-name
+///
+/// objc-type-parameter-variance:
+/// '__covariant'
+/// '__contravariant'
+///
+/// \param lAngleLoc The location of the starting '<'.
+///
+/// \param protocolIdents Will capture the list of identifiers, if the
+/// angle brackets contain a list of protocol references rather than a
+/// type parameter list.
+///
+/// \param rAngleLoc The location of the ending '>'.
+ObjCTypeParamList *Parser::parseObjCTypeParamListOrProtocolRefs(
+ ObjCTypeParamListScope &Scope, SourceLocation &lAngleLoc,
+ SmallVectorImpl<IdentifierLocPair> &protocolIdents,
+ SourceLocation &rAngleLoc, bool mayBeProtocolList) {
+ assert(Tok.is(tok::less) && "Not at the beginning of a type parameter list");
+
+ // Within the type parameter list, don't treat '>' as an operator.
+ GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
+
+ // Local function to "flush" the protocol identifiers, turning them into
+ // type parameters.
+ SmallVector<Decl *, 4> typeParams;
+ auto makeProtocolIdentsIntoTypeParameters = [&]() {
+ unsigned index = 0;
+ for (const auto &pair : protocolIdents) {
+ DeclResult typeParam = Actions.actOnObjCTypeParam(
+ getCurScope(), ObjCTypeParamVariance::Invariant, SourceLocation(),
+ index++, pair.first, pair.second, SourceLocation(), nullptr);
+ if (typeParam.isUsable())
+ typeParams.push_back(typeParam.get());
+ }
+
+ protocolIdents.clear();
+ mayBeProtocolList = false;
+ };
+
+ bool invalid = false;
+ lAngleLoc = ConsumeToken();
+
+ do {
+ // Parse the variance, if any.
+ SourceLocation varianceLoc;
+ ObjCTypeParamVariance variance = ObjCTypeParamVariance::Invariant;
+ if (Tok.is(tok::kw___covariant) || Tok.is(tok::kw___contravariant)) {
+ variance = Tok.is(tok::kw___covariant)
+ ? ObjCTypeParamVariance::Covariant
+ : ObjCTypeParamVariance::Contravariant;
+ varianceLoc = ConsumeToken();
+
+ // Once we've seen a variance specific , we know this is not a
+ // list of protocol references.
+ if (mayBeProtocolList) {
+ // Up until now, we have been queuing up parameters because they
+ // might be protocol references. Turn them into parameters now.
+ makeProtocolIdentsIntoTypeParameters();
+ }
+ }
+
+ // Parse the identifier.
+ if (!Tok.is(tok::identifier)) {
+ // Code completion.
+ if (Tok.is(tok::code_completion)) {
+ // FIXME: If these aren't protocol references, we'll need different
+ // completions.
+ cutOffParsing();
+ Actions.CodeCompleteObjCProtocolReferences(protocolIdents);
+
+ // FIXME: Better recovery here?.
+ return nullptr;
+ }
+
+ Diag(Tok, diag::err_objc_expected_type_parameter);
+ invalid = true;
+ break;
+ }
+
+ IdentifierInfo *paramName = Tok.getIdentifierInfo();
+ SourceLocation paramLoc = ConsumeToken();
+
+ // If there is a bound, parse it.
+ SourceLocation colonLoc;
+ TypeResult boundType;
+ if (TryConsumeToken(tok::colon, colonLoc)) {
+ // Once we've seen a bound, we know this is not a list of protocol
+ // references.
+ if (mayBeProtocolList) {
+ // Up until now, we have been queuing up parameters because they
+ // might be protocol references. Turn them into parameters now.
+ makeProtocolIdentsIntoTypeParameters();
+ }
+
+ // type-name
+ boundType = ParseTypeName();
+ if (boundType.isInvalid())
+ invalid = true;
+ } else if (mayBeProtocolList) {
+ // If this could still be a protocol list, just capture the identifier.
+ // We don't want to turn it into a parameter.
+ protocolIdents.push_back(std::make_pair(paramName, paramLoc));
+ continue;
+ }
+
+ // Create the type parameter.
+ DeclResult typeParam = Actions.actOnObjCTypeParam(
+ getCurScope(), variance, varianceLoc, typeParams.size(), paramName,
+ paramLoc, colonLoc, boundType.isUsable() ? boundType.get() : nullptr);
+ if (typeParam.isUsable())
+ typeParams.push_back(typeParam.get());
+ } while (TryConsumeToken(tok::comma));
+
+ // Parse the '>'.
+ if (invalid) {
+ SkipUntil(tok::greater, tok::at, StopBeforeMatch);
+ if (Tok.is(tok::greater))
+ ConsumeToken();
+ } else if (ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc,
+ /*ConsumeLastToken=*/true,
+ /*ObjCGenericList=*/true)) {
+ SkipUntil({tok::greater, tok::greaterequal, tok::at, tok::minus,
+ tok::minus, tok::plus, tok::colon, tok::l_paren, tok::l_brace,
+ tok::comma, tok::semi },
+ StopBeforeMatch);
+ if (Tok.is(tok::greater))
+ ConsumeToken();
+ }
+
+ if (mayBeProtocolList) {
+ // A type parameter list must be followed by either a ':' (indicating the
+ // presence of a superclass) or a '(' (indicating that this is a category
+ // or extension). This disambiguates between an objc-type-parameter-list
+ // and a objc-protocol-refs.
+ if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_paren)) {
+ // Returning null indicates that we don't have a type parameter list.
+ // The results the caller needs to handle the protocol references are
+ // captured in the reference parameters already.
+ return nullptr;
+ }
+
+ // We have a type parameter list that looks like a list of protocol
+ // references. Turn that parameter list into type parameters.
+ makeProtocolIdentsIntoTypeParameters();
+ }
+
+ // Form the type parameter list and enter its scope.
+ ObjCTypeParamList *list = Actions.actOnObjCTypeParamList(
+ getCurScope(),
+ lAngleLoc,
+ typeParams,
+ rAngleLoc);
+ Scope.enter(list);
+
+ // Clear out the angle locations; they're used by the caller to indicate
+ // whether there are any protocol references.
+ lAngleLoc = SourceLocation();
+ rAngleLoc = SourceLocation();
+ return invalid ? nullptr : list;
+}
+
+/// Parse an objc-type-parameter-list.
+ObjCTypeParamList *Parser::parseObjCTypeParamList() {
+ SourceLocation lAngleLoc;
+ SmallVector<IdentifierLocPair, 1> protocolIdents;
+ SourceLocation rAngleLoc;
+
+ ObjCTypeParamListScope Scope(Actions, getCurScope());
+ return parseObjCTypeParamListOrProtocolRefs(Scope, lAngleLoc, protocolIdents,
+ rAngleLoc,
+ /*mayBeProtocolList=*/false);
+}
+
+/// objc-interface-decl-list:
+/// empty
+/// objc-interface-decl-list objc-property-decl [OBJC2]
+/// objc-interface-decl-list objc-method-requirement [OBJC2]
+/// objc-interface-decl-list objc-method-proto ';'
+/// objc-interface-decl-list declaration
+/// objc-interface-decl-list ';'
+///
+/// objc-method-requirement: [OBJC2]
+/// @required
+/// @optional
+///
+void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
+ Decl *CDecl) {
+ SmallVector<Decl *, 32> allMethods;
+ SmallVector<DeclGroupPtrTy, 8> allTUVariables;
+ tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword;
+
+ SourceRange AtEnd;
+
+ while (true) {
+ // If this is a method prototype, parse it.
+ if (Tok.isOneOf(tok::minus, tok::plus)) {
+ if (Decl *methodPrototype =
+ ParseObjCMethodPrototype(MethodImplKind, false))
+ allMethods.push_back(methodPrototype);
+ // Consume the ';' here, since ParseObjCMethodPrototype() is re-used for
+ // method definitions.
+ if (ExpectAndConsumeSemi(diag::err_expected_semi_after_method_proto)) {
+ // We didn't find a semi and we error'ed out. Skip until a ';' or '@'.
+ SkipUntil(tok::at, StopAtSemi | StopBeforeMatch);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ }
+ continue;
+ }
+ if (Tok.is(tok::l_paren)) {
+ Diag(Tok, diag::err_expected_minus_or_plus);
+ ParseObjCMethodDecl(Tok.getLocation(),
+ tok::minus,
+ MethodImplKind, false);
+ continue;
+ }
+ // Ignore excess semicolons.
+ if (Tok.is(tok::semi)) {
+ // FIXME: This should use ConsumeExtraSemi() for extraneous semicolons,
+ // to make -Wextra-semi diagnose them.
+ ConsumeToken();
+ continue;
+ }
+
+ // If we got to the end of the file, exit the loop.
+ if (isEofOrEom())
+ break;
+
+ // Code completion within an Objective-C interface.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(),
+ CurParsedObjCImpl? Sema::PCC_ObjCImplementation
+ : Sema::PCC_ObjCInterface);
+ return;
+ }
+
+ // If we don't have an @ directive, parse it as a function definition.
+ if (Tok.isNot(tok::at)) {
+ // The code below does not consume '}'s because it is afraid of eating the
+ // end of a namespace. Because of the way this code is structured, an
+ // erroneous r_brace would cause an infinite loop if not handled here.
+ if (Tok.is(tok::r_brace))
+ break;
+
+ ParsedAttributes EmptyDeclAttrs(AttrFactory);
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+
+ // Since we call ParseDeclarationOrFunctionDefinition() instead of
+ // ParseExternalDeclaration() below (so that this doesn't parse nested
+ // @interfaces), this needs to duplicate some code from the latter.
+ if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
+ SourceLocation DeclEnd;
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+ allTUVariables.push_back(ParseDeclaration(DeclaratorContext::File,
+ DeclEnd, EmptyDeclAttrs,
+ EmptyDeclSpecAttrs));
+ continue;
+ }
+
+ allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(
+ EmptyDeclAttrs, EmptyDeclSpecAttrs));
+ continue;
+ }
+
+ // Otherwise, we have an @ directive, eat the @.
+ SourceLocation AtLoc = ConsumeToken(); // the "@"
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCAtDirective(getCurScope());
+ return;
+ }
+
+ tok::ObjCKeywordKind DirectiveKind = Tok.getObjCKeywordID();
+
+ if (DirectiveKind == tok::objc_end) { // @end -> terminate list
+ AtEnd.setBegin(AtLoc);
+ AtEnd.setEnd(Tok.getLocation());
+ break;
+ } else if (DirectiveKind == tok::objc_not_keyword) {
+ Diag(Tok, diag::err_objc_unknown_at);
+ SkipUntil(tok::semi);
+ continue;
+ }
+
+ // Eat the identifier.
+ ConsumeToken();
+
+ switch (DirectiveKind) {
+ default:
+ // FIXME: If someone forgets an @end on a protocol, this loop will
+ // continue to eat up tons of stuff and spew lots of nonsense errors. It
+ // would probably be better to bail out if we saw an @class or @interface
+ // or something like that.
+ Diag(AtLoc, diag::err_objc_illegal_interface_qual);
+ // Skip until we see an '@' or '}' or ';'.
+ SkipUntil(tok::r_brace, tok::at, StopAtSemi);
+ break;
+
+ case tok::objc_implementation:
+ case tok::objc_interface:
+ Diag(AtLoc, diag::err_objc_missing_end)
+ << FixItHint::CreateInsertion(AtLoc, "@end\n");
+ Diag(CDecl->getBeginLoc(), diag::note_objc_container_start)
+ << (int)Actions.getObjCContainerKind();
+ ConsumeToken();
+ break;
+
+ case tok::objc_required:
+ case tok::objc_optional:
+ // This is only valid on protocols.
+ if (contextKey != tok::objc_protocol)
+ Diag(AtLoc, diag::err_objc_directive_only_in_protocol);
+ else
+ MethodImplKind = DirectiveKind;
+ break;
+
+ case tok::objc_property:
+ ObjCDeclSpec OCDS;
+ SourceLocation LParenLoc;
+ // Parse property attribute list, if any.
+ if (Tok.is(tok::l_paren)) {
+ LParenLoc = Tok.getLocation();
+ ParseObjCPropertyAttribute(OCDS);
+ }
+
+ bool addedToDeclSpec = false;
+ auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) {
+ if (FD.D.getIdentifier() == nullptr) {
+ Diag(AtLoc, diag::err_objc_property_requires_field_name)
+ << FD.D.getSourceRange();
+ return;
+ }
+ if (FD.BitfieldSize) {
+ Diag(AtLoc, diag::err_objc_property_bitfield)
+ << FD.D.getSourceRange();
+ return;
+ }
+
+ // Map a nullability property attribute to a context-sensitive keyword
+ // attribute.
+ if (OCDS.getPropertyAttributes() &
+ ObjCPropertyAttribute::kind_nullability)
+ addContextSensitiveTypeNullability(*this, FD.D, OCDS.getNullability(),
+ OCDS.getNullabilityLoc(),
+ addedToDeclSpec);
+
+ // Install the property declarator into interfaceDecl.
+ IdentifierInfo *SelName =
+ OCDS.getGetterName() ? OCDS.getGetterName() : FD.D.getIdentifier();
+
+ Selector GetterSel = PP.getSelectorTable().getNullarySelector(SelName);
+ IdentifierInfo *SetterName = OCDS.getSetterName();
+ Selector SetterSel;
+ if (SetterName)
+ SetterSel = PP.getSelectorTable().getSelector(1, &SetterName);
+ else
+ SetterSel = SelectorTable::constructSetterSelector(
+ PP.getIdentifierTable(), PP.getSelectorTable(),
+ FD.D.getIdentifier());
+ Decl *Property = Actions.ActOnProperty(
+ getCurScope(), AtLoc, LParenLoc, FD, OCDS, GetterSel, SetterSel,
+ MethodImplKind);
+
+ FD.complete(Property);
+ };
+
+ // Parse all the comma separated declarators.
+ ParsingDeclSpec DS(*this);
+ ParseStructDeclaration(DS, ObjCPropertyCallback);
+
+ ExpectAndConsume(tok::semi, diag::err_expected_semi_decl_list);
+ break;
+ }
+ }
+
+ // We break out of the big loop in two cases: when we see @end or when we see
+ // EOF. In the former case, eat the @end. In the later case, emit an error.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCAtDirective(getCurScope());
+ return;
+ } else if (Tok.isObjCAtKeyword(tok::objc_end)) {
+ ConsumeToken(); // the "end" identifier
+ } else {
+ Diag(Tok, diag::err_objc_missing_end)
+ << FixItHint::CreateInsertion(Tok.getLocation(), "\n@end\n");
+ Diag(CDecl->getBeginLoc(), diag::note_objc_container_start)
+ << (int)Actions.getObjCContainerKind();
+ AtEnd.setBegin(Tok.getLocation());
+ AtEnd.setEnd(Tok.getLocation());
+ }
+
+ // Insert collected methods declarations into the @interface object.
+ // This passes in an invalid SourceLocation for AtEndLoc when EOF is hit.
+ Actions.ActOnAtEnd(getCurScope(), AtEnd, allMethods, allTUVariables);
+}
+
+/// Diagnose redundant or conflicting nullability information.
+static void diagnoseRedundantPropertyNullability(Parser &P,
+ ObjCDeclSpec &DS,
+ NullabilityKind nullability,
+ SourceLocation nullabilityLoc){
+ if (DS.getNullability() == nullability) {
+ P.Diag(nullabilityLoc, diag::warn_nullability_duplicate)
+ << DiagNullabilityKind(nullability, true)
+ << SourceRange(DS.getNullabilityLoc());
+ return;
+ }
+
+ P.Diag(nullabilityLoc, diag::err_nullability_conflicting)
+ << DiagNullabilityKind(nullability, true)
+ << DiagNullabilityKind(DS.getNullability(), true)
+ << SourceRange(DS.getNullabilityLoc());
+}
+
+/// Parse property attribute declarations.
+///
+/// property-attr-decl: '(' property-attrlist ')'
+/// property-attrlist:
+/// property-attribute
+/// property-attrlist ',' property-attribute
+/// property-attribute:
+/// getter '=' identifier
+/// setter '=' identifier ':'
+/// direct
+/// readonly
+/// readwrite
+/// assign
+/// retain
+/// copy
+/// nonatomic
+/// atomic
+/// strong
+/// weak
+/// unsafe_unretained
+/// nonnull
+/// nullable
+/// null_unspecified
+/// null_resettable
+/// class
+///
+void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
+ assert(Tok.getKind() == tok::l_paren);
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ while (true) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCPropertyFlags(getCurScope(), DS);
+ return;
+ }
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+
+ // If this is not an identifier at all, bail out early.
+ if (!II) {
+ T.consumeClose();
+ return;
+ }
+
+ SourceLocation AttrName = ConsumeToken(); // consume last attribute name
+
+ if (II->isStr("readonly"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_readonly);
+ else if (II->isStr("assign"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_assign);
+ else if (II->isStr("unsafe_unretained"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_unsafe_unretained);
+ else if (II->isStr("readwrite"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_readwrite);
+ else if (II->isStr("retain"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_retain);
+ else if (II->isStr("strong"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_strong);
+ else if (II->isStr("copy"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_copy);
+ else if (II->isStr("nonatomic"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nonatomic);
+ else if (II->isStr("atomic"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_atomic);
+ else if (II->isStr("weak"))
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_weak);
+ else if (II->isStr("getter") || II->isStr("setter")) {
+ bool IsSetter = II->getNameStart()[0] == 's';
+
+ // getter/setter require extra treatment.
+ unsigned DiagID = IsSetter ? diag::err_objc_expected_equal_for_setter :
+ diag::err_objc_expected_equal_for_getter;
+
+ if (ExpectAndConsume(tok::equal, DiagID)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ if (IsSetter)
+ Actions.CodeCompleteObjCPropertySetter(getCurScope());
+ else
+ Actions.CodeCompleteObjCPropertyGetter(getCurScope());
+ return;
+ }
+
+ SourceLocation SelLoc;
+ IdentifierInfo *SelIdent = ParseObjCSelectorPiece(SelLoc);
+
+ if (!SelIdent) {
+ Diag(Tok, diag::err_objc_expected_selector_for_getter_setter)
+ << IsSetter;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ if (IsSetter) {
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_setter);
+ DS.setSetterName(SelIdent, SelLoc);
+
+ if (ExpectAndConsume(tok::colon,
+ diag::err_expected_colon_after_setter_name)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+ } else {
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_getter);
+ DS.setGetterName(SelIdent, SelLoc);
+ }
+ } else if (II->isStr("nonnull")) {
+ if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability)
+ diagnoseRedundantPropertyNullability(*this, DS,
+ NullabilityKind::NonNull,
+ Tok.getLocation());
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability);
+ DS.setNullability(Tok.getLocation(), NullabilityKind::NonNull);
+ } else if (II->isStr("nullable")) {
+ if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability)
+ diagnoseRedundantPropertyNullability(*this, DS,
+ NullabilityKind::Nullable,
+ Tok.getLocation());
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability);
+ DS.setNullability(Tok.getLocation(), NullabilityKind::Nullable);
+ } else if (II->isStr("null_unspecified")) {
+ if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability)
+ diagnoseRedundantPropertyNullability(*this, DS,
+ NullabilityKind::Unspecified,
+ Tok.getLocation());
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability);
+ DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
+ } else if (II->isStr("null_resettable")) {
+ if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability)
+ diagnoseRedundantPropertyNullability(*this, DS,
+ NullabilityKind::Unspecified,
+ Tok.getLocation());
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability);
+ DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
+
+ // Also set the null_resettable bit.
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_null_resettable);
+ } else if (II->isStr("class")) {
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_class);
+ } else if (II->isStr("direct")) {
+ DS.setPropertyAttributes(ObjCPropertyAttribute::kind_direct);
+ } else {
+ Diag(AttrName, diag::err_objc_expected_property_attr) << II;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return;
+ }
+
+ if (Tok.isNot(tok::comma))
+ break;
+
+ ConsumeToken();
+ }
+
+ T.consumeClose();
+}
+
+/// objc-method-proto:
+/// objc-instance-method objc-method-decl objc-method-attributes[opt]
+/// objc-class-method objc-method-decl objc-method-attributes[opt]
+///
+/// objc-instance-method: '-'
+/// objc-class-method: '+'
+///
+/// objc-method-attributes: [OBJC2]
+/// __attribute__((deprecated))
+///
+Decl *Parser::ParseObjCMethodPrototype(tok::ObjCKeywordKind MethodImplKind,
+ bool MethodDefinition) {
+ assert(Tok.isOneOf(tok::minus, tok::plus) && "expected +/-");
+
+ tok::TokenKind methodType = Tok.getKind();
+ SourceLocation mLoc = ConsumeToken();
+ Decl *MDecl = ParseObjCMethodDecl(mLoc, methodType, MethodImplKind,
+ MethodDefinition);
+ // Since this rule is used for both method declarations and definitions,
+ // the caller is (optionally) responsible for consuming the ';'.
+ return MDecl;
+}
+
+/// objc-selector:
+/// identifier
+/// one of
+/// enum struct union if else while do for switch case default
+/// break continue return goto asm sizeof typeof __alignof
+/// unsigned long const short volatile signed restrict _Complex
+/// in out inout bycopy byref oneway int char float double void _Bool
+///
+IdentifierInfo *Parser::ParseObjCSelectorPiece(SourceLocation &SelectorLoc) {
+
+ switch (Tok.getKind()) {
+ default:
+ return nullptr;
+ case tok::colon:
+ // Empty selector piece uses the location of the ':'.
+ SelectorLoc = Tok.getLocation();
+ return nullptr;
+ case tok::ampamp:
+ case tok::ampequal:
+ case tok::amp:
+ case tok::pipe:
+ case tok::tilde:
+ case tok::exclaim:
+ case tok::exclaimequal:
+ case tok::pipepipe:
+ case tok::pipeequal:
+ case tok::caret:
+ case tok::caretequal: {
+ std::string ThisTok(PP.getSpelling(Tok));
+ if (isLetter(ThisTok[0])) {
+ IdentifierInfo *II = &PP.getIdentifierTable().get(ThisTok);
+ Tok.setKind(tok::identifier);
+ SelectorLoc = ConsumeToken();
+ return II;
+ }
+ return nullptr;
+ }
+
+ case tok::identifier:
+ case tok::kw_asm:
+ case tok::kw_auto:
+ case tok::kw_bool:
+ case tok::kw_break:
+ case tok::kw_case:
+ case tok::kw_catch:
+ case tok::kw_char:
+ case tok::kw_class:
+ case tok::kw_const:
+ case tok::kw_const_cast:
+ case tok::kw_continue:
+ case tok::kw_default:
+ case tok::kw_delete:
+ case tok::kw_do:
+ case tok::kw_double:
+ case tok::kw_dynamic_cast:
+ case tok::kw_else:
+ case tok::kw_enum:
+ case tok::kw_explicit:
+ case tok::kw_export:
+ case tok::kw_extern:
+ case tok::kw_false:
+ case tok::kw_float:
+ case tok::kw_for:
+ case tok::kw_friend:
+ case tok::kw_goto:
+ case tok::kw_if:
+ case tok::kw_inline:
+ case tok::kw_int:
+ case tok::kw_long:
+ case tok::kw_mutable:
+ case tok::kw_namespace:
+ case tok::kw_new:
+ case tok::kw_operator:
+ case tok::kw_private:
+ case tok::kw_protected:
+ case tok::kw_public:
+ case tok::kw_register:
+ case tok::kw_reinterpret_cast:
+ case tok::kw_restrict:
+ case tok::kw_return:
+ case tok::kw_short:
+ case tok::kw_signed:
+ case tok::kw_sizeof:
+ case tok::kw_static:
+ case tok::kw_static_cast:
+ case tok::kw_struct:
+ case tok::kw_switch:
+ case tok::kw_template:
+ case tok::kw_this:
+ case tok::kw_throw:
+ case tok::kw_true:
+ case tok::kw_try:
+ case tok::kw_typedef:
+ case tok::kw_typeid:
+ case tok::kw_typename:
+ case tok::kw_typeof:
+ case tok::kw_union:
+ case tok::kw_unsigned:
+ case tok::kw_using:
+ case tok::kw_virtual:
+ case tok::kw_void:
+ case tok::kw_volatile:
+ case tok::kw_wchar_t:
+ case tok::kw_while:
+ case tok::kw__Bool:
+ case tok::kw__Complex:
+ case tok::kw___alignof:
+ case tok::kw___auto_type:
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ SelectorLoc = ConsumeToken();
+ return II;
+ }
+}
+
+/// objc-for-collection-in: 'in'
+///
+bool Parser::isTokIdentifier_in() const {
+ // FIXME: May have to do additional look-ahead to only allow for
+ // valid tokens following an 'in'; such as an identifier, unary operators,
+ // '[' etc.
+ return (getLangOpts().ObjC && Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo() == ObjCTypeQuals[objc_in]);
+}
+
+/// ParseObjCTypeQualifierList - This routine parses the objective-c's type
+/// qualifier list and builds their bitmask representation in the input
+/// argument.
+///
+/// objc-type-qualifiers:
+/// objc-type-qualifier
+/// objc-type-qualifiers objc-type-qualifier
+///
+/// objc-type-qualifier:
+/// 'in'
+/// 'out'
+/// 'inout'
+/// 'oneway'
+/// 'bycopy'
+/// 'byref'
+/// 'nonnull'
+/// 'nullable'
+/// 'null_unspecified'
+///
+void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
+ DeclaratorContext Context) {
+ assert(Context == DeclaratorContext::ObjCParameter ||
+ Context == DeclaratorContext::ObjCResult);
+
+ while (true) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCPassingType(
+ getCurScope(), DS, Context == DeclaratorContext::ObjCParameter);
+ return;
+ }
+
+ if (Tok.isNot(tok::identifier))
+ return;
+
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ for (unsigned i = 0; i != objc_NumQuals; ++i) {
+ if (II != ObjCTypeQuals[i] ||
+ NextToken().is(tok::less) ||
+ NextToken().is(tok::coloncolon))
+ continue;
+
+ ObjCDeclSpec::ObjCDeclQualifier Qual;
+ NullabilityKind Nullability;
+ switch (i) {
+ default: llvm_unreachable("Unknown decl qualifier");
+ case objc_in: Qual = ObjCDeclSpec::DQ_In; break;
+ case objc_out: Qual = ObjCDeclSpec::DQ_Out; break;
+ case objc_inout: Qual = ObjCDeclSpec::DQ_Inout; break;
+ case objc_oneway: Qual = ObjCDeclSpec::DQ_Oneway; break;
+ case objc_bycopy: Qual = ObjCDeclSpec::DQ_Bycopy; break;
+ case objc_byref: Qual = ObjCDeclSpec::DQ_Byref; break;
+
+ case objc_nonnull:
+ Qual = ObjCDeclSpec::DQ_CSNullability;
+ Nullability = NullabilityKind::NonNull;
+ break;
+
+ case objc_nullable:
+ Qual = ObjCDeclSpec::DQ_CSNullability;
+ Nullability = NullabilityKind::Nullable;
+ break;
+
+ case objc_null_unspecified:
+ Qual = ObjCDeclSpec::DQ_CSNullability;
+ Nullability = NullabilityKind::Unspecified;
+ break;
+ }
+
+ // FIXME: Diagnose redundant specifiers.
+ DS.setObjCDeclQualifier(Qual);
+ if (Qual == ObjCDeclSpec::DQ_CSNullability)
+ DS.setNullability(Tok.getLocation(), Nullability);
+
+ ConsumeToken();
+ II = nullptr;
+ break;
+ }
+
+ // If this wasn't a recognized qualifier, bail out.
+ if (II) return;
+ }
+}
+
+/// Take all the decl attributes out of the given list and add
+/// them to the given attribute set.
+static void takeDeclAttributes(ParsedAttributesView &attrs,
+ ParsedAttributesView &from) {
+ for (auto &AL : llvm::reverse(from)) {
+ if (!AL.isUsedAsTypeAttr()) {
+ from.remove(&AL);
+ attrs.addAtEnd(&AL);
+ }
+ }
+}
+
+/// takeDeclAttributes - Take all the decl attributes from the given
+/// declarator and add them to the given list.
+static void takeDeclAttributes(ParsedAttributes &attrs,
+ Declarator &D) {
+ // This gets called only from Parser::ParseObjCTypeName(), and that should
+ // never add declaration attributes to the Declarator.
+ assert(D.getDeclarationAttributes().empty());
+
+ // First, take ownership of all attributes.
+ attrs.getPool().takeAllFrom(D.getAttributePool());
+ attrs.getPool().takeAllFrom(D.getDeclSpec().getAttributePool());
+
+ // Now actually move the attributes over.
+ takeDeclAttributes(attrs, D.getMutableDeclSpec().getAttributes());
+ takeDeclAttributes(attrs, D.getAttributes());
+ for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i)
+ takeDeclAttributes(attrs, D.getTypeObject(i).getAttrs());
+}
+
+/// objc-type-name:
+/// '(' objc-type-qualifiers[opt] type-name ')'
+/// '(' objc-type-qualifiers[opt] ')'
+///
+ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
+ DeclaratorContext context,
+ ParsedAttributes *paramAttrs) {
+ assert(context == DeclaratorContext::ObjCParameter ||
+ context == DeclaratorContext::ObjCResult);
+ assert((paramAttrs != nullptr) ==
+ (context == DeclaratorContext::ObjCParameter));
+
+ assert(Tok.is(tok::l_paren) && "expected (");
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ ObjCDeclContextSwitch ObjCDC(*this);
+
+ // Parse type qualifiers, in, inout, etc.
+ ParseObjCTypeQualifierList(DS, context);
+ SourceLocation TypeStartLoc = Tok.getLocation();
+
+ ParsedType Ty;
+ if (isTypeSpecifierQualifier() || isObjCInstancetype()) {
+ // Parse an abstract declarator.
+ DeclSpec declSpec(AttrFactory);
+ declSpec.setObjCQualifiers(&DS);
+ DeclSpecContext dsContext = DeclSpecContext::DSC_normal;
+ if (context == DeclaratorContext::ObjCResult)
+ dsContext = DeclSpecContext::DSC_objc_method_result;
+ ParseSpecifierQualifierList(declSpec, AS_none, dsContext);
+ Declarator declarator(declSpec, ParsedAttributesView::none(), context);
+ ParseDeclarator(declarator);
+
+ // If that's not invalid, extract a type.
+ if (!declarator.isInvalidType()) {
+ // Map a nullability specifier to a context-sensitive keyword attribute.
+ bool addedToDeclSpec = false;
+ if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability)
+ addContextSensitiveTypeNullability(*this, declarator,
+ DS.getNullability(),
+ DS.getNullabilityLoc(),
+ addedToDeclSpec);
+
+ TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
+ if (!type.isInvalid())
+ Ty = type.get();
+
+ // If we're parsing a parameter, steal all the decl attributes
+ // and add them to the decl spec.
+ if (context == DeclaratorContext::ObjCParameter)
+ takeDeclAttributes(*paramAttrs, declarator);
+ }
+ }
+
+ if (Tok.is(tok::r_paren))
+ T.consumeClose();
+ else if (Tok.getLocation() == TypeStartLoc) {
+ // If we didn't eat any tokens, then this isn't a type.
+ Diag(Tok, diag::err_expected_type);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ } else {
+ // Otherwise, we found *something*, but didn't get a ')' in the right
+ // place. Emit an error then return what we have as the type.
+ T.consumeClose();
+ }
+ return Ty;
+}
+
+/// objc-method-decl:
+/// objc-selector
+/// objc-keyword-selector objc-parmlist[opt]
+/// objc-type-name objc-selector
+/// objc-type-name objc-keyword-selector objc-parmlist[opt]
+///
+/// objc-keyword-selector:
+/// objc-keyword-decl
+/// objc-keyword-selector objc-keyword-decl
+///
+/// objc-keyword-decl:
+/// objc-selector ':' objc-type-name objc-keyword-attributes[opt] identifier
+/// objc-selector ':' objc-keyword-attributes[opt] identifier
+/// ':' objc-type-name objc-keyword-attributes[opt] identifier
+/// ':' objc-keyword-attributes[opt] identifier
+///
+/// objc-parmlist:
+/// objc-parms objc-ellipsis[opt]
+///
+/// objc-parms:
+/// objc-parms , parameter-declaration
+///
+/// objc-ellipsis:
+/// , ...
+///
+/// objc-keyword-attributes: [OBJC2]
+/// __attribute__((unused))
+///
+Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
+ tok::TokenKind mType,
+ tok::ObjCKeywordKind MethodImplKind,
+ bool MethodDefinition) {
+ ParsingDeclRAIIObject PD(*this, ParsingDeclRAIIObject::NoParent);
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus,
+ /*ReturnType=*/nullptr);
+ return nullptr;
+ }
+
+ // Parse the return type if present.
+ ParsedType ReturnType;
+ ObjCDeclSpec DSRet;
+ if (Tok.is(tok::l_paren))
+ ReturnType =
+ ParseObjCTypeName(DSRet, DeclaratorContext::ObjCResult, nullptr);
+
+ // If attributes exist before the method, parse them.
+ ParsedAttributes methodAttrs(AttrFactory);
+ MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
+ methodAttrs);
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus,
+ ReturnType);
+ return nullptr;
+ }
+
+ // Now parse the selector.
+ SourceLocation selLoc;
+ IdentifierInfo *SelIdent = ParseObjCSelectorPiece(selLoc);
+
+ // An unnamed colon is valid.
+ if (!SelIdent && Tok.isNot(tok::colon)) { // missing selector name.
+ Diag(Tok, diag::err_expected_selector_for_method)
+ << SourceRange(mLoc, Tok.getLocation());
+ // Skip until we get a ; or @.
+ SkipUntil(tok::at, StopAtSemi | StopBeforeMatch);
+ return nullptr;
+ }
+
+ SmallVector<DeclaratorChunk::ParamInfo, 8> CParamInfo;
+ if (Tok.isNot(tok::colon)) {
+ // If attributes exist after the method, parse them.
+ MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
+ methodAttrs);
+
+ Selector Sel = PP.getSelectorTable().getNullarySelector(SelIdent);
+ Decl *Result = Actions.ActOnMethodDeclaration(
+ getCurScope(), mLoc, Tok.getLocation(), mType, DSRet, ReturnType,
+ selLoc, Sel, nullptr, CParamInfo.data(), CParamInfo.size(), methodAttrs,
+ MethodImplKind, false, MethodDefinition);
+ PD.complete(Result);
+ return Result;
+ }
+
+ SmallVector<IdentifierInfo *, 12> KeyIdents;
+ SmallVector<SourceLocation, 12> KeyLocs;
+ SmallVector<Sema::ObjCArgInfo, 12> ArgInfos;
+ ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
+ Scope::FunctionDeclarationScope | Scope::DeclScope);
+
+ AttributePool allParamAttrs(AttrFactory);
+ while (true) {
+ ParsedAttributes paramAttrs(AttrFactory);
+ Sema::ObjCArgInfo ArgInfo;
+
+ // Each iteration parses a single keyword argument.
+ if (ExpectAndConsume(tok::colon))
+ break;
+
+ ArgInfo.Type = nullptr;
+ if (Tok.is(tok::l_paren)) // Parse the argument type if present.
+ ArgInfo.Type = ParseObjCTypeName(
+ ArgInfo.DeclSpec, DeclaratorContext::ObjCParameter, &paramAttrs);
+
+ // If attributes exist before the argument name, parse them.
+ // Regardless, collect all the attributes we've parsed so far.
+ MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
+ paramAttrs);
+ ArgInfo.ArgAttrs = paramAttrs;
+
+ // Code completion for the next piece of the selector.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ KeyIdents.push_back(SelIdent);
+ Actions.CodeCompleteObjCMethodDeclSelector(getCurScope(),
+ mType == tok::minus,
+ /*AtParameterName=*/true,
+ ReturnType, KeyIdents);
+ return nullptr;
+ }
+
+ if (expectIdentifier())
+ break; // missing argument name.
+
+ ArgInfo.Name = Tok.getIdentifierInfo();
+ ArgInfo.NameLoc = Tok.getLocation();
+ ConsumeToken(); // Eat the identifier.
+
+ ArgInfos.push_back(ArgInfo);
+ KeyIdents.push_back(SelIdent);
+ KeyLocs.push_back(selLoc);
+
+ // Make sure the attributes persist.
+ allParamAttrs.takeAllFrom(paramAttrs.getPool());
+
+ // Code completion for the next piece of the selector.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCMethodDeclSelector(getCurScope(),
+ mType == tok::minus,
+ /*AtParameterName=*/false,
+ ReturnType, KeyIdents);
+ return nullptr;
+ }
+
+ // Check for another keyword selector.
+ SelIdent = ParseObjCSelectorPiece(selLoc);
+ if (!SelIdent && Tok.isNot(tok::colon))
+ break;
+ if (!SelIdent) {
+ SourceLocation ColonLoc = Tok.getLocation();
+ if (PP.getLocForEndOfToken(ArgInfo.NameLoc) == ColonLoc) {
+ Diag(ArgInfo.NameLoc, diag::warn_missing_selector_name) << ArgInfo.Name;
+ Diag(ArgInfo.NameLoc, diag::note_missing_selector_name) << ArgInfo.Name;
+ Diag(ColonLoc, diag::note_force_empty_selector_name) << ArgInfo.Name;
+ }
+ }
+ // We have a selector or a colon, continue parsing.
+ }
+
+ bool isVariadic = false;
+ bool cStyleParamWarned = false;
+ // Parse the (optional) parameter list.
+ while (Tok.is(tok::comma)) {
+ ConsumeToken();
+ if (Tok.is(tok::ellipsis)) {
+ isVariadic = true;
+ ConsumeToken();
+ break;
+ }
+ if (!cStyleParamWarned) {
+ Diag(Tok, diag::warn_cstyle_param);
+ cStyleParamWarned = true;
+ }
+ DeclSpec DS(AttrFactory);
+ ParseDeclarationSpecifiers(DS);
+ // Parse the declarator.
+ Declarator ParmDecl(DS, ParsedAttributesView::none(),
+ DeclaratorContext::Prototype);
+ ParseDeclarator(ParmDecl);
+ IdentifierInfo *ParmII = ParmDecl.getIdentifier();
+ Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDecl);
+ CParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII,
+ ParmDecl.getIdentifierLoc(),
+ Param,
+ nullptr));
+ }
+
+ // FIXME: Add support for optional parameter list...
+ // If attributes exist after the method, parse them.
+ MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
+ methodAttrs);
+
+ if (KeyIdents.size() == 0)
+ return nullptr;
+
+ Selector Sel = PP.getSelectorTable().getSelector(KeyIdents.size(),
+ &KeyIdents[0]);
+ Decl *Result = Actions.ActOnMethodDeclaration(
+ getCurScope(), mLoc, Tok.getLocation(), mType, DSRet, ReturnType, KeyLocs,
+ Sel, &ArgInfos[0], CParamInfo.data(), CParamInfo.size(), methodAttrs,
+ MethodImplKind, isVariadic, MethodDefinition);
+
+ PD.complete(Result);
+ return Result;
+}
+
+/// objc-protocol-refs:
+/// '<' identifier-list '>'
+///
+bool Parser::
+ParseObjCProtocolReferences(SmallVectorImpl<Decl *> &Protocols,
+ SmallVectorImpl<SourceLocation> &ProtocolLocs,
+ bool WarnOnDeclarations, bool ForObjCContainer,
+ SourceLocation &LAngleLoc, SourceLocation &EndLoc,
+ bool consumeLastToken) {
+ assert(Tok.is(tok::less) && "expected <");
+
+ LAngleLoc = ConsumeToken(); // the "<"
+
+ SmallVector<IdentifierLocPair, 8> ProtocolIdents;
+
+ while (true) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCProtocolReferences(ProtocolIdents);
+ return true;
+ }
+
+ if (expectIdentifier()) {
+ SkipUntil(tok::greater, StopAtSemi);
+ return true;
+ }
+ ProtocolIdents.push_back(std::make_pair(Tok.getIdentifierInfo(),
+ Tok.getLocation()));
+ ProtocolLocs.push_back(Tok.getLocation());
+ ConsumeToken();
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ // Consume the '>'.
+ if (ParseGreaterThanInTemplateList(LAngleLoc, EndLoc, consumeLastToken,
+ /*ObjCGenericList=*/false))
+ return true;
+
+ // Convert the list of protocols identifiers into a list of protocol decls.
+ Actions.FindProtocolDeclaration(WarnOnDeclarations, ForObjCContainer,
+ ProtocolIdents, Protocols);
+ return false;
+}
+
+TypeResult Parser::parseObjCProtocolQualifierType(SourceLocation &rAngleLoc) {
+ assert(Tok.is(tok::less) && "Protocol qualifiers start with '<'");
+ assert(getLangOpts().ObjC && "Protocol qualifiers only exist in Objective-C");
+
+ SourceLocation lAngleLoc;
+ SmallVector<Decl *, 8> protocols;
+ SmallVector<SourceLocation, 8> protocolLocs;
+ (void)ParseObjCProtocolReferences(protocols, protocolLocs, false, false,
+ lAngleLoc, rAngleLoc,
+ /*consumeLastToken=*/true);
+ TypeResult result = Actions.actOnObjCProtocolQualifierType(lAngleLoc,
+ protocols,
+ protocolLocs,
+ rAngleLoc);
+ if (result.isUsable()) {
+ Diag(lAngleLoc, diag::warn_objc_protocol_qualifier_missing_id)
+ << FixItHint::CreateInsertion(lAngleLoc, "id")
+ << SourceRange(lAngleLoc, rAngleLoc);
+ }
+
+ return result;
+}
+
+/// Parse Objective-C type arguments or protocol qualifiers.
+///
+/// objc-type-arguments:
+/// '<' type-name '...'[opt] (',' type-name '...'[opt])* '>'
+///
+void Parser::parseObjCTypeArgsOrProtocolQualifiers(
+ ParsedType baseType,
+ SourceLocation &typeArgsLAngleLoc,
+ SmallVectorImpl<ParsedType> &typeArgs,
+ SourceLocation &typeArgsRAngleLoc,
+ SourceLocation &protocolLAngleLoc,
+ SmallVectorImpl<Decl *> &protocols,
+ SmallVectorImpl<SourceLocation> &protocolLocs,
+ SourceLocation &protocolRAngleLoc,
+ bool consumeLastToken,
+ bool warnOnIncompleteProtocols) {
+ assert(Tok.is(tok::less) && "Not at the start of type args or protocols");
+ SourceLocation lAngleLoc = ConsumeToken();
+
+ // Whether all of the elements we've parsed thus far are single
+ // identifiers, which might be types or might be protocols.
+ bool allSingleIdentifiers = true;
+ SmallVector<IdentifierInfo *, 4> identifiers;
+ SmallVectorImpl<SourceLocation> &identifierLocs = protocolLocs;
+
+ // Parse a list of comma-separated identifiers, bailing out if we
+ // see something different.
+ do {
+ // Parse a single identifier.
+ if (Tok.is(tok::identifier) &&
+ (NextToken().is(tok::comma) ||
+ NextToken().is(tok::greater) ||
+ NextToken().is(tok::greatergreater))) {
+ identifiers.push_back(Tok.getIdentifierInfo());
+ identifierLocs.push_back(ConsumeToken());
+ continue;
+ }
+
+ if (Tok.is(tok::code_completion)) {
+ // FIXME: Also include types here.
+ SmallVector<IdentifierLocPair, 4> identifierLocPairs;
+ for (unsigned i = 0, n = identifiers.size(); i != n; ++i) {
+ identifierLocPairs.push_back(IdentifierLocPair(identifiers[i],
+ identifierLocs[i]));
+ }
+
+ QualType BaseT = Actions.GetTypeFromParser(baseType);
+ cutOffParsing();
+ if (!BaseT.isNull() && BaseT->acceptsObjCTypeParams()) {
+ Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Type);
+ } else {
+ Actions.CodeCompleteObjCProtocolReferences(identifierLocPairs);
+ }
+ return;
+ }
+
+ allSingleIdentifiers = false;
+ break;
+ } while (TryConsumeToken(tok::comma));
+
+ // If we parsed an identifier list, semantic analysis sorts out
+ // whether it refers to protocols or to type arguments.
+ if (allSingleIdentifiers) {
+ // Parse the closing '>'.
+ SourceLocation rAngleLoc;
+ (void)ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc, consumeLastToken,
+ /*ObjCGenericList=*/true);
+
+ // Let Sema figure out what we parsed.
+ Actions.actOnObjCTypeArgsOrProtocolQualifiers(getCurScope(),
+ baseType,
+ lAngleLoc,
+ identifiers,
+ identifierLocs,
+ rAngleLoc,
+ typeArgsLAngleLoc,
+ typeArgs,
+ typeArgsRAngleLoc,
+ protocolLAngleLoc,
+ protocols,
+ protocolRAngleLoc,
+ warnOnIncompleteProtocols);
+ return;
+ }
+
+ // We parsed an identifier list but stumbled into non single identifiers, this
+ // means we might (a) check that what we already parsed is a legitimate type
+ // (not a protocol or unknown type) and (b) parse the remaining ones, which
+ // must all be type args.
+
+ // Convert the identifiers into type arguments.
+ bool invalid = false;
+ IdentifierInfo *foundProtocolId = nullptr, *foundValidTypeId = nullptr;
+ SourceLocation foundProtocolSrcLoc, foundValidTypeSrcLoc;
+ SmallVector<IdentifierInfo *, 2> unknownTypeArgs;
+ SmallVector<SourceLocation, 2> unknownTypeArgsLoc;
+
+ for (unsigned i = 0, n = identifiers.size(); i != n; ++i) {
+ ParsedType typeArg
+ = Actions.getTypeName(*identifiers[i], identifierLocs[i], getCurScope());
+ if (typeArg) {
+ DeclSpec DS(AttrFactory);
+ const char *prevSpec = nullptr;
+ unsigned diagID;
+ DS.SetTypeSpecType(TST_typename, identifierLocs[i], prevSpec, diagID,
+ typeArg, Actions.getASTContext().getPrintingPolicy());
+
+ // Form a declarator to turn this into a type.
+ Declarator D(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ TypeResult fullTypeArg = Actions.ActOnTypeName(getCurScope(), D);
+ if (fullTypeArg.isUsable()) {
+ typeArgs.push_back(fullTypeArg.get());
+ if (!foundValidTypeId) {
+ foundValidTypeId = identifiers[i];
+ foundValidTypeSrcLoc = identifierLocs[i];
+ }
+ } else {
+ invalid = true;
+ unknownTypeArgs.push_back(identifiers[i]);
+ unknownTypeArgsLoc.push_back(identifierLocs[i]);
+ }
+ } else {
+ invalid = true;
+ if (!Actions.LookupProtocol(identifiers[i], identifierLocs[i])) {
+ unknownTypeArgs.push_back(identifiers[i]);
+ unknownTypeArgsLoc.push_back(identifierLocs[i]);
+ } else if (!foundProtocolId) {
+ foundProtocolId = identifiers[i];
+ foundProtocolSrcLoc = identifierLocs[i];
+ }
+ }
+ }
+
+ // Continue parsing type-names.
+ do {
+ Token CurTypeTok = Tok;
+ TypeResult typeArg = ParseTypeName();
+
+ // Consume the '...' for a pack expansion.
+ SourceLocation ellipsisLoc;
+ TryConsumeToken(tok::ellipsis, ellipsisLoc);
+ if (typeArg.isUsable() && ellipsisLoc.isValid()) {
+ typeArg = Actions.ActOnPackExpansion(typeArg.get(), ellipsisLoc);
+ }
+
+ if (typeArg.isUsable()) {
+ typeArgs.push_back(typeArg.get());
+ if (!foundValidTypeId) {
+ foundValidTypeId = CurTypeTok.getIdentifierInfo();
+ foundValidTypeSrcLoc = CurTypeTok.getLocation();
+ }
+ } else {
+ invalid = true;
+ }
+ } while (TryConsumeToken(tok::comma));
+
+ // Diagnose the mix between type args and protocols.
+ if (foundProtocolId && foundValidTypeId)
+ Actions.DiagnoseTypeArgsAndProtocols(foundProtocolId, foundProtocolSrcLoc,
+ foundValidTypeId,
+ foundValidTypeSrcLoc);
+
+ // Diagnose unknown arg types.
+ ParsedType T;
+ if (unknownTypeArgs.size())
+ for (unsigned i = 0, e = unknownTypeArgsLoc.size(); i < e; ++i)
+ Actions.DiagnoseUnknownTypeName(unknownTypeArgs[i], unknownTypeArgsLoc[i],
+ getCurScope(), nullptr, T);
+
+ // Parse the closing '>'.
+ SourceLocation rAngleLoc;
+ (void)ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc, consumeLastToken,
+ /*ObjCGenericList=*/true);
+
+ if (invalid) {
+ typeArgs.clear();
+ return;
+ }
+
+ // Record left/right angle locations.
+ typeArgsLAngleLoc = lAngleLoc;
+ typeArgsRAngleLoc = rAngleLoc;
+}
+
+void Parser::parseObjCTypeArgsAndProtocolQualifiers(
+ ParsedType baseType,
+ SourceLocation &typeArgsLAngleLoc,
+ SmallVectorImpl<ParsedType> &typeArgs,
+ SourceLocation &typeArgsRAngleLoc,
+ SourceLocation &protocolLAngleLoc,
+ SmallVectorImpl<Decl *> &protocols,
+ SmallVectorImpl<SourceLocation> &protocolLocs,
+ SourceLocation &protocolRAngleLoc,
+ bool consumeLastToken) {
+ assert(Tok.is(tok::less));
+
+ // Parse the first angle-bracket-delimited clause.
+ parseObjCTypeArgsOrProtocolQualifiers(baseType,
+ typeArgsLAngleLoc,
+ typeArgs,
+ typeArgsRAngleLoc,
+ protocolLAngleLoc,
+ protocols,
+ protocolLocs,
+ protocolRAngleLoc,
+ consumeLastToken,
+ /*warnOnIncompleteProtocols=*/false);
+ if (Tok.is(tok::eof)) // Nothing else to do here...
+ return;
+
+ // An Objective-C object pointer followed by type arguments
+ // can then be followed again by a set of protocol references, e.g.,
+ // \c NSArray<NSView><NSTextDelegate>
+ if ((consumeLastToken && Tok.is(tok::less)) ||
+ (!consumeLastToken && NextToken().is(tok::less))) {
+ // If we aren't consuming the last token, the prior '>' is still hanging
+ // there. Consume it before we parse the protocol qualifiers.
+ if (!consumeLastToken)
+ ConsumeToken();
+
+ if (!protocols.empty()) {
+ SkipUntilFlags skipFlags = SkipUntilFlags();
+ if (!consumeLastToken)
+ skipFlags = skipFlags | StopBeforeMatch;
+ Diag(Tok, diag::err_objc_type_args_after_protocols)
+ << SourceRange(protocolLAngleLoc, protocolRAngleLoc);
+ SkipUntil(tok::greater, tok::greatergreater, skipFlags);
+ } else {
+ ParseObjCProtocolReferences(protocols, protocolLocs,
+ /*WarnOnDeclarations=*/false,
+ /*ForObjCContainer=*/false,
+ protocolLAngleLoc, protocolRAngleLoc,
+ consumeLastToken);
+ }
+ }
+}
+
+TypeResult Parser::parseObjCTypeArgsAndProtocolQualifiers(
+ SourceLocation loc,
+ ParsedType type,
+ bool consumeLastToken,
+ SourceLocation &endLoc) {
+ assert(Tok.is(tok::less));
+ SourceLocation typeArgsLAngleLoc;
+ SmallVector<ParsedType, 4> typeArgs;
+ SourceLocation typeArgsRAngleLoc;
+ SourceLocation protocolLAngleLoc;
+ SmallVector<Decl *, 4> protocols;
+ SmallVector<SourceLocation, 4> protocolLocs;
+ SourceLocation protocolRAngleLoc;
+
+ // Parse type arguments and protocol qualifiers.
+ parseObjCTypeArgsAndProtocolQualifiers(type, typeArgsLAngleLoc, typeArgs,
+ typeArgsRAngleLoc, protocolLAngleLoc,
+ protocols, protocolLocs,
+ protocolRAngleLoc, consumeLastToken);
+
+ if (Tok.is(tok::eof))
+ return true; // Invalid type result.
+
+ // Compute the location of the last token.
+ if (consumeLastToken)
+ endLoc = PrevTokLocation;
+ else
+ endLoc = Tok.getLocation();
+
+ return Actions.actOnObjCTypeArgsAndProtocolQualifiers(
+ getCurScope(),
+ loc,
+ type,
+ typeArgsLAngleLoc,
+ typeArgs,
+ typeArgsRAngleLoc,
+ protocolLAngleLoc,
+ protocols,
+ protocolLocs,
+ protocolRAngleLoc);
+}
+
+void Parser::HelperActionsForIvarDeclarations(
+ ObjCContainerDecl *interfaceDecl, SourceLocation atLoc,
+ BalancedDelimiterTracker &T, SmallVectorImpl<Decl *> &AllIvarDecls,
+ bool RBraceMissing) {
+ if (!RBraceMissing)
+ T.consumeClose();
+
+ assert(getObjCDeclContext() == interfaceDecl &&
+ "Ivars should have interfaceDecl as their decl context");
+ Actions.ActOnLastBitfield(T.getCloseLocation(), AllIvarDecls);
+ // Call ActOnFields() even if we don't have any decls. This is useful
+ // for code rewriting tools that need to be aware of the empty list.
+ Actions.ActOnFields(getCurScope(), atLoc, interfaceDecl, AllIvarDecls,
+ T.getOpenLocation(), T.getCloseLocation(),
+ ParsedAttributesView());
+}
+
+/// objc-class-instance-variables:
+/// '{' objc-instance-variable-decl-list[opt] '}'
+///
+/// objc-instance-variable-decl-list:
+/// objc-visibility-spec
+/// objc-instance-variable-decl ';'
+/// ';'
+/// objc-instance-variable-decl-list objc-visibility-spec
+/// objc-instance-variable-decl-list objc-instance-variable-decl ';'
+/// objc-instance-variable-decl-list static_assert-declaration
+/// objc-instance-variable-decl-list ';'
+///
+/// objc-visibility-spec:
+/// @private
+/// @protected
+/// @public
+/// @package [OBJC2]
+///
+/// objc-instance-variable-decl:
+/// struct-declaration
+///
+void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl,
+ tok::ObjCKeywordKind visibility,
+ SourceLocation atLoc) {
+ assert(Tok.is(tok::l_brace) && "expected {");
+ SmallVector<Decl *, 32> AllIvarDecls;
+
+ ParseScope ClassScope(this, Scope::DeclScope | Scope::ClassScope);
+
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+ // While we still have something to read, read the instance variables.
+ while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
+ // Each iteration of this loop reads one objc-instance-variable-decl.
+
+ // Check for extraneous top-level semicolon.
+ if (Tok.is(tok::semi)) {
+ ConsumeExtraSemi(InstanceVariableList);
+ continue;
+ }
+
+ // Set the default visibility to private.
+ if (TryConsumeToken(tok::at)) { // parse objc-visibility-spec
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCAtVisibility(getCurScope());
+ return;
+ }
+
+ switch (Tok.getObjCKeywordID()) {
+ case tok::objc_private:
+ case tok::objc_public:
+ case tok::objc_protected:
+ case tok::objc_package:
+ visibility = Tok.getObjCKeywordID();
+ ConsumeToken();
+ continue;
+
+ case tok::objc_end:
+ Diag(Tok, diag::err_objc_unexpected_atend);
+ Tok.setLocation(Tok.getLocation().getLocWithOffset(-1));
+ Tok.setKind(tok::at);
+ Tok.setLength(1);
+ PP.EnterToken(Tok, /*IsReinject*/true);
+ HelperActionsForIvarDeclarations(interfaceDecl, atLoc,
+ T, AllIvarDecls, true);
+ return;
+
+ default:
+ Diag(Tok, diag::err_objc_illegal_visibility_spec);
+ continue;
+ }
+ }
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(),
+ Sema::PCC_ObjCInstanceVariableList);
+ return;
+ }
+
+ // This needs to duplicate a small amount of code from
+ // ParseStructUnionBody() for things that should work in both
+ // C struct and in Objective-C class instance variables.
+ if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
+ SourceLocation DeclEnd;
+ ParseStaticAssertDeclaration(DeclEnd);
+ continue;
+ }
+
+ auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
+ assert(getObjCDeclContext() == interfaceDecl &&
+ "Ivar should have interfaceDecl as its decl context");
+ // Install the declarator into the interface decl.
+ FD.D.setObjCIvar(true);
+ Decl *Field = Actions.ActOnIvar(
+ getCurScope(), FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D,
+ FD.BitfieldSize, visibility);
+ if (Field)
+ AllIvarDecls.push_back(Field);
+ FD.complete(Field);
+ };
+
+ // Parse all the comma separated declarators.
+ ParsingDeclSpec DS(*this);
+ ParseStructDeclaration(DS, ObjCIvarCallback);
+
+ if (Tok.is(tok::semi)) {
+ ConsumeToken();
+ } else {
+ Diag(Tok, diag::err_expected_semi_decl_list);
+ // Skip to end of block or statement
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ }
+ }
+ HelperActionsForIvarDeclarations(interfaceDecl, atLoc,
+ T, AllIvarDecls, false);
+}
+
+/// objc-protocol-declaration:
+/// objc-protocol-definition
+/// objc-protocol-forward-reference
+///
+/// objc-protocol-definition:
+/// \@protocol identifier
+/// objc-protocol-refs[opt]
+/// objc-interface-decl-list
+/// \@end
+///
+/// objc-protocol-forward-reference:
+/// \@protocol identifier-list ';'
+///
+/// "\@protocol identifier ;" should be resolved as "\@protocol
+/// identifier-list ;": objc-interface-decl-list may not start with a
+/// semicolon in the first alternative if objc-protocol-refs are omitted.
+Parser::DeclGroupPtrTy
+Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc,
+ ParsedAttributes &attrs) {
+ assert(Tok.isObjCAtKeyword(tok::objc_protocol) &&
+ "ParseObjCAtProtocolDeclaration(): Expected @protocol");
+ ConsumeToken(); // the "protocol" identifier
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCProtocolDecl(getCurScope());
+ return nullptr;
+ }
+
+ MaybeSkipAttributes(tok::objc_protocol);
+
+ if (expectIdentifier())
+ return nullptr; // missing protocol name.
+ // Save the protocol name, then consume it.
+ IdentifierInfo *protocolName = Tok.getIdentifierInfo();
+ SourceLocation nameLoc = ConsumeToken();
+
+ if (TryConsumeToken(tok::semi)) { // forward declaration of one protocol.
+ IdentifierLocPair ProtoInfo(protocolName, nameLoc);
+ return Actions.ActOnForwardProtocolDeclaration(AtLoc, ProtoInfo, attrs);
+ }
+
+ CheckNestedObjCContexts(AtLoc);
+
+ if (Tok.is(tok::comma)) { // list of forward declarations.
+ SmallVector<IdentifierLocPair, 8> ProtocolRefs;
+ ProtocolRefs.push_back(std::make_pair(protocolName, nameLoc));
+
+ // Parse the list of forward declarations.
+ while (true) {
+ ConsumeToken(); // the ','
+ if (expectIdentifier()) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+ ProtocolRefs.push_back(IdentifierLocPair(Tok.getIdentifierInfo(),
+ Tok.getLocation()));
+ ConsumeToken(); // the identifier
+
+ if (Tok.isNot(tok::comma))
+ break;
+ }
+ // Consume the ';'.
+ if (ExpectAndConsume(tok::semi, diag::err_expected_after, "@protocol"))
+ return nullptr;
+
+ return Actions.ActOnForwardProtocolDeclaration(AtLoc, ProtocolRefs, attrs);
+ }
+
+ // Last, and definitely not least, parse a protocol declaration.
+ SourceLocation LAngleLoc, EndProtoLoc;
+
+ SmallVector<Decl *, 8> ProtocolRefs;
+ SmallVector<SourceLocation, 8> ProtocolLocs;
+ if (Tok.is(tok::less) &&
+ ParseObjCProtocolReferences(ProtocolRefs, ProtocolLocs, false, true,
+ LAngleLoc, EndProtoLoc,
+ /*consumeLastToken=*/true))
+ return nullptr;
+
+ Sema::SkipBodyInfo SkipBody;
+ ObjCProtocolDecl *ProtoType = Actions.ActOnStartProtocolInterface(
+ AtLoc, protocolName, nameLoc, ProtocolRefs.data(), ProtocolRefs.size(),
+ ProtocolLocs.data(), EndProtoLoc, attrs, &SkipBody);
+
+ ParseObjCInterfaceDeclList(tok::objc_protocol, ProtoType);
+ if (SkipBody.CheckSameAsPrevious) {
+ auto *PreviousDef = cast<ObjCProtocolDecl>(SkipBody.Previous);
+ if (Actions.ActOnDuplicateODRHashDefinition(ProtoType, PreviousDef)) {
+ ProtoType->mergeDuplicateDefinitionWithCommon(
+ PreviousDef->getDefinition());
+ } else {
+ ODRDiagsEmitter DiagsEmitter(Diags, Actions.getASTContext(),
+ getPreprocessor().getLangOpts());
+ DiagsEmitter.diagnoseMismatch(PreviousDef, ProtoType);
+ }
+ }
+ return Actions.ConvertDeclToDeclGroup(ProtoType);
+}
+
+/// objc-implementation:
+/// objc-class-implementation-prologue
+/// objc-category-implementation-prologue
+///
+/// objc-class-implementation-prologue:
+/// @implementation identifier objc-superclass[opt]
+/// objc-class-instance-variables[opt]
+///
+/// objc-category-implementation-prologue:
+/// @implementation identifier ( identifier )
+Parser::DeclGroupPtrTy
+Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc,
+ ParsedAttributes &Attrs) {
+ assert(Tok.isObjCAtKeyword(tok::objc_implementation) &&
+ "ParseObjCAtImplementationDeclaration(): Expected @implementation");
+ CheckNestedObjCContexts(AtLoc);
+ ConsumeToken(); // the "implementation" identifier
+
+ // Code completion after '@implementation'.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCImplementationDecl(getCurScope());
+ return nullptr;
+ }
+
+ MaybeSkipAttributes(tok::objc_implementation);
+
+ if (expectIdentifier())
+ return nullptr; // missing class or category name.
+ // We have a class or category name - consume it.
+ IdentifierInfo *nameId = Tok.getIdentifierInfo();
+ SourceLocation nameLoc = ConsumeToken(); // consume class or category name
+ ObjCImplDecl *ObjCImpDecl = nullptr;
+
+ // Neither a type parameter list nor a list of protocol references is
+ // permitted here. Parse and diagnose them.
+ if (Tok.is(tok::less)) {
+ SourceLocation lAngleLoc, rAngleLoc;
+ SmallVector<IdentifierLocPair, 8> protocolIdents;
+ SourceLocation diagLoc = Tok.getLocation();
+ ObjCTypeParamListScope typeParamScope(Actions, getCurScope());
+ if (parseObjCTypeParamListOrProtocolRefs(typeParamScope, lAngleLoc,
+ protocolIdents, rAngleLoc)) {
+ Diag(diagLoc, diag::err_objc_parameterized_implementation)
+ << SourceRange(diagLoc, PrevTokLocation);
+ } else if (lAngleLoc.isValid()) {
+ Diag(lAngleLoc, diag::err_unexpected_protocol_qualifier)
+ << FixItHint::CreateRemoval(SourceRange(lAngleLoc, rAngleLoc));
+ }
+ }
+
+ if (Tok.is(tok::l_paren)) {
+ // we have a category implementation.
+ ConsumeParen();
+ SourceLocation categoryLoc, rparenLoc;
+ IdentifierInfo *categoryId = nullptr;
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCImplementationCategory(getCurScope(), nameId, nameLoc);
+ return nullptr;
+ }
+
+ if (Tok.is(tok::identifier)) {
+ categoryId = Tok.getIdentifierInfo();
+ categoryLoc = ConsumeToken();
+ } else {
+ Diag(Tok, diag::err_expected)
+ << tok::identifier; // missing category name.
+ return nullptr;
+ }
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ SkipUntil(tok::r_paren); // don't stop at ';'
+ return nullptr;
+ }
+ rparenLoc = ConsumeParen();
+ if (Tok.is(tok::less)) { // we have illegal '<' try to recover
+ Diag(Tok, diag::err_unexpected_protocol_qualifier);
+ SourceLocation protocolLAngleLoc, protocolRAngleLoc;
+ SmallVector<Decl *, 4> protocols;
+ SmallVector<SourceLocation, 4> protocolLocs;
+ (void)ParseObjCProtocolReferences(protocols, protocolLocs,
+ /*warnOnIncompleteProtocols=*/false,
+ /*ForObjCContainer=*/false,
+ protocolLAngleLoc, protocolRAngleLoc,
+ /*consumeLastToken=*/true);
+ }
+ ObjCImpDecl = Actions.ActOnStartCategoryImplementation(
+ AtLoc, nameId, nameLoc, categoryId, categoryLoc, Attrs);
+
+ } else {
+ // We have a class implementation
+ SourceLocation superClassLoc;
+ IdentifierInfo *superClassId = nullptr;
+ if (TryConsumeToken(tok::colon)) {
+ // We have a super class
+ if (expectIdentifier())
+ return nullptr; // missing super class name.
+ superClassId = Tok.getIdentifierInfo();
+ superClassLoc = ConsumeToken(); // Consume super class name
+ }
+ ObjCImpDecl = Actions.ActOnStartClassImplementation(
+ AtLoc, nameId, nameLoc, superClassId, superClassLoc, Attrs);
+
+ if (Tok.is(tok::l_brace)) // we have ivars
+ ParseObjCClassInstanceVariables(ObjCImpDecl, tok::objc_private, AtLoc);
+ else if (Tok.is(tok::less)) { // we have illegal '<' try to recover
+ Diag(Tok, diag::err_unexpected_protocol_qualifier);
+
+ SourceLocation protocolLAngleLoc, protocolRAngleLoc;
+ SmallVector<Decl *, 4> protocols;
+ SmallVector<SourceLocation, 4> protocolLocs;
+ (void)ParseObjCProtocolReferences(protocols, protocolLocs,
+ /*warnOnIncompleteProtocols=*/false,
+ /*ForObjCContainer=*/false,
+ protocolLAngleLoc, protocolRAngleLoc,
+ /*consumeLastToken=*/true);
+ }
+ }
+ assert(ObjCImpDecl);
+
+ SmallVector<Decl *, 8> DeclsInGroup;
+
+ {
+ ObjCImplParsingDataRAII ObjCImplParsing(*this, ObjCImpDecl);
+ while (!ObjCImplParsing.isFinished() && !isEofOrEom()) {
+ ParsedAttributes DeclAttrs(AttrFactory);
+ MaybeParseCXX11Attributes(DeclAttrs);
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+ if (DeclGroupPtrTy DGP =
+ ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs)) {
+ DeclGroupRef DG = DGP.get();
+ DeclsInGroup.append(DG.begin(), DG.end());
+ }
+ }
+ }
+
+ return Actions.ActOnFinishObjCImplementation(ObjCImpDecl, DeclsInGroup);
+}
+
+Parser::DeclGroupPtrTy
+Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) {
+ assert(Tok.isObjCAtKeyword(tok::objc_end) &&
+ "ParseObjCAtEndDeclaration(): Expected @end");
+ ConsumeToken(); // the "end" identifier
+ if (CurParsedObjCImpl)
+ CurParsedObjCImpl->finish(atEnd);
+ else
+ // missing @implementation
+ Diag(atEnd.getBegin(), diag::err_expected_objc_container);
+ return nullptr;
+}
+
+Parser::ObjCImplParsingDataRAII::~ObjCImplParsingDataRAII() {
+ if (!Finished) {
+ finish(P.Tok.getLocation());
+ if (P.isEofOrEom()) {
+ P.Diag(P.Tok, diag::err_objc_missing_end)
+ << FixItHint::CreateInsertion(P.Tok.getLocation(), "\n@end\n");
+ P.Diag(Dcl->getBeginLoc(), diag::note_objc_container_start)
+ << Sema::OCK_Implementation;
+ }
+ }
+ P.CurParsedObjCImpl = nullptr;
+ assert(LateParsedObjCMethods.empty());
+}
+
+void Parser::ObjCImplParsingDataRAII::finish(SourceRange AtEnd) {
+ assert(!Finished);
+ P.Actions.DefaultSynthesizeProperties(P.getCurScope(), Dcl, AtEnd.getBegin());
+ for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i)
+ P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i],
+ true/*Methods*/);
+
+ P.Actions.ActOnAtEnd(P.getCurScope(), AtEnd);
+
+ if (HasCFunction)
+ for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i)
+ P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i],
+ false/*c-functions*/);
+
+ /// Clear and free the cached objc methods.
+ for (LateParsedObjCMethodContainer::iterator
+ I = LateParsedObjCMethods.begin(),
+ E = LateParsedObjCMethods.end(); I != E; ++I)
+ delete *I;
+ LateParsedObjCMethods.clear();
+
+ Finished = true;
+}
+
+/// compatibility-alias-decl:
+/// @compatibility_alias alias-name class-name ';'
+///
+Decl *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) {
+ assert(Tok.isObjCAtKeyword(tok::objc_compatibility_alias) &&
+ "ParseObjCAtAliasDeclaration(): Expected @compatibility_alias");
+ ConsumeToken(); // consume compatibility_alias
+ if (expectIdentifier())
+ return nullptr;
+ IdentifierInfo *aliasId = Tok.getIdentifierInfo();
+ SourceLocation aliasLoc = ConsumeToken(); // consume alias-name
+ if (expectIdentifier())
+ return nullptr;
+ IdentifierInfo *classId = Tok.getIdentifierInfo();
+ SourceLocation classLoc = ConsumeToken(); // consume class-name;
+ ExpectAndConsume(tok::semi, diag::err_expected_after, "@compatibility_alias");
+ return Actions.ActOnCompatibilityAlias(atLoc, aliasId, aliasLoc,
+ classId, classLoc);
+}
+
+/// property-synthesis:
+/// @synthesize property-ivar-list ';'
+///
+/// property-ivar-list:
+/// property-ivar
+/// property-ivar-list ',' property-ivar
+///
+/// property-ivar:
+/// identifier
+/// identifier '=' identifier
+///
+Decl *Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) {
+ assert(Tok.isObjCAtKeyword(tok::objc_synthesize) &&
+ "ParseObjCPropertySynthesize(): Expected '@synthesize'");
+ ConsumeToken(); // consume synthesize
+
+ while (true) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCPropertyDefinition(getCurScope());
+ return nullptr;
+ }
+
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_synthesized_property_name);
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ IdentifierInfo *propertyIvar = nullptr;
+ IdentifierInfo *propertyId = Tok.getIdentifierInfo();
+ SourceLocation propertyLoc = ConsumeToken(); // consume property name
+ SourceLocation propertyIvarLoc;
+ if (TryConsumeToken(tok::equal)) {
+ // property '=' ivar-name
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCPropertySynthesizeIvar(getCurScope(), propertyId);
+ return nullptr;
+ }
+
+ if (expectIdentifier())
+ break;
+ propertyIvar = Tok.getIdentifierInfo();
+ propertyIvarLoc = ConsumeToken(); // consume ivar-name
+ }
+ Actions.ActOnPropertyImplDecl(
+ getCurScope(), atLoc, propertyLoc, true,
+ propertyId, propertyIvar, propertyIvarLoc,
+ ObjCPropertyQueryKind::OBJC_PR_query_unknown);
+ if (Tok.isNot(tok::comma))
+ break;
+ ConsumeToken(); // consume ','
+ }
+ ExpectAndConsume(tok::semi, diag::err_expected_after, "@synthesize");
+ return nullptr;
+}
+
+/// property-dynamic:
+/// @dynamic property-list
+///
+/// property-list:
+/// identifier
+/// property-list ',' identifier
+///
+Decl *Parser::ParseObjCPropertyDynamic(SourceLocation atLoc) {
+ assert(Tok.isObjCAtKeyword(tok::objc_dynamic) &&
+ "ParseObjCPropertyDynamic(): Expected '@dynamic'");
+ ConsumeToken(); // consume dynamic
+
+ bool isClassProperty = false;
+ if (Tok.is(tok::l_paren)) {
+ ConsumeParen();
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+
+ if (!II) {
+ Diag(Tok, diag::err_objc_expected_property_attr) << II;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ } else {
+ SourceLocation AttrName = ConsumeToken(); // consume attribute name
+ if (II->isStr("class")) {
+ isClassProperty = true;
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ } else
+ ConsumeParen();
+ } else {
+ Diag(AttrName, diag::err_objc_expected_property_attr) << II;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ }
+ }
+ }
+
+ while (true) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCPropertyDefinition(getCurScope());
+ return nullptr;
+ }
+
+ if (expectIdentifier()) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ IdentifierInfo *propertyId = Tok.getIdentifierInfo();
+ SourceLocation propertyLoc = ConsumeToken(); // consume property name
+ Actions.ActOnPropertyImplDecl(
+ getCurScope(), atLoc, propertyLoc, false,
+ propertyId, nullptr, SourceLocation(),
+ isClassProperty ? ObjCPropertyQueryKind::OBJC_PR_query_class :
+ ObjCPropertyQueryKind::OBJC_PR_query_unknown);
+
+ if (Tok.isNot(tok::comma))
+ break;
+ ConsumeToken(); // consume ','
+ }
+ ExpectAndConsume(tok::semi, diag::err_expected_after, "@dynamic");
+ return nullptr;
+}
+
+/// objc-throw-statement:
+/// throw expression[opt];
+///
+StmtResult Parser::ParseObjCThrowStmt(SourceLocation atLoc) {
+ ExprResult Res;
+ ConsumeToken(); // consume throw
+ if (Tok.isNot(tok::semi)) {
+ Res = ParseExpression();
+ if (Res.isInvalid()) {
+ SkipUntil(tok::semi);
+ return StmtError();
+ }
+ }
+ // consume ';'
+ ExpectAndConsume(tok::semi, diag::err_expected_after, "@throw");
+ return Actions.ActOnObjCAtThrowStmt(atLoc, Res.get(), getCurScope());
+}
+
+/// objc-synchronized-statement:
+/// @synchronized '(' expression ')' compound-statement
+///
+StmtResult
+Parser::ParseObjCSynchronizedStmt(SourceLocation atLoc) {
+ ConsumeToken(); // consume synchronized
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_expected_lparen_after) << "@synchronized";
+ return StmtError();
+ }
+
+ // The operand is surrounded with parentheses.
+ ConsumeParen(); // '('
+ ExprResult operand(ParseExpression());
+
+ if (Tok.is(tok::r_paren)) {
+ ConsumeParen(); // ')'
+ } else {
+ if (!operand.isInvalid())
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+
+ // Skip forward until we see a left brace, but don't consume it.
+ SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch);
+ }
+
+ // Require a compound statement.
+ if (Tok.isNot(tok::l_brace)) {
+ if (!operand.isInvalid())
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return StmtError();
+ }
+
+ // Check the @synchronized operand now.
+ if (!operand.isInvalid())
+ operand = Actions.ActOnObjCAtSynchronizedOperand(atLoc, operand.get());
+
+ // Parse the compound statement within a new scope.
+ ParseScope bodyScope(this, Scope::DeclScope | Scope::CompoundStmtScope);
+ StmtResult body(ParseCompoundStatementBody());
+ bodyScope.Exit();
+
+ // If there was a semantic or parse error earlier with the
+ // operand, fail now.
+ if (operand.isInvalid())
+ return StmtError();
+
+ if (body.isInvalid())
+ body = Actions.ActOnNullStmt(Tok.getLocation());
+
+ return Actions.ActOnObjCAtSynchronizedStmt(atLoc, operand.get(), body.get());
+}
+
+/// objc-try-catch-statement:
+/// @try compound-statement objc-catch-list[opt]
+/// @try compound-statement objc-catch-list[opt] @finally compound-statement
+///
+/// objc-catch-list:
+/// @catch ( parameter-declaration ) compound-statement
+/// objc-catch-list @catch ( catch-parameter-declaration ) compound-statement
+/// catch-parameter-declaration:
+/// parameter-declaration
+/// '...' [OBJC2]
+///
+StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) {
+ bool catch_or_finally_seen = false;
+
+ ConsumeToken(); // consume try
+ if (Tok.isNot(tok::l_brace)) {
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return StmtError();
+ }
+ StmtVector CatchStmts;
+ StmtResult FinallyStmt;
+ ParseScope TryScope(this, Scope::DeclScope | Scope::CompoundStmtScope);
+ StmtResult TryBody(ParseCompoundStatementBody());
+ TryScope.Exit();
+ if (TryBody.isInvalid())
+ TryBody = Actions.ActOnNullStmt(Tok.getLocation());
+
+ while (Tok.is(tok::at)) {
+ // At this point, we need to lookahead to determine if this @ is the start
+ // of an @catch or @finally. We don't want to consume the @ token if this
+ // is an @try or @encode or something else.
+ Token AfterAt = GetLookAheadToken(1);
+ if (!AfterAt.isObjCAtKeyword(tok::objc_catch) &&
+ !AfterAt.isObjCAtKeyword(tok::objc_finally))
+ break;
+
+ SourceLocation AtCatchFinallyLoc = ConsumeToken();
+ if (Tok.isObjCAtKeyword(tok::objc_catch)) {
+ Decl *FirstPart = nullptr;
+ ConsumeToken(); // consume catch
+ if (Tok.is(tok::l_paren)) {
+ ConsumeParen();
+ ParseScope CatchScope(this, Scope::DeclScope |
+ Scope::CompoundStmtScope |
+ Scope::AtCatchScope);
+ if (Tok.isNot(tok::ellipsis)) {
+ DeclSpec DS(AttrFactory);
+ ParseDeclarationSpecifiers(DS);
+ Declarator ParmDecl(DS, ParsedAttributesView::none(),
+ DeclaratorContext::ObjCCatch);
+ ParseDeclarator(ParmDecl);
+
+ // Inform the actions module about the declarator, so it
+ // gets added to the current scope.
+ FirstPart = Actions.ActOnObjCExceptionDecl(getCurScope(), ParmDecl);
+ } else
+ ConsumeToken(); // consume '...'
+
+ SourceLocation RParenLoc;
+
+ if (Tok.is(tok::r_paren))
+ RParenLoc = ConsumeParen();
+ else // Skip over garbage, until we get to ')'. Eat the ')'.
+ SkipUntil(tok::r_paren, StopAtSemi);
+
+ StmtResult CatchBody(true);
+ if (Tok.is(tok::l_brace))
+ CatchBody = ParseCompoundStatementBody();
+ else
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ if (CatchBody.isInvalid())
+ CatchBody = Actions.ActOnNullStmt(Tok.getLocation());
+
+ StmtResult Catch = Actions.ActOnObjCAtCatchStmt(AtCatchFinallyLoc,
+ RParenLoc,
+ FirstPart,
+ CatchBody.get());
+ if (!Catch.isInvalid())
+ CatchStmts.push_back(Catch.get());
+
+ } else {
+ Diag(AtCatchFinallyLoc, diag::err_expected_lparen_after)
+ << "@catch clause";
+ return StmtError();
+ }
+ catch_or_finally_seen = true;
+ } else {
+ assert(Tok.isObjCAtKeyword(tok::objc_finally) && "Lookahead confused?");
+ ConsumeToken(); // consume finally
+ ParseScope FinallyScope(this,
+ Scope::DeclScope | Scope::CompoundStmtScope);
+
+ bool ShouldCapture =
+ getTargetInfo().getTriple().isWindowsMSVCEnvironment();
+ if (ShouldCapture)
+ Actions.ActOnCapturedRegionStart(Tok.getLocation(), getCurScope(),
+ CR_ObjCAtFinally, 1);
+
+ StmtResult FinallyBody(true);
+ if (Tok.is(tok::l_brace))
+ FinallyBody = ParseCompoundStatementBody();
+ else
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+
+ if (FinallyBody.isInvalid()) {
+ FinallyBody = Actions.ActOnNullStmt(Tok.getLocation());
+ if (ShouldCapture)
+ Actions.ActOnCapturedRegionError();
+ } else if (ShouldCapture) {
+ FinallyBody = Actions.ActOnCapturedRegionEnd(FinallyBody.get());
+ }
+
+ FinallyStmt = Actions.ActOnObjCAtFinallyStmt(AtCatchFinallyLoc,
+ FinallyBody.get());
+ catch_or_finally_seen = true;
+ break;
+ }
+ }
+ if (!catch_or_finally_seen) {
+ Diag(atLoc, diag::err_missing_catch_finally);
+ return StmtError();
+ }
+
+ return Actions.ActOnObjCAtTryStmt(atLoc, TryBody.get(),
+ CatchStmts,
+ FinallyStmt.get());
+}
+
+/// objc-autoreleasepool-statement:
+/// @autoreleasepool compound-statement
+///
+StmtResult
+Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) {
+ ConsumeToken(); // consume autoreleasepool
+ if (Tok.isNot(tok::l_brace)) {
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return StmtError();
+ }
+ // Enter a scope to hold everything within the compound stmt. Compound
+ // statements can always hold declarations.
+ ParseScope BodyScope(this, Scope::DeclScope | Scope::CompoundStmtScope);
+
+ StmtResult AutoreleasePoolBody(ParseCompoundStatementBody());
+
+ BodyScope.Exit();
+ if (AutoreleasePoolBody.isInvalid())
+ AutoreleasePoolBody = Actions.ActOnNullStmt(Tok.getLocation());
+ return Actions.ActOnObjCAutoreleasePoolStmt(atLoc,
+ AutoreleasePoolBody.get());
+}
+
+/// StashAwayMethodOrFunctionBodyTokens - Consume the tokens and store them
+/// for later parsing.
+void Parser::StashAwayMethodOrFunctionBodyTokens(Decl *MDecl) {
+ if (SkipFunctionBodies && (!MDecl || Actions.canSkipFunctionBody(MDecl)) &&
+ trySkippingFunctionBody()) {
+ Actions.ActOnSkippedFunctionBody(MDecl);
+ return;
+ }
+
+ LexedMethod* LM = new LexedMethod(this, MDecl);
+ CurParsedObjCImpl->LateParsedObjCMethods.push_back(LM);
+ CachedTokens &Toks = LM->Toks;
+ // Begin by storing the '{' or 'try' or ':' token.
+ Toks.push_back(Tok);
+ if (Tok.is(tok::kw_try)) {
+ ConsumeToken();
+ if (Tok.is(tok::colon)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ while (Tok.isNot(tok::l_brace)) {
+ ConsumeAndStoreUntil(tok::l_paren, Toks, /*StopAtSemi=*/false);
+ ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false);
+ }
+ }
+ Toks.push_back(Tok); // also store '{'
+ }
+ else if (Tok.is(tok::colon)) {
+ ConsumeToken();
+ // FIXME: This is wrong, due to C++11 braced initialization.
+ while (Tok.isNot(tok::l_brace)) {
+ ConsumeAndStoreUntil(tok::l_paren, Toks, /*StopAtSemi=*/false);
+ ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false);
+ }
+ Toks.push_back(Tok); // also store '{'
+ }
+ ConsumeBrace();
+ // Consume everything up to (and including) the matching right brace.
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ while (Tok.is(tok::kw_catch)) {
+ ConsumeAndStoreUntil(tok::l_brace, Toks, /*StopAtSemi=*/false);
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ }
+}
+
+/// objc-method-def: objc-method-proto ';'[opt] '{' body '}'
+///
+Decl *Parser::ParseObjCMethodDefinition() {
+ Decl *MDecl = ParseObjCMethodPrototype();
+
+ PrettyDeclStackTraceEntry CrashInfo(Actions.Context, MDecl, Tok.getLocation(),
+ "parsing Objective-C method");
+
+ // parse optional ';'
+ if (Tok.is(tok::semi)) {
+ if (CurParsedObjCImpl) {
+ Diag(Tok, diag::warn_semicolon_before_method_body)
+ << FixItHint::CreateRemoval(Tok.getLocation());
+ }
+ ConsumeToken();
+ }
+
+ // We should have an opening brace now.
+ if (Tok.isNot(tok::l_brace)) {
+ Diag(Tok, diag::err_expected_method_body);
+
+ // Skip over garbage, until we get to '{'. Don't eat the '{'.
+ SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch);
+
+ // If we didn't find the '{', bail out.
+ if (Tok.isNot(tok::l_brace))
+ return nullptr;
+ }
+
+ if (!MDecl) {
+ ConsumeBrace();
+ SkipUntil(tok::r_brace);
+ return nullptr;
+ }
+
+ // Allow the rest of sema to find private method decl implementations.
+ Actions.AddAnyMethodToGlobalPool(MDecl);
+ assert (CurParsedObjCImpl
+ && "ParseObjCMethodDefinition - Method out of @implementation");
+ // Consume the tokens and store them for later parsing.
+ StashAwayMethodOrFunctionBodyTokens(MDecl);
+ return MDecl;
+}
+
+StmtResult Parser::ParseObjCAtStatement(SourceLocation AtLoc,
+ ParsedStmtContext StmtCtx) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCAtStatement(getCurScope());
+ return StmtError();
+ }
+
+ if (Tok.isObjCAtKeyword(tok::objc_try))
+ return ParseObjCTryStmt(AtLoc);
+
+ if (Tok.isObjCAtKeyword(tok::objc_throw))
+ return ParseObjCThrowStmt(AtLoc);
+
+ if (Tok.isObjCAtKeyword(tok::objc_synchronized))
+ return ParseObjCSynchronizedStmt(AtLoc);
+
+ if (Tok.isObjCAtKeyword(tok::objc_autoreleasepool))
+ return ParseObjCAutoreleasePoolStmt(AtLoc);
+
+ if (Tok.isObjCAtKeyword(tok::objc_import) &&
+ getLangOpts().DebuggerSupport) {
+ SkipUntil(tok::semi);
+ return Actions.ActOnNullStmt(Tok.getLocation());
+ }
+
+ ExprStatementTokLoc = AtLoc;
+ ExprResult Res(ParseExpressionWithLeadingAt(AtLoc));
+ if (Res.isInvalid()) {
+ // If the expression is invalid, skip ahead to the next semicolon. Not
+ // doing this opens us up to the possibility of infinite loops if
+ // ParseExpression does not consume any tokens.
+ SkipUntil(tok::semi);
+ return StmtError();
+ }
+
+ // Otherwise, eat the semicolon.
+ ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
+ return handleExprStmt(Res, StmtCtx);
+}
+
+ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) {
+ switch (Tok.getKind()) {
+ case tok::code_completion:
+ cutOffParsing();
+ Actions.CodeCompleteObjCAtExpression(getCurScope());
+ return ExprError();
+
+ case tok::minus:
+ case tok::plus: {
+ tok::TokenKind Kind = Tok.getKind();
+ SourceLocation OpLoc = ConsumeToken();
+
+ if (!Tok.is(tok::numeric_constant)) {
+ const char *Symbol = nullptr;
+ switch (Kind) {
+ case tok::minus: Symbol = "-"; break;
+ case tok::plus: Symbol = "+"; break;
+ default: llvm_unreachable("missing unary operator case");
+ }
+ Diag(Tok, diag::err_nsnumber_nonliteral_unary)
+ << Symbol;
+ return ExprError();
+ }
+
+ ExprResult Lit(Actions.ActOnNumericConstant(Tok));
+ if (Lit.isInvalid()) {
+ return Lit;
+ }
+ ConsumeToken(); // Consume the literal token.
+
+ Lit = Actions.ActOnUnaryOp(getCurScope(), OpLoc, Kind, Lit.get());
+ if (Lit.isInvalid())
+ return Lit;
+
+ return ParsePostfixExpressionSuffix(
+ Actions.BuildObjCNumericLiteral(AtLoc, Lit.get()));
+ }
+
+ case tok::string_literal: // primary-expression: string-literal
+ case tok::wide_string_literal:
+ return ParsePostfixExpressionSuffix(ParseObjCStringLiteral(AtLoc));
+
+ case tok::char_constant:
+ return ParsePostfixExpressionSuffix(ParseObjCCharacterLiteral(AtLoc));
+
+ case tok::numeric_constant:
+ return ParsePostfixExpressionSuffix(ParseObjCNumericLiteral(AtLoc));
+
+ case tok::kw_true: // Objective-C++, etc.
+ case tok::kw___objc_yes: // c/c++/objc/objc++ __objc_yes
+ return ParsePostfixExpressionSuffix(ParseObjCBooleanLiteral(AtLoc, true));
+ case tok::kw_false: // Objective-C++, etc.
+ case tok::kw___objc_no: // c/c++/objc/objc++ __objc_no
+ return ParsePostfixExpressionSuffix(ParseObjCBooleanLiteral(AtLoc, false));
+
+ case tok::l_square:
+ // Objective-C array literal
+ return ParsePostfixExpressionSuffix(ParseObjCArrayLiteral(AtLoc));
+
+ case tok::l_brace:
+ // Objective-C dictionary literal
+ return ParsePostfixExpressionSuffix(ParseObjCDictionaryLiteral(AtLoc));
+
+ case tok::l_paren:
+ // Objective-C boxed expression
+ return ParsePostfixExpressionSuffix(ParseObjCBoxedExpr(AtLoc));
+
+ default:
+ if (Tok.getIdentifierInfo() == nullptr)
+ return ExprError(Diag(AtLoc, diag::err_unexpected_at));
+
+ switch (Tok.getIdentifierInfo()->getObjCKeywordID()) {
+ case tok::objc_encode:
+ return ParsePostfixExpressionSuffix(ParseObjCEncodeExpression(AtLoc));
+ case tok::objc_protocol:
+ return ParsePostfixExpressionSuffix(ParseObjCProtocolExpression(AtLoc));
+ case tok::objc_selector:
+ return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc));
+ case tok::objc_available:
+ return ParseAvailabilityCheckExpr(AtLoc);
+ default: {
+ const char *str = nullptr;
+ // Only provide the @try/@finally/@autoreleasepool fixit when we're sure
+ // that this is a proper statement where such directives could actually
+ // occur.
+ if (GetLookAheadToken(1).is(tok::l_brace) &&
+ ExprStatementTokLoc == AtLoc) {
+ char ch = Tok.getIdentifierInfo()->getNameStart()[0];
+ str =
+ ch == 't' ? "try"
+ : (ch == 'f' ? "finally"
+ : (ch == 'a' ? "autoreleasepool" : nullptr));
+ }
+ if (str) {
+ SourceLocation kwLoc = Tok.getLocation();
+ return ExprError(Diag(AtLoc, diag::err_unexpected_at) <<
+ FixItHint::CreateReplacement(kwLoc, str));
+ }
+ else
+ return ExprError(Diag(AtLoc, diag::err_unexpected_at));
+ }
+ }
+ }
+}
+
+/// Parse the receiver of an Objective-C++ message send.
+///
+/// This routine parses the receiver of a message send in
+/// Objective-C++ either as a type or as an expression. Note that this
+/// routine must not be called to parse a send to 'super', since it
+/// has no way to return such a result.
+///
+/// \param IsExpr Whether the receiver was parsed as an expression.
+///
+/// \param TypeOrExpr If the receiver was parsed as an expression (\c
+/// IsExpr is true), the parsed expression. If the receiver was parsed
+/// as a type (\c IsExpr is false), the parsed type.
+///
+/// \returns True if an error occurred during parsing or semantic
+/// analysis, in which case the arguments do not have valid
+/// values. Otherwise, returns false for a successful parse.
+///
+/// objc-receiver: [C++]
+/// 'super' [not parsed here]
+/// expression
+/// simple-type-specifier
+/// typename-specifier
+bool Parser::ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr) {
+ InMessageExpressionRAIIObject InMessage(*this, true);
+
+ if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_typename,
+ tok::annot_cxxscope))
+ TryAnnotateTypeOrScopeToken();
+
+ if (!Actions.isSimpleTypeSpecifier(Tok.getKind())) {
+ // objc-receiver:
+ // expression
+ // Make sure any typos in the receiver are corrected or diagnosed, so that
+ // proper recovery can happen. FIXME: Perhaps filter the corrected expr to
+ // only the things that are valid ObjC receivers?
+ ExprResult Receiver = Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ if (Receiver.isInvalid())
+ return true;
+
+ IsExpr = true;
+ TypeOrExpr = Receiver.get();
+ return false;
+ }
+
+ // objc-receiver:
+ // typename-specifier
+ // simple-type-specifier
+ // expression (that starts with one of the above)
+ DeclSpec DS(AttrFactory);
+ ParseCXXSimpleTypeSpecifier(DS);
+
+ if (Tok.is(tok::l_paren)) {
+ // If we see an opening parentheses at this point, we are
+ // actually parsing an expression that starts with a
+ // function-style cast, e.g.,
+ //
+ // postfix-expression:
+ // simple-type-specifier ( expression-list [opt] )
+ // typename-specifier ( expression-list [opt] )
+ //
+ // Parse the remainder of this case, then the (optional)
+ // postfix-expression suffix, followed by the (optional)
+ // right-hand side of the binary expression. We have an
+ // instance method.
+ ExprResult Receiver = ParseCXXTypeConstructExpression(DS);
+ if (!Receiver.isInvalid())
+ Receiver = ParsePostfixExpressionSuffix(Receiver.get());
+ if (!Receiver.isInvalid())
+ Receiver = ParseRHSOfBinaryExpression(Receiver.get(), prec::Comma);
+ if (Receiver.isInvalid())
+ return true;
+
+ IsExpr = true;
+ TypeOrExpr = Receiver.get();
+ return false;
+ }
+
+ // We have a class message. Turn the simple-type-specifier or
+ // typename-specifier we parsed into a type and parse the
+ // remainder of the class message.
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TypeName);
+ TypeResult Type = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ if (Type.isInvalid())
+ return true;
+
+ IsExpr = false;
+ TypeOrExpr = Type.get().getAsOpaquePtr();
+ return false;
+}
+
+/// Determine whether the parser is currently referring to a an
+/// Objective-C message send, using a simplified heuristic to avoid overhead.
+///
+/// This routine will only return true for a subset of valid message-send
+/// expressions.
+bool Parser::isSimpleObjCMessageExpression() {
+ assert(Tok.is(tok::l_square) && getLangOpts().ObjC &&
+ "Incorrect start for isSimpleObjCMessageExpression");
+ return GetLookAheadToken(1).is(tok::identifier) &&
+ GetLookAheadToken(2).is(tok::identifier);
+}
+
+bool Parser::isStartOfObjCClassMessageMissingOpenBracket() {
+ if (!getLangOpts().ObjC || !NextToken().is(tok::identifier) ||
+ InMessageExpression)
+ return false;
+
+ TypeResult Type;
+
+ if (Tok.is(tok::annot_typename))
+ Type = getTypeAnnotation(Tok);
+ else if (Tok.is(tok::identifier))
+ Type = Actions.getTypeName(*Tok.getIdentifierInfo(), Tok.getLocation(),
+ getCurScope());
+ else
+ return false;
+
+ // FIXME: Should not be querying properties of types from the parser.
+ if (Type.isUsable() && Type.get().get()->isObjCObjectOrInterfaceType()) {
+ const Token &AfterNext = GetLookAheadToken(2);
+ if (AfterNext.isOneOf(tok::colon, tok::r_square)) {
+ if (Tok.is(tok::identifier))
+ TryAnnotateTypeOrScopeToken();
+
+ return Tok.is(tok::annot_typename);
+ }
+ }
+
+ return false;
+}
+
+/// objc-message-expr:
+/// '[' objc-receiver objc-message-args ']'
+///
+/// objc-receiver: [C]
+/// 'super'
+/// expression
+/// class-name
+/// type-name
+///
+ExprResult Parser::ParseObjCMessageExpression() {
+ assert(Tok.is(tok::l_square) && "'[' expected");
+ SourceLocation LBracLoc = ConsumeBracket(); // consume '['
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCMessageReceiver(getCurScope());
+ return ExprError();
+ }
+
+ InMessageExpressionRAIIObject InMessage(*this, true);
+
+ if (getLangOpts().CPlusPlus) {
+ // We completely separate the C and C++ cases because C++ requires
+ // more complicated (read: slower) parsing.
+
+ // Handle send to super.
+ // FIXME: This doesn't benefit from the same typo-correction we
+ // get in Objective-C.
+ if (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_super &&
+ NextToken().isNot(tok::period) && getCurScope()->isInObjcMethodScope())
+ return ParseObjCMessageExpressionBody(LBracLoc, ConsumeToken(), nullptr,
+ nullptr);
+
+ // Parse the receiver, which is either a type or an expression.
+ bool IsExpr;
+ void *TypeOrExpr = nullptr;
+ if (ParseObjCXXMessageReceiver(IsExpr, TypeOrExpr)) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ if (IsExpr)
+ return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(), nullptr,
+ static_cast<Expr *>(TypeOrExpr));
+
+ return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(),
+ ParsedType::getFromOpaquePtr(TypeOrExpr),
+ nullptr);
+ }
+
+ if (Tok.is(tok::identifier)) {
+ IdentifierInfo *Name = Tok.getIdentifierInfo();
+ SourceLocation NameLoc = Tok.getLocation();
+ ParsedType ReceiverType;
+ switch (Actions.getObjCMessageKind(getCurScope(), Name, NameLoc,
+ Name == Ident_super,
+ NextToken().is(tok::period),
+ ReceiverType)) {
+ case Sema::ObjCSuperMessage:
+ return ParseObjCMessageExpressionBody(LBracLoc, ConsumeToken(), nullptr,
+ nullptr);
+
+ case Sema::ObjCClassMessage:
+ if (!ReceiverType) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ ConsumeToken(); // the type name
+
+ // Parse type arguments and protocol qualifiers.
+ if (Tok.is(tok::less)) {
+ SourceLocation NewEndLoc;
+ TypeResult NewReceiverType
+ = parseObjCTypeArgsAndProtocolQualifiers(NameLoc, ReceiverType,
+ /*consumeLastToken=*/true,
+ NewEndLoc);
+ if (!NewReceiverType.isUsable()) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ ReceiverType = NewReceiverType.get();
+ }
+
+ return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(),
+ ReceiverType, nullptr);
+
+ case Sema::ObjCInstanceMessage:
+ // Fall through to parse an expression.
+ break;
+ }
+ }
+
+ // Otherwise, an arbitrary expression can be the receiver of a send.
+ ExprResult Res = Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ if (Res.isInvalid()) {
+ SkipUntil(tok::r_square, StopAtSemi);
+ return Res;
+ }
+
+ return ParseObjCMessageExpressionBody(LBracLoc, SourceLocation(), nullptr,
+ Res.get());
+}
+
+/// Parse the remainder of an Objective-C message following the
+/// '[' objc-receiver.
+///
+/// This routine handles sends to super, class messages (sent to a
+/// class name), and instance messages (sent to an object), and the
+/// target is represented by \p SuperLoc, \p ReceiverType, or \p
+/// ReceiverExpr, respectively. Only one of these parameters may have
+/// a valid value.
+///
+/// \param LBracLoc The location of the opening '['.
+///
+/// \param SuperLoc If this is a send to 'super', the location of the
+/// 'super' keyword that indicates a send to the superclass.
+///
+/// \param ReceiverType If this is a class message, the type of the
+/// class we are sending a message to.
+///
+/// \param ReceiverExpr If this is an instance message, the expression
+/// used to compute the receiver object.
+///
+/// objc-message-args:
+/// objc-selector
+/// objc-keywordarg-list
+///
+/// objc-keywordarg-list:
+/// objc-keywordarg
+/// objc-keywordarg-list objc-keywordarg
+///
+/// objc-keywordarg:
+/// selector-name[opt] ':' objc-keywordexpr
+///
+/// objc-keywordexpr:
+/// nonempty-expr-list
+///
+/// nonempty-expr-list:
+/// assignment-expression
+/// nonempty-expr-list , assignment-expression
+///
+ExprResult
+Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc,
+ SourceLocation SuperLoc,
+ ParsedType ReceiverType,
+ Expr *ReceiverExpr) {
+ InMessageExpressionRAIIObject InMessage(*this, true);
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ if (SuperLoc.isValid())
+ Actions.CodeCompleteObjCSuperMessage(getCurScope(), SuperLoc,
+ std::nullopt, false);
+ else if (ReceiverType)
+ Actions.CodeCompleteObjCClassMessage(getCurScope(), ReceiverType,
+ std::nullopt, false);
+ else
+ Actions.CodeCompleteObjCInstanceMessage(getCurScope(), ReceiverExpr,
+ std::nullopt, false);
+ return ExprError();
+ }
+
+ // Parse objc-selector
+ SourceLocation Loc;
+ IdentifierInfo *selIdent = ParseObjCSelectorPiece(Loc);
+
+ SmallVector<IdentifierInfo *, 12> KeyIdents;
+ SmallVector<SourceLocation, 12> KeyLocs;
+ ExprVector KeyExprs;
+
+ if (Tok.is(tok::colon)) {
+ while (true) {
+ // Each iteration parses a single keyword argument.
+ KeyIdents.push_back(selIdent);
+ KeyLocs.push_back(Loc);
+
+ if (ExpectAndConsume(tok::colon)) {
+ // We must manually skip to a ']', otherwise the expression skipper will
+ // stop at the ']' when it skips to the ';'. We want it to skip beyond
+ // the enclosing expression.
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ /// Parse the expression after ':'
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ if (SuperLoc.isValid())
+ Actions.CodeCompleteObjCSuperMessage(getCurScope(), SuperLoc,
+ KeyIdents,
+ /*AtArgumentExpression=*/true);
+ else if (ReceiverType)
+ Actions.CodeCompleteObjCClassMessage(getCurScope(), ReceiverType,
+ KeyIdents,
+ /*AtArgumentExpression=*/true);
+ else
+ Actions.CodeCompleteObjCInstanceMessage(getCurScope(), ReceiverExpr,
+ KeyIdents,
+ /*AtArgumentExpression=*/true);
+
+ return ExprError();
+ }
+
+ ExprResult Expr;
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+ Expr = ParseBraceInitializer();
+ } else
+ Expr = ParseAssignmentExpression();
+
+ ExprResult Res(Expr);
+ if (Res.isInvalid()) {
+ // We must manually skip to a ']', otherwise the expression skipper will
+ // stop at the ']' when it skips to the ';'. We want it to skip beyond
+ // the enclosing expression.
+ SkipUntil(tok::r_square, StopAtSemi);
+ return Res;
+ }
+
+ // We have a valid expression.
+ KeyExprs.push_back(Res.get());
+
+ // Code completion after each argument.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ if (SuperLoc.isValid())
+ Actions.CodeCompleteObjCSuperMessage(getCurScope(), SuperLoc,
+ KeyIdents,
+ /*AtArgumentExpression=*/false);
+ else if (ReceiverType)
+ Actions.CodeCompleteObjCClassMessage(getCurScope(), ReceiverType,
+ KeyIdents,
+ /*AtArgumentExpression=*/false);
+ else
+ Actions.CodeCompleteObjCInstanceMessage(getCurScope(), ReceiverExpr,
+ KeyIdents,
+ /*AtArgumentExpression=*/false);
+ return ExprError();
+ }
+
+ // Check for another keyword selector.
+ selIdent = ParseObjCSelectorPiece(Loc);
+ if (!selIdent && Tok.isNot(tok::colon))
+ break;
+ // We have a selector or a colon, continue parsing.
+ }
+ // Parse the, optional, argument list, comma separated.
+ while (Tok.is(tok::comma)) {
+ SourceLocation commaLoc = ConsumeToken(); // Eat the ','.
+ /// Parse the expression after ','
+ ExprResult Res(ParseAssignmentExpression());
+ if (Tok.is(tok::colon))
+ Res = Actions.CorrectDelayedTyposInExpr(Res);
+ if (Res.isInvalid()) {
+ if (Tok.is(tok::colon)) {
+ Diag(commaLoc, diag::note_extra_comma_message_arg) <<
+ FixItHint::CreateRemoval(commaLoc);
+ }
+ // We must manually skip to a ']', otherwise the expression skipper will
+ // stop at the ']' when it skips to the ';'. We want it to skip beyond
+ // the enclosing expression.
+ SkipUntil(tok::r_square, StopAtSemi);
+ return Res;
+ }
+
+ // We have a valid expression.
+ KeyExprs.push_back(Res.get());
+ }
+ } else if (!selIdent) {
+ Diag(Tok, diag::err_expected) << tok::identifier; // missing selector name.
+
+ // We must manually skip to a ']', otherwise the expression skipper will
+ // stop at the ']' when it skips to the ';'. We want it to skip beyond
+ // the enclosing expression.
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ if (Tok.isNot(tok::r_square)) {
+ Diag(Tok, diag::err_expected)
+ << (Tok.is(tok::identifier) ? tok::colon : tok::r_square);
+ // We must manually skip to a ']', otherwise the expression skipper will
+ // stop at the ']' when it skips to the ';'. We want it to skip beyond
+ // the enclosing expression.
+ SkipUntil(tok::r_square, StopAtSemi);
+ return ExprError();
+ }
+
+ SourceLocation RBracLoc = ConsumeBracket(); // consume ']'
+
+ unsigned nKeys = KeyIdents.size();
+ if (nKeys == 0) {
+ KeyIdents.push_back(selIdent);
+ KeyLocs.push_back(Loc);
+ }
+ Selector Sel = PP.getSelectorTable().getSelector(nKeys, &KeyIdents[0]);
+
+ if (SuperLoc.isValid())
+ return Actions.ActOnSuperMessage(getCurScope(), SuperLoc, Sel,
+ LBracLoc, KeyLocs, RBracLoc, KeyExprs);
+ else if (ReceiverType)
+ return Actions.ActOnClassMessage(getCurScope(), ReceiverType, Sel,
+ LBracLoc, KeyLocs, RBracLoc, KeyExprs);
+ return Actions.ActOnInstanceMessage(getCurScope(), ReceiverExpr, Sel,
+ LBracLoc, KeyLocs, RBracLoc, KeyExprs);
+}
+
+ExprResult Parser::ParseObjCStringLiteral(SourceLocation AtLoc) {
+ ExprResult Res(ParseStringLiteralExpression());
+ if (Res.isInvalid()) return Res;
+
+ // @"foo" @"bar" is a valid concatenated string. Eat any subsequent string
+ // expressions. At this point, we know that the only valid thing that starts
+ // with '@' is an @"".
+ SmallVector<SourceLocation, 4> AtLocs;
+ ExprVector AtStrings;
+ AtLocs.push_back(AtLoc);
+ AtStrings.push_back(Res.get());
+
+ while (Tok.is(tok::at)) {
+ AtLocs.push_back(ConsumeToken()); // eat the @.
+
+ // Invalid unless there is a string literal.
+ if (!isTokenStringLiteral())
+ return ExprError(Diag(Tok, diag::err_objc_concat_string));
+
+ ExprResult Lit(ParseStringLiteralExpression());
+ if (Lit.isInvalid())
+ return Lit;
+
+ AtStrings.push_back(Lit.get());
+ }
+
+ return Actions.ParseObjCStringLiteral(AtLocs.data(), AtStrings);
+}
+
+/// ParseObjCBooleanLiteral -
+/// objc-scalar-literal : '@' boolean-keyword
+/// ;
+/// boolean-keyword: 'true' | 'false' | '__objc_yes' | '__objc_no'
+/// ;
+ExprResult Parser::ParseObjCBooleanLiteral(SourceLocation AtLoc,
+ bool ArgValue) {
+ SourceLocation EndLoc = ConsumeToken(); // consume the keyword.
+ return Actions.ActOnObjCBoolLiteral(AtLoc, EndLoc, ArgValue);
+}
+
+/// ParseObjCCharacterLiteral -
+/// objc-scalar-literal : '@' character-literal
+/// ;
+ExprResult Parser::ParseObjCCharacterLiteral(SourceLocation AtLoc) {
+ ExprResult Lit(Actions.ActOnCharacterConstant(Tok));
+ if (Lit.isInvalid()) {
+ return Lit;
+ }
+ ConsumeToken(); // Consume the literal token.
+ return Actions.BuildObjCNumericLiteral(AtLoc, Lit.get());
+}
+
+/// ParseObjCNumericLiteral -
+/// objc-scalar-literal : '@' scalar-literal
+/// ;
+/// scalar-literal : | numeric-constant /* any numeric constant. */
+/// ;
+ExprResult Parser::ParseObjCNumericLiteral(SourceLocation AtLoc) {
+ ExprResult Lit(Actions.ActOnNumericConstant(Tok));
+ if (Lit.isInvalid()) {
+ return Lit;
+ }
+ ConsumeToken(); // Consume the literal token.
+ return Actions.BuildObjCNumericLiteral(AtLoc, Lit.get());
+}
+
+/// ParseObjCBoxedExpr -
+/// objc-box-expression:
+/// @( assignment-expression )
+ExprResult
+Parser::ParseObjCBoxedExpr(SourceLocation AtLoc) {
+ if (Tok.isNot(tok::l_paren))
+ return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@");
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ ExprResult ValueExpr(ParseAssignmentExpression());
+ if (T.consumeClose())
+ return ExprError();
+
+ if (ValueExpr.isInvalid())
+ return ExprError();
+
+ // Wrap the sub-expression in a parenthesized expression, to distinguish
+ // a boxed expression from a literal.
+ SourceLocation LPLoc = T.getOpenLocation(), RPLoc = T.getCloseLocation();
+ ValueExpr = Actions.ActOnParenExpr(LPLoc, RPLoc, ValueExpr.get());
+ return Actions.BuildObjCBoxedExpr(SourceRange(AtLoc, RPLoc),
+ ValueExpr.get());
+}
+
+ExprResult Parser::ParseObjCArrayLiteral(SourceLocation AtLoc) {
+ ExprVector ElementExprs; // array elements.
+ ConsumeBracket(); // consume the l_square.
+
+ bool HasInvalidEltExpr = false;
+ while (Tok.isNot(tok::r_square)) {
+ // Parse list of array element expressions (all must be id types).
+ ExprResult Res(ParseAssignmentExpression());
+ if (Res.isInvalid()) {
+ // We must manually skip to a ']', otherwise the expression skipper will
+ // stop at the ']' when it skips to the ';'. We want it to skip beyond
+ // the enclosing expression.
+ SkipUntil(tok::r_square, StopAtSemi);
+ return Res;
+ }
+
+ Res = Actions.CorrectDelayedTyposInExpr(Res.get());
+ if (Res.isInvalid())
+ HasInvalidEltExpr = true;
+
+ // Parse the ellipsis that indicates a pack expansion.
+ if (Tok.is(tok::ellipsis))
+ Res = Actions.ActOnPackExpansion(Res.get(), ConsumeToken());
+ if (Res.isInvalid())
+ HasInvalidEltExpr = true;
+
+ ElementExprs.push_back(Res.get());
+
+ if (Tok.is(tok::comma))
+ ConsumeToken(); // Eat the ','.
+ else if (Tok.isNot(tok::r_square))
+ return ExprError(Diag(Tok, diag::err_expected_either) << tok::r_square
+ << tok::comma);
+ }
+ SourceLocation EndLoc = ConsumeBracket(); // location of ']'
+
+ if (HasInvalidEltExpr)
+ return ExprError();
+
+ MultiExprArg Args(ElementExprs);
+ return Actions.BuildObjCArrayLiteral(SourceRange(AtLoc, EndLoc), Args);
+}
+
+ExprResult Parser::ParseObjCDictionaryLiteral(SourceLocation AtLoc) {
+ SmallVector<ObjCDictionaryElement, 4> Elements; // dictionary elements.
+ ConsumeBrace(); // consume the l_square.
+ bool HasInvalidEltExpr = false;
+ while (Tok.isNot(tok::r_brace)) {
+ // Parse the comma separated key : value expressions.
+ ExprResult KeyExpr;
+ {
+ ColonProtectionRAIIObject X(*this);
+ KeyExpr = ParseAssignmentExpression();
+ if (KeyExpr.isInvalid()) {
+ // We must manually skip to a '}', otherwise the expression skipper will
+ // stop at the '}' when it skips to the ';'. We want it to skip beyond
+ // the enclosing expression.
+ SkipUntil(tok::r_brace, StopAtSemi);
+ return KeyExpr;
+ }
+ }
+
+ if (ExpectAndConsume(tok::colon)) {
+ SkipUntil(tok::r_brace, StopAtSemi);
+ return ExprError();
+ }
+
+ ExprResult ValueExpr(ParseAssignmentExpression());
+ if (ValueExpr.isInvalid()) {
+ // We must manually skip to a '}', otherwise the expression skipper will
+ // stop at the '}' when it skips to the ';'. We want it to skip beyond
+ // the enclosing expression.
+ SkipUntil(tok::r_brace, StopAtSemi);
+ return ValueExpr;
+ }
+
+ // Check the key and value for possible typos
+ KeyExpr = Actions.CorrectDelayedTyposInExpr(KeyExpr.get());
+ ValueExpr = Actions.CorrectDelayedTyposInExpr(ValueExpr.get());
+ if (KeyExpr.isInvalid() || ValueExpr.isInvalid())
+ HasInvalidEltExpr = true;
+
+ // Parse the ellipsis that designates this as a pack expansion. Do not
+ // ActOnPackExpansion here, leave it to template instantiation time where
+ // we can get better diagnostics.
+ SourceLocation EllipsisLoc;
+ if (getLangOpts().CPlusPlus)
+ TryConsumeToken(tok::ellipsis, EllipsisLoc);
+
+ // We have a valid expression. Collect it in a vector so we can
+ // build the argument list.
+ ObjCDictionaryElement Element = {KeyExpr.get(), ValueExpr.get(),
+ EllipsisLoc, std::nullopt};
+ Elements.push_back(Element);
+
+ if (!TryConsumeToken(tok::comma) && Tok.isNot(tok::r_brace))
+ return ExprError(Diag(Tok, diag::err_expected_either) << tok::r_brace
+ << tok::comma);
+ }
+ SourceLocation EndLoc = ConsumeBrace();
+
+ if (HasInvalidEltExpr)
+ return ExprError();
+
+ // Create the ObjCDictionaryLiteral.
+ return Actions.BuildObjCDictionaryLiteral(SourceRange(AtLoc, EndLoc),
+ Elements);
+}
+
+/// objc-encode-expression:
+/// \@encode ( type-name )
+ExprResult
+Parser::ParseObjCEncodeExpression(SourceLocation AtLoc) {
+ assert(Tok.isObjCAtKeyword(tok::objc_encode) && "Not an @encode expression!");
+
+ SourceLocation EncLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::l_paren))
+ return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@encode");
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ TypeResult Ty = ParseTypeName();
+
+ T.consumeClose();
+
+ if (Ty.isInvalid())
+ return ExprError();
+
+ return Actions.ParseObjCEncodeExpression(AtLoc, EncLoc, T.getOpenLocation(),
+ Ty.get(), T.getCloseLocation());
+}
+
+/// objc-protocol-expression
+/// \@protocol ( protocol-name )
+ExprResult
+Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) {
+ SourceLocation ProtoLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::l_paren))
+ return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@protocol");
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ if (expectIdentifier())
+ return ExprError();
+
+ IdentifierInfo *protocolId = Tok.getIdentifierInfo();
+ SourceLocation ProtoIdLoc = ConsumeToken();
+
+ T.consumeClose();
+
+ return Actions.ParseObjCProtocolExpression(protocolId, AtLoc, ProtoLoc,
+ T.getOpenLocation(), ProtoIdLoc,
+ T.getCloseLocation());
+}
+
+/// objc-selector-expression
+/// @selector '(' '('[opt] objc-keyword-selector ')'[opt] ')'
+ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) {
+ SourceLocation SelectorLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::l_paren))
+ return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@selector");
+
+ SmallVector<IdentifierInfo *, 12> KeyIdents;
+ SourceLocation sLoc;
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ bool HasOptionalParen = Tok.is(tok::l_paren);
+ if (HasOptionalParen)
+ ConsumeParen();
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCSelector(getCurScope(), KeyIdents);
+ return ExprError();
+ }
+
+ IdentifierInfo *SelIdent = ParseObjCSelectorPiece(sLoc);
+ if (!SelIdent && // missing selector name.
+ Tok.isNot(tok::colon) && Tok.isNot(tok::coloncolon))
+ return ExprError(Diag(Tok, diag::err_expected) << tok::identifier);
+
+ KeyIdents.push_back(SelIdent);
+
+ unsigned nColons = 0;
+ if (Tok.isNot(tok::r_paren)) {
+ while (true) {
+ if (TryConsumeToken(tok::coloncolon)) { // Handle :: in C++.
+ ++nColons;
+ KeyIdents.push_back(nullptr);
+ } else if (ExpectAndConsume(tok::colon)) // Otherwise expect ':'.
+ return ExprError();
+ ++nColons;
+
+ if (Tok.is(tok::r_paren))
+ break;
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCSelector(getCurScope(), KeyIdents);
+ return ExprError();
+ }
+
+ // Check for another keyword selector.
+ SourceLocation Loc;
+ SelIdent = ParseObjCSelectorPiece(Loc);
+ KeyIdents.push_back(SelIdent);
+ if (!SelIdent && Tok.isNot(tok::colon) && Tok.isNot(tok::coloncolon))
+ break;
+ }
+ }
+ if (HasOptionalParen && Tok.is(tok::r_paren))
+ ConsumeParen(); // ')'
+ T.consumeClose();
+ Selector Sel = PP.getSelectorTable().getSelector(nColons, &KeyIdents[0]);
+ return Actions.ParseObjCSelectorExpression(Sel, AtLoc, SelectorLoc,
+ T.getOpenLocation(),
+ T.getCloseLocation(),
+ !HasOptionalParen);
+}
+
+void Parser::ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod) {
+ // MCDecl might be null due to error in method or c-function prototype, etc.
+ Decl *MCDecl = LM.D;
+ bool skip = MCDecl &&
+ ((parseMethod && !Actions.isObjCMethodDecl(MCDecl)) ||
+ (!parseMethod && Actions.isObjCMethodDecl(MCDecl)));
+ if (skip)
+ return;
+
+ // Save the current token position.
+ SourceLocation OrigLoc = Tok.getLocation();
+
+ assert(!LM.Toks.empty() && "ParseLexedObjCMethodDef - Empty body!");
+ // Store an artificial EOF token to ensure that we don't run off the end of
+ // the method's body when we come to parse it.
+ Token Eof;
+ Eof.startToken();
+ Eof.setKind(tok::eof);
+ Eof.setEofData(MCDecl);
+ Eof.setLocation(OrigLoc);
+ LM.Toks.push_back(Eof);
+ // Append the current token at the end of the new token stream so that it
+ // doesn't get lost.
+ LM.Toks.push_back(Tok);
+ PP.EnterTokenStream(LM.Toks, true, /*IsReinject*/true);
+
+ // Consume the previously pushed token.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+
+ assert(Tok.isOneOf(tok::l_brace, tok::kw_try, tok::colon) &&
+ "Inline objective-c method not starting with '{' or 'try' or ':'");
+ // Enter a scope for the method or c-function body.
+ ParseScope BodyScope(this, (parseMethod ? Scope::ObjCMethodScope : 0) |
+ Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+
+ // Tell the actions module that we have entered a method or c-function definition
+ // with the specified Declarator for the method/function.
+ if (parseMethod)
+ Actions.ActOnStartOfObjCMethodDef(getCurScope(), MCDecl);
+ else
+ Actions.ActOnStartOfFunctionDef(getCurScope(), MCDecl);
+ if (Tok.is(tok::kw_try))
+ ParseFunctionTryBlock(MCDecl, BodyScope);
+ else {
+ if (Tok.is(tok::colon))
+ ParseConstructorInitializer(MCDecl);
+ else
+ Actions.ActOnDefaultCtorInitializers(MCDecl);
+ ParseFunctionStatementBody(MCDecl, BodyScope);
+ }
+
+ if (Tok.getLocation() != OrigLoc) {
+ // Due to parsing error, we either went over the cached tokens or
+ // there are still cached tokens left. If it's the latter case skip the
+ // leftover tokens.
+ // Since this is an uncommon situation that should be avoided, use the
+ // expensive isBeforeInTranslationUnit call.
+ if (PP.getSourceManager().isBeforeInTranslationUnit(Tok.getLocation(),
+ OrigLoc))
+ while (Tok.getLocation() != OrigLoc && Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+ }
+ // Clean up the remaining EOF token.
+ ConsumeAnyToken();
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseOpenMP.cpp b/contrib/libs/clang16/lib/Parse/ParseOpenMP.cpp
new file mode 100644
index 0000000000..a31ceaeebd
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseOpenMP.cpp
@@ -0,0 +1,4720 @@
+//===--- ParseOpenMP.cpp - OpenMP directives parsing ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements parsing of all OpenMP directives and clauses.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/OpenMPClause.h"
+#include "clang/AST/StmtOpenMP.h"
+#include "clang/Basic/OpenMPKinds.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/Scope.h"
+#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/UniqueVector.h"
+#include "llvm/Frontend/OpenMP/OMPAssume.h"
+#include "llvm/Frontend/OpenMP/OMPContext.h"
+#include <optional>
+
+using namespace clang;
+using namespace llvm::omp;
+
+//===----------------------------------------------------------------------===//
+// OpenMP declarative directives.
+//===----------------------------------------------------------------------===//
+
+namespace {
+enum OpenMPDirectiveKindEx {
+ OMPD_cancellation = llvm::omp::Directive_enumSize + 1,
+ OMPD_data,
+ OMPD_declare,
+ OMPD_end,
+ OMPD_end_declare,
+ OMPD_enter,
+ OMPD_exit,
+ OMPD_point,
+ OMPD_reduction,
+ OMPD_target_enter,
+ OMPD_target_exit,
+ OMPD_update,
+ OMPD_distribute_parallel,
+ OMPD_teams_distribute_parallel,
+ OMPD_target_teams_distribute_parallel,
+ OMPD_mapper,
+ OMPD_variant,
+ OMPD_begin,
+ OMPD_begin_declare,
+};
+
+// Helper to unify the enum class OpenMPDirectiveKind with its extension
+// the OpenMPDirectiveKindEx enum which allows to use them together as if they
+// are unsigned values.
+struct OpenMPDirectiveKindExWrapper {
+ OpenMPDirectiveKindExWrapper(unsigned Value) : Value(Value) {}
+ OpenMPDirectiveKindExWrapper(OpenMPDirectiveKind DK) : Value(unsigned(DK)) {}
+ bool operator==(OpenMPDirectiveKindExWrapper V) const {
+ return Value == V.Value;
+ }
+ bool operator!=(OpenMPDirectiveKindExWrapper V) const {
+ return Value != V.Value;
+ }
+ bool operator==(OpenMPDirectiveKind V) const { return Value == unsigned(V); }
+ bool operator!=(OpenMPDirectiveKind V) const { return Value != unsigned(V); }
+ bool operator<(OpenMPDirectiveKind V) const { return Value < unsigned(V); }
+ operator unsigned() const { return Value; }
+ operator OpenMPDirectiveKind() const { return OpenMPDirectiveKind(Value); }
+ unsigned Value;
+};
+
+class DeclDirectiveListParserHelper final {
+ SmallVector<Expr *, 4> Identifiers;
+ Parser *P;
+ OpenMPDirectiveKind Kind;
+
+public:
+ DeclDirectiveListParserHelper(Parser *P, OpenMPDirectiveKind Kind)
+ : P(P), Kind(Kind) {}
+ void operator()(CXXScopeSpec &SS, DeclarationNameInfo NameInfo) {
+ ExprResult Res = P->getActions().ActOnOpenMPIdExpression(
+ P->getCurScope(), SS, NameInfo, Kind);
+ if (Res.isUsable())
+ Identifiers.push_back(Res.get());
+ }
+ llvm::ArrayRef<Expr *> getIdentifiers() const { return Identifiers; }
+};
+} // namespace
+
+// Map token string to extended OMP token kind that are
+// OpenMPDirectiveKind + OpenMPDirectiveKindEx.
+static unsigned getOpenMPDirectiveKindEx(StringRef S) {
+ OpenMPDirectiveKindExWrapper DKind = getOpenMPDirectiveKind(S);
+ if (DKind != OMPD_unknown)
+ return DKind;
+
+ return llvm::StringSwitch<OpenMPDirectiveKindExWrapper>(S)
+ .Case("cancellation", OMPD_cancellation)
+ .Case("data", OMPD_data)
+ .Case("declare", OMPD_declare)
+ .Case("end", OMPD_end)
+ .Case("enter", OMPD_enter)
+ .Case("exit", OMPD_exit)
+ .Case("point", OMPD_point)
+ .Case("reduction", OMPD_reduction)
+ .Case("update", OMPD_update)
+ .Case("mapper", OMPD_mapper)
+ .Case("variant", OMPD_variant)
+ .Case("begin", OMPD_begin)
+ .Default(OMPD_unknown);
+}
+
+static OpenMPDirectiveKindExWrapper parseOpenMPDirectiveKind(Parser &P) {
+ // Array of foldings: F[i][0] F[i][1] ===> F[i][2].
+ // E.g.: OMPD_for OMPD_simd ===> OMPD_for_simd
+ // TODO: add other combined directives in topological order.
+ static const OpenMPDirectiveKindExWrapper F[][3] = {
+ {OMPD_begin, OMPD_declare, OMPD_begin_declare},
+ {OMPD_begin, OMPD_assumes, OMPD_begin_assumes},
+ {OMPD_end, OMPD_declare, OMPD_end_declare},
+ {OMPD_end, OMPD_assumes, OMPD_end_assumes},
+ {OMPD_cancellation, OMPD_point, OMPD_cancellation_point},
+ {OMPD_declare, OMPD_reduction, OMPD_declare_reduction},
+ {OMPD_declare, OMPD_mapper, OMPD_declare_mapper},
+ {OMPD_declare, OMPD_simd, OMPD_declare_simd},
+ {OMPD_declare, OMPD_target, OMPD_declare_target},
+ {OMPD_declare, OMPD_variant, OMPD_declare_variant},
+ {OMPD_begin_declare, OMPD_target, OMPD_begin_declare_target},
+ {OMPD_begin_declare, OMPD_variant, OMPD_begin_declare_variant},
+ {OMPD_end_declare, OMPD_variant, OMPD_end_declare_variant},
+ {OMPD_distribute, OMPD_parallel, OMPD_distribute_parallel},
+ {OMPD_distribute_parallel, OMPD_for, OMPD_distribute_parallel_for},
+ {OMPD_distribute_parallel_for, OMPD_simd,
+ OMPD_distribute_parallel_for_simd},
+ {OMPD_distribute, OMPD_simd, OMPD_distribute_simd},
+ {OMPD_end_declare, OMPD_target, OMPD_end_declare_target},
+ {OMPD_target, OMPD_data, OMPD_target_data},
+ {OMPD_target, OMPD_enter, OMPD_target_enter},
+ {OMPD_target, OMPD_exit, OMPD_target_exit},
+ {OMPD_target, OMPD_update, OMPD_target_update},
+ {OMPD_target_enter, OMPD_data, OMPD_target_enter_data},
+ {OMPD_target_exit, OMPD_data, OMPD_target_exit_data},
+ {OMPD_for, OMPD_simd, OMPD_for_simd},
+ {OMPD_parallel, OMPD_for, OMPD_parallel_for},
+ {OMPD_parallel_for, OMPD_simd, OMPD_parallel_for_simd},
+ {OMPD_parallel, OMPD_loop, OMPD_parallel_loop},
+ {OMPD_parallel, OMPD_sections, OMPD_parallel_sections},
+ {OMPD_taskloop, OMPD_simd, OMPD_taskloop_simd},
+ {OMPD_target, OMPD_parallel, OMPD_target_parallel},
+ {OMPD_target, OMPD_simd, OMPD_target_simd},
+ {OMPD_target_parallel, OMPD_loop, OMPD_target_parallel_loop},
+ {OMPD_target_parallel, OMPD_for, OMPD_target_parallel_for},
+ {OMPD_target_parallel_for, OMPD_simd, OMPD_target_parallel_for_simd},
+ {OMPD_teams, OMPD_distribute, OMPD_teams_distribute},
+ {OMPD_teams_distribute, OMPD_simd, OMPD_teams_distribute_simd},
+ {OMPD_teams_distribute, OMPD_parallel, OMPD_teams_distribute_parallel},
+ {OMPD_teams_distribute_parallel, OMPD_for,
+ OMPD_teams_distribute_parallel_for},
+ {OMPD_teams_distribute_parallel_for, OMPD_simd,
+ OMPD_teams_distribute_parallel_for_simd},
+ {OMPD_teams, OMPD_loop, OMPD_teams_loop},
+ {OMPD_target, OMPD_teams, OMPD_target_teams},
+ {OMPD_target_teams, OMPD_distribute, OMPD_target_teams_distribute},
+ {OMPD_target_teams, OMPD_loop, OMPD_target_teams_loop},
+ {OMPD_target_teams_distribute, OMPD_parallel,
+ OMPD_target_teams_distribute_parallel},
+ {OMPD_target_teams_distribute, OMPD_simd,
+ OMPD_target_teams_distribute_simd},
+ {OMPD_target_teams_distribute_parallel, OMPD_for,
+ OMPD_target_teams_distribute_parallel_for},
+ {OMPD_target_teams_distribute_parallel_for, OMPD_simd,
+ OMPD_target_teams_distribute_parallel_for_simd},
+ {OMPD_master, OMPD_taskloop, OMPD_master_taskloop},
+ {OMPD_masked, OMPD_taskloop, OMPD_masked_taskloop},
+ {OMPD_master_taskloop, OMPD_simd, OMPD_master_taskloop_simd},
+ {OMPD_masked_taskloop, OMPD_simd, OMPD_masked_taskloop_simd},
+ {OMPD_parallel, OMPD_master, OMPD_parallel_master},
+ {OMPD_parallel, OMPD_masked, OMPD_parallel_masked},
+ {OMPD_parallel_master, OMPD_taskloop, OMPD_parallel_master_taskloop},
+ {OMPD_parallel_masked, OMPD_taskloop, OMPD_parallel_masked_taskloop},
+ {OMPD_parallel_master_taskloop, OMPD_simd,
+ OMPD_parallel_master_taskloop_simd},
+ {OMPD_parallel_masked_taskloop, OMPD_simd,
+ OMPD_parallel_masked_taskloop_simd}};
+ enum { CancellationPoint = 0, DeclareReduction = 1, TargetData = 2 };
+ Token Tok = P.getCurToken();
+ OpenMPDirectiveKindExWrapper DKind =
+ Tok.isAnnotation()
+ ? static_cast<unsigned>(OMPD_unknown)
+ : getOpenMPDirectiveKindEx(P.getPreprocessor().getSpelling(Tok));
+ if (DKind == OMPD_unknown)
+ return OMPD_unknown;
+
+ for (const auto &I : F) {
+ if (DKind != I[0])
+ continue;
+
+ Tok = P.getPreprocessor().LookAhead(0);
+ OpenMPDirectiveKindExWrapper SDKind =
+ Tok.isAnnotation()
+ ? static_cast<unsigned>(OMPD_unknown)
+ : getOpenMPDirectiveKindEx(P.getPreprocessor().getSpelling(Tok));
+ if (SDKind == OMPD_unknown)
+ continue;
+
+ if (SDKind == I[1]) {
+ P.ConsumeToken();
+ DKind = I[2];
+ }
+ }
+ return unsigned(DKind) < llvm::omp::Directive_enumSize
+ ? static_cast<OpenMPDirectiveKind>(DKind)
+ : OMPD_unknown;
+}
+
+static DeclarationName parseOpenMPReductionId(Parser &P) {
+ Token Tok = P.getCurToken();
+ Sema &Actions = P.getActions();
+ OverloadedOperatorKind OOK = OO_None;
+ // Allow to use 'operator' keyword for C++ operators
+ bool WithOperator = false;
+ if (Tok.is(tok::kw_operator)) {
+ P.ConsumeToken();
+ Tok = P.getCurToken();
+ WithOperator = true;
+ }
+ switch (Tok.getKind()) {
+ case tok::plus: // '+'
+ OOK = OO_Plus;
+ break;
+ case tok::minus: // '-'
+ OOK = OO_Minus;
+ break;
+ case tok::star: // '*'
+ OOK = OO_Star;
+ break;
+ case tok::amp: // '&'
+ OOK = OO_Amp;
+ break;
+ case tok::pipe: // '|'
+ OOK = OO_Pipe;
+ break;
+ case tok::caret: // '^'
+ OOK = OO_Caret;
+ break;
+ case tok::ampamp: // '&&'
+ OOK = OO_AmpAmp;
+ break;
+ case tok::pipepipe: // '||'
+ OOK = OO_PipePipe;
+ break;
+ case tok::identifier: // identifier
+ if (!WithOperator)
+ break;
+ [[fallthrough]];
+ default:
+ P.Diag(Tok.getLocation(), diag::err_omp_expected_reduction_identifier);
+ P.SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+ Parser::StopBeforeMatch);
+ return DeclarationName();
+ }
+ P.ConsumeToken();
+ auto &DeclNames = Actions.getASTContext().DeclarationNames;
+ return OOK == OO_None ? DeclNames.getIdentifier(Tok.getIdentifierInfo())
+ : DeclNames.getCXXOperatorName(OOK);
+}
+
+/// Parse 'omp declare reduction' construct.
+///
+/// declare-reduction-directive:
+/// annot_pragma_openmp 'declare' 'reduction'
+/// '(' <reduction_id> ':' <type> {',' <type>} ':' <expression> ')'
+/// ['initializer' '(' ('omp_priv' '=' <expression>)|<function_call> ')']
+/// annot_pragma_openmp_end
+/// <reduction_id> is either a base language identifier or one of the following
+/// operators: '+', '-', '*', '&', '|', '^', '&&' and '||'.
+///
+Parser::DeclGroupPtrTy
+Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) {
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(
+ diag::err_expected_lparen_after,
+ getOpenMPDirectiveName(OMPD_declare_reduction).data())) {
+ SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+ return DeclGroupPtrTy();
+ }
+
+ DeclarationName Name = parseOpenMPReductionId(*this);
+ if (Name.isEmpty() && Tok.is(tok::annot_pragma_openmp_end))
+ return DeclGroupPtrTy();
+
+ // Consume ':'.
+ bool IsCorrect = !ExpectAndConsume(tok::colon);
+
+ if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end))
+ return DeclGroupPtrTy();
+
+ IsCorrect = IsCorrect && !Name.isEmpty();
+
+ if (Tok.is(tok::colon) || Tok.is(tok::annot_pragma_openmp_end)) {
+ Diag(Tok.getLocation(), diag::err_expected_type);
+ IsCorrect = false;
+ }
+
+ if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end))
+ return DeclGroupPtrTy();
+
+ SmallVector<std::pair<QualType, SourceLocation>, 8> ReductionTypes;
+ // Parse list of types until ':' token.
+ do {
+ ColonProtectionRAIIObject ColonRAII(*this);
+ SourceRange Range;
+ TypeResult TR = ParseTypeName(&Range, DeclaratorContext::Prototype, AS);
+ if (TR.isUsable()) {
+ QualType ReductionType =
+ Actions.ActOnOpenMPDeclareReductionType(Range.getBegin(), TR);
+ if (!ReductionType.isNull()) {
+ ReductionTypes.push_back(
+ std::make_pair(ReductionType, Range.getBegin()));
+ }
+ } else {
+ SkipUntil(tok::comma, tok::colon, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+
+ if (Tok.is(tok::colon) || Tok.is(tok::annot_pragma_openmp_end))
+ break;
+
+ // Consume ','.
+ if (ExpectAndConsume(tok::comma)) {
+ IsCorrect = false;
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Diag(Tok.getLocation(), diag::err_expected_type);
+ return DeclGroupPtrTy();
+ }
+ }
+ } while (Tok.isNot(tok::annot_pragma_openmp_end));
+
+ if (ReductionTypes.empty()) {
+ SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+ return DeclGroupPtrTy();
+ }
+
+ if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end))
+ return DeclGroupPtrTy();
+
+ // Consume ':'.
+ if (ExpectAndConsume(tok::colon))
+ IsCorrect = false;
+
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Diag(Tok.getLocation(), diag::err_expected_expression);
+ return DeclGroupPtrTy();
+ }
+
+ DeclGroupPtrTy DRD = Actions.ActOnOpenMPDeclareReductionDirectiveStart(
+ getCurScope(), Actions.getCurLexicalContext(), Name, ReductionTypes, AS);
+
+ // Parse <combiner> expression and then parse initializer if any for each
+ // correct type.
+ unsigned I = 0, E = ReductionTypes.size();
+ for (Decl *D : DRD.get()) {
+ TentativeParsingAction TPA(*this);
+ ParseScope OMPDRScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope |
+ Scope::OpenMPDirectiveScope);
+ // Parse <combiner> expression.
+ Actions.ActOnOpenMPDeclareReductionCombinerStart(getCurScope(), D);
+ ExprResult CombinerResult = Actions.ActOnFinishFullExpr(
+ ParseExpression().get(), D->getLocation(), /*DiscardedValue*/ false);
+ Actions.ActOnOpenMPDeclareReductionCombinerEnd(D, CombinerResult.get());
+
+ if (CombinerResult.isInvalid() && Tok.isNot(tok::r_paren) &&
+ Tok.isNot(tok::annot_pragma_openmp_end)) {
+ TPA.Commit();
+ IsCorrect = false;
+ break;
+ }
+ IsCorrect = !T.consumeClose() && IsCorrect && CombinerResult.isUsable();
+ ExprResult InitializerResult;
+ if (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ // Parse <initializer> expression.
+ if (Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo()->isStr("initializer")) {
+ ConsumeToken();
+ } else {
+ Diag(Tok.getLocation(), diag::err_expected) << "'initializer'";
+ TPA.Commit();
+ IsCorrect = false;
+ break;
+ }
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ IsCorrect =
+ !T.expectAndConsume(diag::err_expected_lparen_after, "initializer") &&
+ IsCorrect;
+ if (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ ParseScope OMPDRScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope |
+ Scope::OpenMPDirectiveScope);
+ // Parse expression.
+ VarDecl *OmpPrivParm =
+ Actions.ActOnOpenMPDeclareReductionInitializerStart(getCurScope(),
+ D);
+ // Check if initializer is omp_priv <init_expr> or something else.
+ if (Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo()->isStr("omp_priv")) {
+ ConsumeToken();
+ ParseOpenMPReductionInitializerForDecl(OmpPrivParm);
+ } else {
+ InitializerResult = Actions.ActOnFinishFullExpr(
+ ParseAssignmentExpression().get(), D->getLocation(),
+ /*DiscardedValue*/ false);
+ }
+ Actions.ActOnOpenMPDeclareReductionInitializerEnd(
+ D, InitializerResult.get(), OmpPrivParm);
+ if (InitializerResult.isInvalid() && Tok.isNot(tok::r_paren) &&
+ Tok.isNot(tok::annot_pragma_openmp_end)) {
+ TPA.Commit();
+ IsCorrect = false;
+ break;
+ }
+ IsCorrect =
+ !T.consumeClose() && IsCorrect && !InitializerResult.isInvalid();
+ }
+ }
+
+ ++I;
+ // Revert parsing if not the last type, otherwise accept it, we're done with
+ // parsing.
+ if (I != E)
+ TPA.Revert();
+ else
+ TPA.Commit();
+ }
+ return Actions.ActOnOpenMPDeclareReductionDirectiveEnd(getCurScope(), DRD,
+ IsCorrect);
+}
+
+void Parser::ParseOpenMPReductionInitializerForDecl(VarDecl *OmpPrivParm) {
+ // Parse declarator '=' initializer.
+ // If a '==' or '+=' is found, suggest a fixit to '='.
+ if (isTokenEqualOrEqualTypo()) {
+ ConsumeToken();
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteInitializer(getCurScope(), OmpPrivParm);
+ Actions.FinalizeDeclaration(OmpPrivParm);
+ return;
+ }
+
+ PreferredType.enterVariableInit(Tok.getLocation(), OmpPrivParm);
+ ExprResult Init = ParseInitializer();
+
+ if (Init.isInvalid()) {
+ SkipUntil(tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch);
+ Actions.ActOnInitializerError(OmpPrivParm);
+ } else {
+ Actions.AddInitializerToDecl(OmpPrivParm, Init.get(),
+ /*DirectInit=*/false);
+ }
+ } else if (Tok.is(tok::l_paren)) {
+ // Parse C++ direct initializer: '(' expression-list ')'
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ ExprVector Exprs;
+
+ SourceLocation LParLoc = T.getOpenLocation();
+ auto RunSignatureHelp = [this, OmpPrivParm, LParLoc, &Exprs]() {
+ QualType PreferredType = Actions.ProduceConstructorSignatureHelp(
+ OmpPrivParm->getType()->getCanonicalTypeInternal(),
+ OmpPrivParm->getLocation(), Exprs, LParLoc, /*Braced=*/false);
+ CalledSignatureHelp = true;
+ return PreferredType;
+ };
+ if (ParseExpressionList(Exprs, [&] {
+ PreferredType.enterFunctionArgument(Tok.getLocation(),
+ RunSignatureHelp);
+ })) {
+ if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
+ RunSignatureHelp();
+ Actions.ActOnInitializerError(OmpPrivParm);
+ SkipUntil(tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch);
+ } else {
+ // Match the ')'.
+ SourceLocation RLoc = Tok.getLocation();
+ if (!T.consumeClose())
+ RLoc = T.getCloseLocation();
+
+ ExprResult Initializer =
+ Actions.ActOnParenListExpr(T.getOpenLocation(), RLoc, Exprs);
+ Actions.AddInitializerToDecl(OmpPrivParm, Initializer.get(),
+ /*DirectInit=*/true);
+ }
+ } else if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
+ // Parse C++0x braced-init-list.
+ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+
+ ExprResult Init(ParseBraceInitializer());
+
+ if (Init.isInvalid()) {
+ Actions.ActOnInitializerError(OmpPrivParm);
+ } else {
+ Actions.AddInitializerToDecl(OmpPrivParm, Init.get(),
+ /*DirectInit=*/true);
+ }
+ } else {
+ Actions.ActOnUninitializedDecl(OmpPrivParm);
+ }
+}
+
+/// Parses 'omp declare mapper' directive.
+///
+/// declare-mapper-directive:
+/// annot_pragma_openmp 'declare' 'mapper' '(' [<mapper-identifier> ':']
+/// <type> <var> ')' [<clause>[[,] <clause>] ... ]
+/// annot_pragma_openmp_end
+/// <mapper-identifier> and <var> are base language identifiers.
+///
+Parser::DeclGroupPtrTy
+Parser::ParseOpenMPDeclareMapperDirective(AccessSpecifier AS) {
+ bool IsCorrect = true;
+ // Parse '('
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPDirectiveName(OMPD_declare_mapper).data())) {
+ SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+ return DeclGroupPtrTy();
+ }
+
+ // Parse <mapper-identifier>
+ auto &DeclNames = Actions.getASTContext().DeclarationNames;
+ DeclarationName MapperId;
+ if (PP.LookAhead(0).is(tok::colon)) {
+ if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_default)) {
+ Diag(Tok.getLocation(), diag::err_omp_mapper_illegal_identifier);
+ IsCorrect = false;
+ } else {
+ MapperId = DeclNames.getIdentifier(Tok.getIdentifierInfo());
+ }
+ ConsumeToken();
+ // Consume ':'.
+ ExpectAndConsume(tok::colon);
+ } else {
+ // If no mapper identifier is provided, its name is "default" by default
+ MapperId =
+ DeclNames.getIdentifier(&Actions.getASTContext().Idents.get("default"));
+ }
+
+ if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end))
+ return DeclGroupPtrTy();
+
+ // Parse <type> <var>
+ DeclarationName VName;
+ QualType MapperType;
+ SourceRange Range;
+ TypeResult ParsedType = parseOpenMPDeclareMapperVarDecl(Range, VName, AS);
+ if (ParsedType.isUsable())
+ MapperType =
+ Actions.ActOnOpenMPDeclareMapperType(Range.getBegin(), ParsedType);
+ if (MapperType.isNull())
+ IsCorrect = false;
+ if (!IsCorrect) {
+ SkipUntil(tok::annot_pragma_openmp_end, Parser::StopBeforeMatch);
+ return DeclGroupPtrTy();
+ }
+
+ // Consume ')'.
+ IsCorrect &= !T.consumeClose();
+ if (!IsCorrect) {
+ SkipUntil(tok::annot_pragma_openmp_end, Parser::StopBeforeMatch);
+ return DeclGroupPtrTy();
+ }
+
+ // Enter scope.
+ DeclarationNameInfo DirName;
+ SourceLocation Loc = Tok.getLocation();
+ unsigned ScopeFlags = Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope | Scope::OpenMPDirectiveScope;
+ ParseScope OMPDirectiveScope(this, ScopeFlags);
+ Actions.StartOpenMPDSABlock(OMPD_declare_mapper, DirName, getCurScope(), Loc);
+
+ // Add the mapper variable declaration.
+ ExprResult MapperVarRef = Actions.ActOnOpenMPDeclareMapperDirectiveVarDecl(
+ getCurScope(), MapperType, Range.getBegin(), VName);
+
+ // Parse map clauses.
+ SmallVector<OMPClause *, 6> Clauses;
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ Actions.StartOpenMPClause(CKind);
+ OMPClause *Clause =
+ ParseOpenMPClause(OMPD_declare_mapper, CKind, Clauses.empty());
+ if (Clause)
+ Clauses.push_back(Clause);
+ else
+ IsCorrect = false;
+ // Skip ',' if any.
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ Actions.EndOpenMPClause();
+ }
+ if (Clauses.empty()) {
+ Diag(Tok, diag::err_omp_expected_clause)
+ << getOpenMPDirectiveName(OMPD_declare_mapper);
+ IsCorrect = false;
+ }
+
+ // Exit scope.
+ Actions.EndOpenMPDSABlock(nullptr);
+ OMPDirectiveScope.Exit();
+ DeclGroupPtrTy DG = Actions.ActOnOpenMPDeclareMapperDirective(
+ getCurScope(), Actions.getCurLexicalContext(), MapperId, MapperType,
+ Range.getBegin(), VName, AS, MapperVarRef.get(), Clauses);
+ if (!IsCorrect)
+ return DeclGroupPtrTy();
+
+ return DG;
+}
+
+TypeResult Parser::parseOpenMPDeclareMapperVarDecl(SourceRange &Range,
+ DeclarationName &Name,
+ AccessSpecifier AS) {
+ // Parse the common declaration-specifiers piece.
+ Parser::DeclSpecContext DSC = Parser::DeclSpecContext::DSC_type_specifier;
+ DeclSpec DS(AttrFactory);
+ ParseSpecifierQualifierList(DS, AS, DSC);
+
+ // Parse the declarator.
+ DeclaratorContext Context = DeclaratorContext::Prototype;
+ Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), Context);
+ ParseDeclarator(DeclaratorInfo);
+ Range = DeclaratorInfo.getSourceRange();
+ if (DeclaratorInfo.getIdentifier() == nullptr) {
+ Diag(Tok.getLocation(), diag::err_omp_mapper_expected_declarator);
+ return true;
+ }
+ Name = Actions.GetNameForDeclarator(DeclaratorInfo).getName();
+
+ return Actions.ActOnOpenMPDeclareMapperVarDecl(getCurScope(), DeclaratorInfo);
+}
+
+namespace {
+/// RAII that recreates function context for correct parsing of clauses of
+/// 'declare simd' construct.
+/// OpenMP, 2.8.2 declare simd Construct
+/// The expressions appearing in the clauses of this directive are evaluated in
+/// the scope of the arguments of the function declaration or definition.
+class FNContextRAII final {
+ Parser &P;
+ Sema::CXXThisScopeRAII *ThisScope;
+ Parser::MultiParseScope Scopes;
+ bool HasFunScope = false;
+ FNContextRAII() = delete;
+ FNContextRAII(const FNContextRAII &) = delete;
+ FNContextRAII &operator=(const FNContextRAII &) = delete;
+
+public:
+ FNContextRAII(Parser &P, Parser::DeclGroupPtrTy Ptr) : P(P), Scopes(P) {
+ Decl *D = *Ptr.get().begin();
+ NamedDecl *ND = dyn_cast<NamedDecl>(D);
+ RecordDecl *RD = dyn_cast_or_null<RecordDecl>(D->getDeclContext());
+ Sema &Actions = P.getActions();
+
+ // Allow 'this' within late-parsed attributes.
+ ThisScope = new Sema::CXXThisScopeRAII(Actions, RD, Qualifiers(),
+ ND && ND->isCXXInstanceMember());
+
+ // If the Decl is templatized, add template parameters to scope.
+ // FIXME: Track CurTemplateDepth?
+ P.ReenterTemplateScopes(Scopes, D);
+
+ // If the Decl is on a function, add function parameters to the scope.
+ if (D->isFunctionOrFunctionTemplate()) {
+ HasFunScope = true;
+ Scopes.Enter(Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+ Actions.ActOnReenterFunctionContext(Actions.getCurScope(), D);
+ }
+ }
+ ~FNContextRAII() {
+ if (HasFunScope)
+ P.getActions().ActOnExitFunctionContext();
+ delete ThisScope;
+ }
+};
+} // namespace
+
+/// Parses clauses for 'declare simd' directive.
+/// clause:
+/// 'inbranch' | 'notinbranch'
+/// 'simdlen' '(' <expr> ')'
+/// { 'uniform' '(' <argument_list> ')' }
+/// { 'aligned '(' <argument_list> [ ':' <alignment> ] ')' }
+/// { 'linear '(' <argument_list> [ ':' <step> ] ')' }
+static bool parseDeclareSimdClauses(
+ Parser &P, OMPDeclareSimdDeclAttr::BranchStateTy &BS, ExprResult &SimdLen,
+ SmallVectorImpl<Expr *> &Uniforms, SmallVectorImpl<Expr *> &Aligneds,
+ SmallVectorImpl<Expr *> &Alignments, SmallVectorImpl<Expr *> &Linears,
+ SmallVectorImpl<unsigned> &LinModifiers, SmallVectorImpl<Expr *> &Steps) {
+ SourceRange BSRange;
+ const Token &Tok = P.getCurToken();
+ bool IsError = false;
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ if (Tok.isNot(tok::identifier))
+ break;
+ OMPDeclareSimdDeclAttr::BranchStateTy Out;
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ StringRef ClauseName = II->getName();
+ // Parse 'inranch|notinbranch' clauses.
+ if (OMPDeclareSimdDeclAttr::ConvertStrToBranchStateTy(ClauseName, Out)) {
+ if (BS != OMPDeclareSimdDeclAttr::BS_Undefined && BS != Out) {
+ P.Diag(Tok, diag::err_omp_declare_simd_inbranch_notinbranch)
+ << ClauseName
+ << OMPDeclareSimdDeclAttr::ConvertBranchStateTyToStr(BS) << BSRange;
+ IsError = true;
+ }
+ BS = Out;
+ BSRange = SourceRange(Tok.getLocation(), Tok.getEndLoc());
+ P.ConsumeToken();
+ } else if (ClauseName.equals("simdlen")) {
+ if (SimdLen.isUsable()) {
+ P.Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(OMPD_declare_simd) << ClauseName << 0;
+ IsError = true;
+ }
+ P.ConsumeToken();
+ SourceLocation RLoc;
+ SimdLen = P.ParseOpenMPParensExpr(ClauseName, RLoc);
+ if (SimdLen.isInvalid())
+ IsError = true;
+ } else {
+ OpenMPClauseKind CKind = getOpenMPClauseKind(ClauseName);
+ if (CKind == OMPC_uniform || CKind == OMPC_aligned ||
+ CKind == OMPC_linear) {
+ Sema::OpenMPVarListDataTy Data;
+ SmallVectorImpl<Expr *> *Vars = &Uniforms;
+ if (CKind == OMPC_aligned) {
+ Vars = &Aligneds;
+ } else if (CKind == OMPC_linear) {
+ Data.ExtraModifier = OMPC_LINEAR_val;
+ Vars = &Linears;
+ }
+
+ P.ConsumeToken();
+ if (P.ParseOpenMPVarList(OMPD_declare_simd,
+ getOpenMPClauseKind(ClauseName), *Vars, Data))
+ IsError = true;
+ if (CKind == OMPC_aligned) {
+ Alignments.append(Aligneds.size() - Alignments.size(),
+ Data.DepModOrTailExpr);
+ } else if (CKind == OMPC_linear) {
+ assert(0 <= Data.ExtraModifier &&
+ Data.ExtraModifier <= OMPC_LINEAR_unknown &&
+ "Unexpected linear modifier.");
+ if (P.getActions().CheckOpenMPLinearModifier(
+ static_cast<OpenMPLinearClauseKind>(Data.ExtraModifier),
+ Data.ExtraModifierLoc))
+ Data.ExtraModifier = OMPC_LINEAR_val;
+ LinModifiers.append(Linears.size() - LinModifiers.size(),
+ Data.ExtraModifier);
+ Steps.append(Linears.size() - Steps.size(), Data.DepModOrTailExpr);
+ }
+ } else
+ // TODO: add parsing of other clauses.
+ break;
+ }
+ // Skip ',' if any.
+ if (Tok.is(tok::comma))
+ P.ConsumeToken();
+ }
+ return IsError;
+}
+
+/// Parse clauses for '#pragma omp declare simd'.
+Parser::DeclGroupPtrTy
+Parser::ParseOMPDeclareSimdClauses(Parser::DeclGroupPtrTy Ptr,
+ CachedTokens &Toks, SourceLocation Loc) {
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject*/ true);
+ // Consume the previously pushed token.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+
+ FNContextRAII FnContext(*this, Ptr);
+ OMPDeclareSimdDeclAttr::BranchStateTy BS =
+ OMPDeclareSimdDeclAttr::BS_Undefined;
+ ExprResult Simdlen;
+ SmallVector<Expr *, 4> Uniforms;
+ SmallVector<Expr *, 4> Aligneds;
+ SmallVector<Expr *, 4> Alignments;
+ SmallVector<Expr *, 4> Linears;
+ SmallVector<unsigned, 4> LinModifiers;
+ SmallVector<Expr *, 4> Steps;
+ bool IsError =
+ parseDeclareSimdClauses(*this, BS, Simdlen, Uniforms, Aligneds,
+ Alignments, Linears, LinModifiers, Steps);
+ skipUntilPragmaOpenMPEnd(OMPD_declare_simd);
+ // Skip the last annot_pragma_openmp_end.
+ SourceLocation EndLoc = ConsumeAnnotationToken();
+ if (IsError)
+ return Ptr;
+ return Actions.ActOnOpenMPDeclareSimdDirective(
+ Ptr, BS, Simdlen.get(), Uniforms, Aligneds, Alignments, Linears,
+ LinModifiers, Steps, SourceRange(Loc, EndLoc));
+}
+
+namespace {
+/// Constant used in the diagnostics to distinguish the levels in an OpenMP
+/// contexts: selector-set={selector(trait, ...), ...}, ....
+enum OMPContextLvl {
+ CONTEXT_SELECTOR_SET_LVL = 0,
+ CONTEXT_SELECTOR_LVL = 1,
+ CONTEXT_TRAIT_LVL = 2,
+};
+
+static StringRef stringLiteralParser(Parser &P) {
+ ExprResult Res = P.ParseStringLiteralExpression(true);
+ return Res.isUsable() ? Res.getAs<StringLiteral>()->getString() : "";
+}
+
+static StringRef getNameFromIdOrString(Parser &P, Token &Tok,
+ OMPContextLvl Lvl) {
+ if (Tok.is(tok::identifier) || Tok.is(tok::kw_for)) {
+ llvm::SmallString<16> Buffer;
+ StringRef Name = P.getPreprocessor().getSpelling(Tok, Buffer);
+ (void)P.ConsumeToken();
+ return Name;
+ }
+
+ if (tok::isStringLiteral(Tok.getKind()))
+ return stringLiteralParser(P);
+
+ P.Diag(Tok.getLocation(),
+ diag::warn_omp_declare_variant_string_literal_or_identifier)
+ << Lvl;
+ return "";
+}
+
+static bool checkForDuplicates(Parser &P, StringRef Name,
+ SourceLocation NameLoc,
+ llvm::StringMap<SourceLocation> &Seen,
+ OMPContextLvl Lvl) {
+ auto Res = Seen.try_emplace(Name, NameLoc);
+ if (Res.second)
+ return false;
+
+ // Each trait-set-selector-name, trait-selector-name and trait-name can
+ // only be specified once.
+ P.Diag(NameLoc, diag::warn_omp_declare_variant_ctx_mutiple_use)
+ << Lvl << Name;
+ P.Diag(Res.first->getValue(), diag::note_omp_declare_variant_ctx_used_here)
+ << Lvl << Name;
+ return true;
+}
+} // namespace
+
+void Parser::parseOMPTraitPropertyKind(OMPTraitProperty &TIProperty,
+ llvm::omp::TraitSet Set,
+ llvm::omp::TraitSelector Selector,
+ llvm::StringMap<SourceLocation> &Seen) {
+ TIProperty.Kind = TraitProperty::invalid;
+
+ SourceLocation NameLoc = Tok.getLocation();
+ StringRef Name = getNameFromIdOrString(*this, Tok, CONTEXT_TRAIT_LVL);
+ if (Name.empty()) {
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_TRAIT_LVL << listOpenMPContextTraitProperties(Set, Selector);
+ return;
+ }
+
+ TIProperty.RawString = Name;
+ TIProperty.Kind = getOpenMPContextTraitPropertyKind(Set, Selector, Name);
+ if (TIProperty.Kind != TraitProperty::invalid) {
+ if (checkForDuplicates(*this, Name, NameLoc, Seen, CONTEXT_TRAIT_LVL))
+ TIProperty.Kind = TraitProperty::invalid;
+ return;
+ }
+
+ // It follows diagnosis and helping notes.
+ // FIXME: We should move the diagnosis string generation into libFrontend.
+ Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_property)
+ << Name << getOpenMPContextTraitSelectorName(Selector)
+ << getOpenMPContextTraitSetName(Set);
+
+ TraitSet SetForName = getOpenMPContextTraitSetKind(Name);
+ if (SetForName != TraitSet::invalid) {
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_SELECTOR_SET_LVL << CONTEXT_TRAIT_LVL;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << Name << "<selector-name>"
+ << "(<property-name>)";
+ return;
+ }
+ TraitSelector SelectorForName = getOpenMPContextTraitSelectorKind(Name);
+ if (SelectorForName != TraitSelector::invalid) {
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_SELECTOR_LVL << CONTEXT_TRAIT_LVL;
+ bool AllowsTraitScore = false;
+ bool RequiresProperty = false;
+ isValidTraitSelectorForTraitSet(
+ SelectorForName, getOpenMPContextTraitSetForSelector(SelectorForName),
+ AllowsTraitScore, RequiresProperty);
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForSelector(SelectorForName))
+ << Name << (RequiresProperty ? "(<property-name>)" : "");
+ return;
+ }
+ for (const auto &PotentialSet :
+ {TraitSet::construct, TraitSet::user, TraitSet::implementation,
+ TraitSet::device}) {
+ TraitProperty PropertyForName =
+ getOpenMPContextTraitPropertyKind(PotentialSet, Selector, Name);
+ if (PropertyForName == TraitProperty::invalid)
+ continue;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForProperty(PropertyForName))
+ << getOpenMPContextTraitSelectorName(
+ getOpenMPContextTraitSelectorForProperty(PropertyForName))
+ << ("(" + Name + ")").str();
+ return;
+ }
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_TRAIT_LVL << listOpenMPContextTraitProperties(Set, Selector);
+}
+
+static bool checkExtensionProperty(Parser &P, SourceLocation Loc,
+ OMPTraitProperty &TIProperty,
+ OMPTraitSelector &TISelector,
+ llvm::StringMap<SourceLocation> &Seen) {
+ assert(TISelector.Kind ==
+ llvm::omp::TraitSelector::implementation_extension &&
+ "Only for extension properties, e.g., "
+ "`implementation={extension(PROPERTY)}`");
+ if (TIProperty.Kind == TraitProperty::invalid)
+ return false;
+
+ if (TIProperty.Kind ==
+ TraitProperty::implementation_extension_disable_implicit_base)
+ return true;
+
+ if (TIProperty.Kind ==
+ TraitProperty::implementation_extension_allow_templates)
+ return true;
+
+ if (TIProperty.Kind ==
+ TraitProperty::implementation_extension_bind_to_declaration)
+ return true;
+
+ auto IsMatchExtension = [](OMPTraitProperty &TP) {
+ return (TP.Kind ==
+ llvm::omp::TraitProperty::implementation_extension_match_all ||
+ TP.Kind ==
+ llvm::omp::TraitProperty::implementation_extension_match_any ||
+ TP.Kind ==
+ llvm::omp::TraitProperty::implementation_extension_match_none);
+ };
+
+ if (IsMatchExtension(TIProperty)) {
+ for (OMPTraitProperty &SeenProp : TISelector.Properties)
+ if (IsMatchExtension(SeenProp)) {
+ P.Diag(Loc, diag::err_omp_variant_ctx_second_match_extension);
+ StringRef SeenName = llvm::omp::getOpenMPContextTraitPropertyName(
+ SeenProp.Kind, SeenProp.RawString);
+ SourceLocation SeenLoc = Seen[SeenName];
+ P.Diag(SeenLoc, diag::note_omp_declare_variant_ctx_used_here)
+ << CONTEXT_TRAIT_LVL << SeenName;
+ return false;
+ }
+ return true;
+ }
+
+ llvm_unreachable("Unknown extension property!");
+}
+
+void Parser::parseOMPContextProperty(OMPTraitSelector &TISelector,
+ llvm::omp::TraitSet Set,
+ llvm::StringMap<SourceLocation> &Seen) {
+ assert(TISelector.Kind != TraitSelector::user_condition &&
+ "User conditions are special properties not handled here!");
+
+ SourceLocation PropertyLoc = Tok.getLocation();
+ OMPTraitProperty TIProperty;
+ parseOMPTraitPropertyKind(TIProperty, Set, TISelector.Kind, Seen);
+
+ if (TISelector.Kind == llvm::omp::TraitSelector::implementation_extension)
+ if (!checkExtensionProperty(*this, Tok.getLocation(), TIProperty,
+ TISelector, Seen))
+ TIProperty.Kind = TraitProperty::invalid;
+
+ // If we have an invalid property here we already issued a warning.
+ if (TIProperty.Kind == TraitProperty::invalid) {
+ if (PropertyLoc != Tok.getLocation())
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here)
+ << CONTEXT_TRAIT_LVL;
+ return;
+ }
+
+ if (isValidTraitPropertyForTraitSetAndSelector(TIProperty.Kind,
+ TISelector.Kind, Set)) {
+
+ // If we make it here the property, selector, set, score, condition, ... are
+ // all valid (or have been corrected). Thus we can record the property.
+ TISelector.Properties.push_back(TIProperty);
+ return;
+ }
+
+ Diag(PropertyLoc, diag::warn_omp_ctx_incompatible_property_for_selector)
+ << getOpenMPContextTraitPropertyName(TIProperty.Kind,
+ TIProperty.RawString)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set);
+ Diag(PropertyLoc, diag::note_omp_ctx_compatible_set_and_selector_for_property)
+ << getOpenMPContextTraitPropertyName(TIProperty.Kind,
+ TIProperty.RawString)
+ << getOpenMPContextTraitSelectorName(
+ getOpenMPContextTraitSelectorForProperty(TIProperty.Kind))
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForProperty(TIProperty.Kind));
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here)
+ << CONTEXT_TRAIT_LVL;
+}
+
+void Parser::parseOMPTraitSelectorKind(OMPTraitSelector &TISelector,
+ llvm::omp::TraitSet Set,
+ llvm::StringMap<SourceLocation> &Seen) {
+ TISelector.Kind = TraitSelector::invalid;
+
+ SourceLocation NameLoc = Tok.getLocation();
+ StringRef Name = getNameFromIdOrString(*this, Tok, CONTEXT_SELECTOR_LVL);
+ if (Name.empty()) {
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_SELECTOR_LVL << listOpenMPContextTraitSelectors(Set);
+ return;
+ }
+
+ TISelector.Kind = getOpenMPContextTraitSelectorKind(Name);
+ if (TISelector.Kind != TraitSelector::invalid) {
+ if (checkForDuplicates(*this, Name, NameLoc, Seen, CONTEXT_SELECTOR_LVL))
+ TISelector.Kind = TraitSelector::invalid;
+ return;
+ }
+
+ // It follows diagnosis and helping notes.
+ Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_selector)
+ << Name << getOpenMPContextTraitSetName(Set);
+
+ TraitSet SetForName = getOpenMPContextTraitSetKind(Name);
+ if (SetForName != TraitSet::invalid) {
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_SELECTOR_SET_LVL << CONTEXT_SELECTOR_LVL;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << Name << "<selector-name>"
+ << "<property-name>";
+ return;
+ }
+ for (const auto &PotentialSet :
+ {TraitSet::construct, TraitSet::user, TraitSet::implementation,
+ TraitSet::device}) {
+ TraitProperty PropertyForName = getOpenMPContextTraitPropertyKind(
+ PotentialSet, TraitSelector::invalid, Name);
+ if (PropertyForName == TraitProperty::invalid)
+ continue;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_TRAIT_LVL << CONTEXT_SELECTOR_LVL;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForProperty(PropertyForName))
+ << getOpenMPContextTraitSelectorName(
+ getOpenMPContextTraitSelectorForProperty(PropertyForName))
+ << ("(" + Name + ")").str();
+ return;
+ }
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_SELECTOR_LVL << listOpenMPContextTraitSelectors(Set);
+}
+
+/// Parse optional 'score' '(' <expr> ')' ':'.
+static ExprResult parseContextScore(Parser &P) {
+ ExprResult ScoreExpr;
+ llvm::SmallString<16> Buffer;
+ StringRef SelectorName =
+ P.getPreprocessor().getSpelling(P.getCurToken(), Buffer);
+ if (!SelectorName.equals("score"))
+ return ScoreExpr;
+ (void)P.ConsumeToken();
+ SourceLocation RLoc;
+ ScoreExpr = P.ParseOpenMPParensExpr(SelectorName, RLoc);
+ // Parse ':'
+ if (P.getCurToken().is(tok::colon))
+ (void)P.ConsumeAnyToken();
+ else
+ P.Diag(P.getCurToken(), diag::warn_omp_declare_variant_expected)
+ << "':'"
+ << "score expression";
+ return ScoreExpr;
+}
+
+/// Parses an OpenMP context selector.
+///
+/// <trait-selector-name> ['('[<trait-score>] <trait-property> [, <t-p>]* ')']
+void Parser::parseOMPContextSelector(
+ OMPTraitSelector &TISelector, llvm::omp::TraitSet Set,
+ llvm::StringMap<SourceLocation> &SeenSelectors) {
+ unsigned short OuterPC = ParenCount;
+
+ // If anything went wrong we issue an error or warning and then skip the rest
+ // of the selector. However, commas are ambiguous so we look for the nesting
+ // of parentheses here as well.
+ auto FinishSelector = [OuterPC, this]() -> void {
+ bool Done = false;
+ while (!Done) {
+ while (!SkipUntil({tok::r_brace, tok::r_paren, tok::comma,
+ tok::annot_pragma_openmp_end},
+ StopBeforeMatch))
+ ;
+ if (Tok.is(tok::r_paren) && OuterPC > ParenCount)
+ (void)ConsumeParen();
+ if (OuterPC <= ParenCount) {
+ Done = true;
+ break;
+ }
+ if (!Tok.is(tok::comma) && !Tok.is(tok::r_paren)) {
+ Done = true;
+ break;
+ }
+ (void)ConsumeAnyToken();
+ }
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here)
+ << CONTEXT_SELECTOR_LVL;
+ };
+
+ SourceLocation SelectorLoc = Tok.getLocation();
+ parseOMPTraitSelectorKind(TISelector, Set, SeenSelectors);
+ if (TISelector.Kind == TraitSelector::invalid)
+ return FinishSelector();
+
+ bool AllowsTraitScore = false;
+ bool RequiresProperty = false;
+ if (!isValidTraitSelectorForTraitSet(TISelector.Kind, Set, AllowsTraitScore,
+ RequiresProperty)) {
+ Diag(SelectorLoc, diag::warn_omp_ctx_incompatible_selector_for_set)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set);
+ Diag(SelectorLoc, diag::note_omp_ctx_compatible_set_for_selector)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForSelector(TISelector.Kind))
+ << RequiresProperty;
+ return FinishSelector();
+ }
+
+ if (!RequiresProperty) {
+ TISelector.Properties.push_back(
+ {getOpenMPContextTraitPropertyForSelector(TISelector.Kind),
+ getOpenMPContextTraitSelectorName(TISelector.Kind)});
+ return;
+ }
+
+ if (!Tok.is(tok::l_paren)) {
+ Diag(SelectorLoc, diag::warn_omp_ctx_selector_without_properties)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set);
+ return FinishSelector();
+ }
+
+ if (TISelector.Kind == TraitSelector::user_condition) {
+ SourceLocation RLoc;
+ ExprResult Condition = ParseOpenMPParensExpr("user condition", RLoc);
+ if (!Condition.isUsable())
+ return FinishSelector();
+ TISelector.ScoreOrCondition = Condition.get();
+ TISelector.Properties.push_back(
+ {TraitProperty::user_condition_unknown, "<condition>"});
+ return;
+ }
+
+ BalancedDelimiterTracker BDT(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ // Parse '('.
+ (void)BDT.consumeOpen();
+
+ SourceLocation ScoreLoc = Tok.getLocation();
+ ExprResult Score = parseContextScore(*this);
+
+ if (!AllowsTraitScore && !Score.isUnset()) {
+ if (Score.isUsable()) {
+ Diag(ScoreLoc, diag::warn_omp_ctx_incompatible_score_for_property)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set) << Score.get();
+ } else {
+ Diag(ScoreLoc, diag::warn_omp_ctx_incompatible_score_for_property)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set) << "<invalid>";
+ }
+ Score = ExprResult();
+ }
+
+ if (Score.isUsable())
+ TISelector.ScoreOrCondition = Score.get();
+
+ llvm::StringMap<SourceLocation> SeenProperties;
+ do {
+ parseOMPContextProperty(TISelector, Set, SeenProperties);
+ } while (TryConsumeToken(tok::comma));
+
+ // Parse ')'.
+ BDT.consumeClose();
+}
+
+void Parser::parseOMPTraitSetKind(OMPTraitSet &TISet,
+ llvm::StringMap<SourceLocation> &Seen) {
+ TISet.Kind = TraitSet::invalid;
+
+ SourceLocation NameLoc = Tok.getLocation();
+ StringRef Name = getNameFromIdOrString(*this, Tok, CONTEXT_SELECTOR_SET_LVL);
+ if (Name.empty()) {
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_SELECTOR_SET_LVL << listOpenMPContextTraitSets();
+ return;
+ }
+
+ TISet.Kind = getOpenMPContextTraitSetKind(Name);
+ if (TISet.Kind != TraitSet::invalid) {
+ if (checkForDuplicates(*this, Name, NameLoc, Seen,
+ CONTEXT_SELECTOR_SET_LVL))
+ TISet.Kind = TraitSet::invalid;
+ return;
+ }
+
+ // It follows diagnosis and helping notes.
+ Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_set) << Name;
+
+ TraitSelector SelectorForName = getOpenMPContextTraitSelectorKind(Name);
+ if (SelectorForName != TraitSelector::invalid) {
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_SELECTOR_LVL << CONTEXT_SELECTOR_SET_LVL;
+ bool AllowsTraitScore = false;
+ bool RequiresProperty = false;
+ isValidTraitSelectorForTraitSet(
+ SelectorForName, getOpenMPContextTraitSetForSelector(SelectorForName),
+ AllowsTraitScore, RequiresProperty);
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForSelector(SelectorForName))
+ << Name << (RequiresProperty ? "(<property-name>)" : "");
+ return;
+ }
+ for (const auto &PotentialSet :
+ {TraitSet::construct, TraitSet::user, TraitSet::implementation,
+ TraitSet::device}) {
+ TraitProperty PropertyForName = getOpenMPContextTraitPropertyKind(
+ PotentialSet, TraitSelector::invalid, Name);
+ if (PropertyForName == TraitProperty::invalid)
+ continue;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_TRAIT_LVL << CONTEXT_SELECTOR_SET_LVL;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForProperty(PropertyForName))
+ << getOpenMPContextTraitSelectorName(
+ getOpenMPContextTraitSelectorForProperty(PropertyForName))
+ << ("(" + Name + ")").str();
+ return;
+ }
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_SELECTOR_SET_LVL << listOpenMPContextTraitSets();
+}
+
+/// Parses an OpenMP context selector set.
+///
+/// <trait-set-selector-name> '=' '{' <trait-selector> [, <trait-selector>]* '}'
+void Parser::parseOMPContextSelectorSet(
+ OMPTraitSet &TISet, llvm::StringMap<SourceLocation> &SeenSets) {
+ auto OuterBC = BraceCount;
+
+ // If anything went wrong we issue an error or warning and then skip the rest
+ // of the set. However, commas are ambiguous so we look for the nesting
+ // of braces here as well.
+ auto FinishSelectorSet = [this, OuterBC]() -> void {
+ bool Done = false;
+ while (!Done) {
+ while (!SkipUntil({tok::comma, tok::r_brace, tok::r_paren,
+ tok::annot_pragma_openmp_end},
+ StopBeforeMatch))
+ ;
+ if (Tok.is(tok::r_brace) && OuterBC > BraceCount)
+ (void)ConsumeBrace();
+ if (OuterBC <= BraceCount) {
+ Done = true;
+ break;
+ }
+ if (!Tok.is(tok::comma) && !Tok.is(tok::r_brace)) {
+ Done = true;
+ break;
+ }
+ (void)ConsumeAnyToken();
+ }
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here)
+ << CONTEXT_SELECTOR_SET_LVL;
+ };
+
+ parseOMPTraitSetKind(TISet, SeenSets);
+ if (TISet.Kind == TraitSet::invalid)
+ return FinishSelectorSet();
+
+ // Parse '='.
+ if (!TryConsumeToken(tok::equal))
+ Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected)
+ << "="
+ << ("context set name \"" + getOpenMPContextTraitSetName(TISet.Kind) +
+ "\"")
+ .str();
+
+ // Parse '{'.
+ if (Tok.is(tok::l_brace)) {
+ (void)ConsumeBrace();
+ } else {
+ Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected)
+ << "{"
+ << ("'=' that follows the context set name \"" +
+ getOpenMPContextTraitSetName(TISet.Kind) + "\"")
+ .str();
+ }
+
+ llvm::StringMap<SourceLocation> SeenSelectors;
+ do {
+ OMPTraitSelector TISelector;
+ parseOMPContextSelector(TISelector, TISet.Kind, SeenSelectors);
+ if (TISelector.Kind != TraitSelector::invalid &&
+ !TISelector.Properties.empty())
+ TISet.Selectors.push_back(TISelector);
+ } while (TryConsumeToken(tok::comma));
+
+ // Parse '}'.
+ if (Tok.is(tok::r_brace)) {
+ (void)ConsumeBrace();
+ } else {
+ Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected)
+ << "}"
+ << ("context selectors for the context set \"" +
+ getOpenMPContextTraitSetName(TISet.Kind) + "\"")
+ .str();
+ }
+}
+
+/// Parse OpenMP context selectors:
+///
+/// <trait-set-selector> [, <trait-set-selector>]*
+bool Parser::parseOMPContextSelectors(SourceLocation Loc, OMPTraitInfo &TI) {
+ llvm::StringMap<SourceLocation> SeenSets;
+ do {
+ OMPTraitSet TISet;
+ parseOMPContextSelectorSet(TISet, SeenSets);
+ if (TISet.Kind != TraitSet::invalid && !TISet.Selectors.empty())
+ TI.Sets.push_back(TISet);
+ } while (TryConsumeToken(tok::comma));
+
+ return false;
+}
+
+/// Parse clauses for '#pragma omp declare variant ( variant-func-id ) clause'.
+void Parser::ParseOMPDeclareVariantClauses(Parser::DeclGroupPtrTy Ptr,
+ CachedTokens &Toks,
+ SourceLocation Loc) {
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject*/ true);
+ // Consume the previously pushed token.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+
+ FNContextRAII FnContext(*this, Ptr);
+ // Parse function declaration id.
+ SourceLocation RLoc;
+ // Parse with IsAddressOfOperand set to true to parse methods as DeclRefExprs
+ // instead of MemberExprs.
+ ExprResult AssociatedFunction;
+ {
+ // Do not mark function as is used to prevent its emission if this is the
+ // only place where it is used.
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+ AssociatedFunction = ParseOpenMPParensExpr(
+ getOpenMPDirectiveName(OMPD_declare_variant), RLoc,
+ /*IsAddressOfOperand=*/true);
+ }
+ if (!AssociatedFunction.isUsable()) {
+ if (!Tok.is(tok::annot_pragma_openmp_end))
+ while (!SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch))
+ ;
+ // Skip the last annot_pragma_openmp_end.
+ (void)ConsumeAnnotationToken();
+ return;
+ }
+
+ OMPTraitInfo *ParentTI = Actions.getOMPTraitInfoForSurroundingScope();
+ ASTContext &ASTCtx = Actions.getASTContext();
+ OMPTraitInfo &TI = ASTCtx.getNewOMPTraitInfo();
+ SmallVector<Expr *, 6> AdjustNothing;
+ SmallVector<Expr *, 6> AdjustNeedDevicePtr;
+ SmallVector<OMPInteropInfo, 3> AppendArgs;
+ SourceLocation AdjustArgsLoc, AppendArgsLoc;
+
+ // At least one clause is required.
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Diag(Tok.getLocation(), diag::err_omp_declare_variant_wrong_clause)
+ << (getLangOpts().OpenMP < 51 ? 0 : 1);
+ }
+
+ bool IsError = false;
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ if (!isAllowedClauseForDirective(OMPD_declare_variant, CKind,
+ getLangOpts().OpenMP)) {
+ Diag(Tok.getLocation(), diag::err_omp_declare_variant_wrong_clause)
+ << (getLangOpts().OpenMP < 51 ? 0 : 1);
+ IsError = true;
+ }
+ if (!IsError) {
+ switch (CKind) {
+ case OMPC_match:
+ IsError = parseOMPDeclareVariantMatchClause(Loc, TI, ParentTI);
+ break;
+ case OMPC_adjust_args: {
+ AdjustArgsLoc = Tok.getLocation();
+ ConsumeToken();
+ Sema::OpenMPVarListDataTy Data;
+ SmallVector<Expr *> Vars;
+ IsError = ParseOpenMPVarList(OMPD_declare_variant, OMPC_adjust_args,
+ Vars, Data);
+ if (!IsError)
+ llvm::append_range(Data.ExtraModifier == OMPC_ADJUST_ARGS_nothing
+ ? AdjustNothing
+ : AdjustNeedDevicePtr,
+ Vars);
+ break;
+ }
+ case OMPC_append_args:
+ if (!AppendArgs.empty()) {
+ Diag(AppendArgsLoc, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(OMPD_declare_variant)
+ << getOpenMPClauseName(CKind) << 0;
+ IsError = true;
+ }
+ if (!IsError) {
+ AppendArgsLoc = Tok.getLocation();
+ ConsumeToken();
+ IsError = parseOpenMPAppendArgs(AppendArgs);
+ }
+ break;
+ default:
+ llvm_unreachable("Unexpected clause for declare variant.");
+ }
+ }
+ if (IsError) {
+ while (!SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch))
+ ;
+ // Skip the last annot_pragma_openmp_end.
+ (void)ConsumeAnnotationToken();
+ return;
+ }
+ // Skip ',' if any.
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ }
+
+ std::optional<std::pair<FunctionDecl *, Expr *>> DeclVarData =
+ Actions.checkOpenMPDeclareVariantFunction(
+ Ptr, AssociatedFunction.get(), TI, AppendArgs.size(),
+ SourceRange(Loc, Tok.getLocation()));
+
+ if (DeclVarData && !TI.Sets.empty())
+ Actions.ActOnOpenMPDeclareVariantDirective(
+ DeclVarData->first, DeclVarData->second, TI, AdjustNothing,
+ AdjustNeedDevicePtr, AppendArgs, AdjustArgsLoc, AppendArgsLoc,
+ SourceRange(Loc, Tok.getLocation()));
+
+ // Skip the last annot_pragma_openmp_end.
+ (void)ConsumeAnnotationToken();
+}
+
+bool Parser::parseOpenMPAppendArgs(
+ SmallVectorImpl<OMPInteropInfo> &InteropInfos) {
+ bool HasError = false;
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPClauseName(OMPC_append_args).data()))
+ return true;
+
+ // Parse the list of append-ops, each is;
+ // interop(interop-type[,interop-type]...)
+ while (Tok.is(tok::identifier) && Tok.getIdentifierInfo()->isStr("interop")) {
+ ConsumeToken();
+ BalancedDelimiterTracker IT(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ if (IT.expectAndConsume(diag::err_expected_lparen_after, "interop"))
+ return true;
+
+ OMPInteropInfo InteropInfo;
+ if (ParseOMPInteropInfo(InteropInfo, OMPC_append_args))
+ HasError = true;
+ else
+ InteropInfos.push_back(InteropInfo);
+
+ IT.consumeClose();
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ }
+ if (!HasError && InteropInfos.empty()) {
+ HasError = true;
+ Diag(Tok.getLocation(), diag::err_omp_unexpected_append_op);
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+ HasError = T.consumeClose() || HasError;
+ return HasError;
+}
+
+bool Parser::parseOMPDeclareVariantMatchClause(SourceLocation Loc,
+ OMPTraitInfo &TI,
+ OMPTraitInfo *ParentTI) {
+ // Parse 'match'.
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ if (CKind != OMPC_match) {
+ Diag(Tok.getLocation(), diag::err_omp_declare_variant_wrong_clause)
+ << (getLangOpts().OpenMP < 51 ? 0 : 1);
+ return true;
+ }
+ (void)ConsumeToken();
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPClauseName(OMPC_match).data()))
+ return true;
+
+ // Parse inner context selectors.
+ parseOMPContextSelectors(Loc, TI);
+
+ // Parse ')'
+ (void)T.consumeClose();
+
+ if (!ParentTI)
+ return false;
+
+ // Merge the parent/outer trait info into the one we just parsed and diagnose
+ // problems.
+ // TODO: Keep some source location in the TI to provide better diagnostics.
+ // TODO: Perform some kind of equivalence check on the condition and score
+ // expressions.
+ for (const OMPTraitSet &ParentSet : ParentTI->Sets) {
+ bool MergedSet = false;
+ for (OMPTraitSet &Set : TI.Sets) {
+ if (Set.Kind != ParentSet.Kind)
+ continue;
+ MergedSet = true;
+ for (const OMPTraitSelector &ParentSelector : ParentSet.Selectors) {
+ bool MergedSelector = false;
+ for (OMPTraitSelector &Selector : Set.Selectors) {
+ if (Selector.Kind != ParentSelector.Kind)
+ continue;
+ MergedSelector = true;
+ for (const OMPTraitProperty &ParentProperty :
+ ParentSelector.Properties) {
+ bool MergedProperty = false;
+ for (OMPTraitProperty &Property : Selector.Properties) {
+ // Ignore "equivalent" properties.
+ if (Property.Kind != ParentProperty.Kind)
+ continue;
+
+ // If the kind is the same but the raw string not, we don't want
+ // to skip out on the property.
+ MergedProperty |= Property.RawString == ParentProperty.RawString;
+
+ if (Property.RawString == ParentProperty.RawString &&
+ Selector.ScoreOrCondition == ParentSelector.ScoreOrCondition)
+ continue;
+
+ if (Selector.Kind == llvm::omp::TraitSelector::user_condition) {
+ Diag(Loc, diag::err_omp_declare_variant_nested_user_condition);
+ } else if (Selector.ScoreOrCondition !=
+ ParentSelector.ScoreOrCondition) {
+ Diag(Loc, diag::err_omp_declare_variant_duplicate_nested_trait)
+ << getOpenMPContextTraitPropertyName(
+ ParentProperty.Kind, ParentProperty.RawString)
+ << getOpenMPContextTraitSelectorName(ParentSelector.Kind)
+ << getOpenMPContextTraitSetName(ParentSet.Kind);
+ }
+ }
+ if (!MergedProperty)
+ Selector.Properties.push_back(ParentProperty);
+ }
+ }
+ if (!MergedSelector)
+ Set.Selectors.push_back(ParentSelector);
+ }
+ }
+ if (!MergedSet)
+ TI.Sets.push_back(ParentSet);
+ }
+
+ return false;
+}
+
+/// <clause> [clause[ [,] clause] ... ]
+///
+/// clauses: for error directive
+/// 'at' '(' compilation | execution ')'
+/// 'severity' '(' fatal | warning ')'
+/// 'message' '(' msg-string ')'
+/// ....
+void Parser::ParseOpenMPClauses(OpenMPDirectiveKind DKind,
+ SmallVectorImpl<OMPClause *> &Clauses,
+ SourceLocation Loc) {
+ SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>,
+ llvm::omp::Clause_enumSize + 1>
+ FirstClauses(llvm::omp::Clause_enumSize + 1);
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ Actions.StartOpenMPClause(CKind);
+ OMPClause *Clause = ParseOpenMPClause(
+ DKind, CKind, !FirstClauses[unsigned(CKind)].getInt());
+ SkipUntil(tok::comma, tok::identifier, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ FirstClauses[unsigned(CKind)].setInt(true);
+ if (Clause != nullptr)
+ Clauses.push_back(Clause);
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Actions.EndOpenMPClause();
+ break;
+ }
+ // Skip ',' if any.
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ Actions.EndOpenMPClause();
+ }
+}
+
+/// `omp assumes` or `omp begin/end assumes` <clause> [[,]<clause>]...
+/// where
+///
+/// clause:
+/// 'ext_IMPL_DEFINED'
+/// 'absent' '(' directive-name [, directive-name]* ')'
+/// 'contains' '(' directive-name [, directive-name]* ')'
+/// 'holds' '(' scalar-expression ')'
+/// 'no_openmp'
+/// 'no_openmp_routines'
+/// 'no_parallelism'
+///
+void Parser::ParseOpenMPAssumesDirective(OpenMPDirectiveKind DKind,
+ SourceLocation Loc) {
+ SmallVector<std::string, 4> Assumptions;
+ bool SkippedClauses = false;
+
+ auto SkipBraces = [&](llvm::StringRef Spelling, bool IssueNote) {
+ BalancedDelimiterTracker T(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, Spelling.data()))
+ return;
+ T.skipToEnd();
+ if (IssueNote && T.getCloseLocation().isValid())
+ Diag(T.getCloseLocation(),
+ diag::note_omp_assumption_clause_continue_here);
+ };
+
+ /// Helper to determine which AssumptionClauseMapping (ACM) in the
+ /// AssumptionClauseMappings table matches \p RawString. The return value is
+ /// the index of the matching ACM into the table or -1 if there was no match.
+ auto MatchACMClause = [&](StringRef RawString) {
+ llvm::StringSwitch<int> SS(RawString);
+ unsigned ACMIdx = 0;
+ for (const AssumptionClauseMappingInfo &ACMI : AssumptionClauseMappings) {
+ if (ACMI.StartsWith)
+ SS.StartsWith(ACMI.Identifier, ACMIdx++);
+ else
+ SS.Case(ACMI.Identifier, ACMIdx++);
+ }
+ return SS.Default(-1);
+ };
+
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ IdentifierInfo *II = nullptr;
+ SourceLocation StartLoc = Tok.getLocation();
+ int Idx = -1;
+ if (Tok.isAnyIdentifier()) {
+ II = Tok.getIdentifierInfo();
+ Idx = MatchACMClause(II->getName());
+ }
+ ConsumeAnyToken();
+
+ bool NextIsLPar = Tok.is(tok::l_paren);
+ // Handle unknown clauses by skipping them.
+ if (Idx == -1) {
+ Diag(StartLoc, diag::warn_omp_unknown_assumption_clause_missing_id)
+ << llvm::omp::getOpenMPDirectiveName(DKind)
+ << llvm::omp::getAllAssumeClauseOptions() << NextIsLPar;
+ if (NextIsLPar)
+ SkipBraces(II ? II->getName() : "", /* IssueNote */ true);
+ SkippedClauses = true;
+ continue;
+ }
+ const AssumptionClauseMappingInfo &ACMI = AssumptionClauseMappings[Idx];
+ if (ACMI.HasDirectiveList || ACMI.HasExpression) {
+ // TODO: We ignore absent, contains, and holds assumptions for now. We
+ // also do not verify the content in the parenthesis at all.
+ SkippedClauses = true;
+ SkipBraces(II->getName(), /* IssueNote */ false);
+ continue;
+ }
+
+ if (NextIsLPar) {
+ Diag(Tok.getLocation(),
+ diag::warn_omp_unknown_assumption_clause_without_args)
+ << II;
+ SkipBraces(II->getName(), /* IssueNote */ true);
+ }
+
+ assert(II && "Expected an identifier clause!");
+ std::string Assumption = II->getName().str();
+ if (ACMI.StartsWith)
+ Assumption = "ompx_" + Assumption.substr(ACMI.Identifier.size());
+ else
+ Assumption = "omp_" + Assumption;
+ Assumptions.push_back(Assumption);
+ }
+
+ Actions.ActOnOpenMPAssumesDirective(Loc, DKind, Assumptions, SkippedClauses);
+}
+
+void Parser::ParseOpenMPEndAssumesDirective(SourceLocation Loc) {
+ if (Actions.isInOpenMPAssumeScope())
+ Actions.ActOnOpenMPEndAssumesDirective();
+ else
+ Diag(Loc, diag::err_expected_begin_assumes);
+}
+
+/// Parsing of simple OpenMP clauses like 'default' or 'proc_bind'.
+///
+/// default-clause:
+/// 'default' '(' 'none' | 'shared' | 'private' | 'firstprivate' ')
+///
+/// proc_bind-clause:
+/// 'proc_bind' '(' 'master' | 'close' | 'spread' ')
+///
+/// device_type-clause:
+/// 'device_type' '(' 'host' | 'nohost' | 'any' )'
+namespace {
+struct SimpleClauseData {
+ unsigned Type;
+ SourceLocation Loc;
+ SourceLocation LOpen;
+ SourceLocation TypeLoc;
+ SourceLocation RLoc;
+ SimpleClauseData(unsigned Type, SourceLocation Loc, SourceLocation LOpen,
+ SourceLocation TypeLoc, SourceLocation RLoc)
+ : Type(Type), Loc(Loc), LOpen(LOpen), TypeLoc(TypeLoc), RLoc(RLoc) {}
+};
+} // anonymous namespace
+
+static std::optional<SimpleClauseData>
+parseOpenMPSimpleClause(Parser &P, OpenMPClauseKind Kind) {
+ const Token &Tok = P.getCurToken();
+ SourceLocation Loc = Tok.getLocation();
+ SourceLocation LOpen = P.ConsumeToken();
+ // Parse '('.
+ BalancedDelimiterTracker T(P, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPClauseName(Kind).data()))
+ return std::nullopt;
+
+ unsigned Type = getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : P.getPreprocessor().getSpelling(Tok),
+ P.getLangOpts());
+ SourceLocation TypeLoc = Tok.getLocation();
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ P.ConsumeAnyToken();
+
+ // Parse ')'.
+ SourceLocation RLoc = Tok.getLocation();
+ if (!T.consumeClose())
+ RLoc = T.getCloseLocation();
+
+ return SimpleClauseData(Type, Loc, LOpen, TypeLoc, RLoc);
+}
+
+void Parser::ParseOMPDeclareTargetClauses(
+ Sema::DeclareTargetContextInfo &DTCI) {
+ SourceLocation DeviceTypeLoc;
+ bool RequiresToOrLinkOrIndirectClause = false;
+ bool HasToOrLinkOrIndirectClause = false;
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OMPDeclareTargetDeclAttr::MapTypeTy MT = OMPDeclareTargetDeclAttr::MT_To;
+ bool HasIdentifier = Tok.is(tok::identifier);
+ if (HasIdentifier) {
+ // If we see any clause we need a to or link clause.
+ RequiresToOrLinkOrIndirectClause = true;
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ StringRef ClauseName = II->getName();
+ bool IsDeviceTypeClause =
+ getLangOpts().OpenMP >= 50 &&
+ getOpenMPClauseKind(ClauseName) == OMPC_device_type;
+
+ bool IsIndirectClause = getLangOpts().OpenMP >= 51 &&
+ getOpenMPClauseKind(ClauseName) == OMPC_indirect;
+ if (DTCI.Indirect && IsIndirectClause) {
+ Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(OMPD_declare_target)
+ << getOpenMPClauseName(OMPC_indirect) << 0;
+ break;
+ }
+ bool IsToEnterOrLinkClause =
+ OMPDeclareTargetDeclAttr::ConvertStrToMapTypeTy(ClauseName, MT);
+ assert((!IsDeviceTypeClause || !IsToEnterOrLinkClause) &&
+ "Cannot be both!");
+
+ // Starting with OpenMP 5.2 the `to` clause has been replaced by the
+ // `enter` clause.
+ if (getLangOpts().OpenMP >= 52 && ClauseName == "to") {
+ Diag(Tok, diag::err_omp_declare_target_unexpected_to_clause);
+ break;
+ }
+ if (getLangOpts().OpenMP <= 51 && ClauseName == "enter") {
+ Diag(Tok, diag::err_omp_declare_target_unexpected_enter_clause);
+ break;
+ }
+
+ if (!IsDeviceTypeClause && !IsIndirectClause &&
+ DTCI.Kind == OMPD_begin_declare_target) {
+ Diag(Tok, diag::err_omp_declare_target_unexpected_clause)
+ << ClauseName << (getLangOpts().OpenMP >= 51 ? 3 : 0);
+ break;
+ }
+ if (!IsDeviceTypeClause && !IsToEnterOrLinkClause && !IsIndirectClause) {
+ Diag(Tok, getLangOpts().OpenMP >= 52
+ ? diag::err_omp_declare_target_unexpected_clause_52
+ : diag::err_omp_declare_target_unexpected_clause)
+ << ClauseName
+ << (getLangOpts().OpenMP >= 51
+ ? 4
+ : getLangOpts().OpenMP >= 50 ? 2 : 1);
+ break;
+ }
+
+ if (IsToEnterOrLinkClause || IsIndirectClause)
+ HasToOrLinkOrIndirectClause = true;
+
+ if (IsIndirectClause) {
+ if (!ParseOpenMPIndirectClause(DTCI, /*ParseOnly*/ false))
+ break;
+ continue;
+ }
+ // Parse 'device_type' clause and go to next clause if any.
+ if (IsDeviceTypeClause) {
+ std::optional<SimpleClauseData> DevTypeData =
+ parseOpenMPSimpleClause(*this, OMPC_device_type);
+ if (DevTypeData) {
+ if (DeviceTypeLoc.isValid()) {
+ // We already saw another device_type clause, diagnose it.
+ Diag(DevTypeData->Loc,
+ diag::warn_omp_more_one_device_type_clause);
+ break;
+ }
+ switch (static_cast<OpenMPDeviceType>(DevTypeData->Type)) {
+ case OMPC_DEVICE_TYPE_any:
+ DTCI.DT = OMPDeclareTargetDeclAttr::DT_Any;
+ break;
+ case OMPC_DEVICE_TYPE_host:
+ DTCI.DT = OMPDeclareTargetDeclAttr::DT_Host;
+ break;
+ case OMPC_DEVICE_TYPE_nohost:
+ DTCI.DT = OMPDeclareTargetDeclAttr::DT_NoHost;
+ break;
+ case OMPC_DEVICE_TYPE_unknown:
+ llvm_unreachable("Unexpected device_type");
+ }
+ DeviceTypeLoc = DevTypeData->Loc;
+ }
+ continue;
+ }
+ ConsumeToken();
+ }
+
+ if (DTCI.Kind == OMPD_declare_target || HasIdentifier) {
+ auto &&Callback = [this, MT, &DTCI](CXXScopeSpec &SS,
+ DeclarationNameInfo NameInfo) {
+ NamedDecl *ND =
+ Actions.lookupOpenMPDeclareTargetName(getCurScope(), SS, NameInfo);
+ if (!ND)
+ return;
+ Sema::DeclareTargetContextInfo::MapInfo MI{MT, NameInfo.getLoc()};
+ bool FirstMapping = DTCI.ExplicitlyMapped.try_emplace(ND, MI).second;
+ if (!FirstMapping)
+ Diag(NameInfo.getLoc(), diag::err_omp_declare_target_multiple)
+ << NameInfo.getName();
+ };
+ if (ParseOpenMPSimpleVarList(OMPD_declare_target, Callback,
+ /*AllowScopeSpecifier=*/true))
+ break;
+ }
+
+ if (Tok.is(tok::l_paren)) {
+ Diag(Tok,
+ diag::err_omp_begin_declare_target_unexpected_implicit_to_clause);
+ break;
+ }
+ if (!HasIdentifier && Tok.isNot(tok::annot_pragma_openmp_end)) {
+ Diag(Tok,
+ getLangOpts().OpenMP >= 52
+ ? diag::err_omp_declare_target_wrong_clause_after_implicit_enter
+ : diag::err_omp_declare_target_wrong_clause_after_implicit_to);
+ break;
+ }
+
+ // Consume optional ','.
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ }
+
+ if (DTCI.Indirect && DTCI.DT != OMPDeclareTargetDeclAttr::DT_Any)
+ Diag(DeviceTypeLoc, diag::err_omp_declare_target_indirect_device_type);
+
+ // For declare target require at least 'to' or 'link' to be present.
+ if (DTCI.Kind == OMPD_declare_target && RequiresToOrLinkOrIndirectClause &&
+ !HasToOrLinkOrIndirectClause)
+ Diag(DTCI.Loc,
+ getLangOpts().OpenMP >= 52
+ ? diag::err_omp_declare_target_missing_enter_or_link_clause
+ : diag::err_omp_declare_target_missing_to_or_link_clause)
+ << (getLangOpts().OpenMP >= 51 ? 1 : 0);
+
+ SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+}
+
+void Parser::skipUntilPragmaOpenMPEnd(OpenMPDirectiveKind DKind) {
+ // The last seen token is annot_pragma_openmp_end - need to check for
+ // extra tokens.
+ if (Tok.is(tok::annot_pragma_openmp_end))
+ return;
+
+ Diag(Tok, diag::warn_omp_extra_tokens_at_eol)
+ << getOpenMPDirectiveName(DKind);
+ while (Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+}
+
+void Parser::parseOMPEndDirective(OpenMPDirectiveKind BeginKind,
+ OpenMPDirectiveKind ExpectedKind,
+ OpenMPDirectiveKind FoundKind,
+ SourceLocation BeginLoc,
+ SourceLocation FoundLoc,
+ bool SkipUntilOpenMPEnd) {
+ int DiagSelection = ExpectedKind == OMPD_end_declare_target ? 0 : 1;
+
+ if (FoundKind == ExpectedKind) {
+ ConsumeAnyToken();
+ skipUntilPragmaOpenMPEnd(ExpectedKind);
+ return;
+ }
+
+ Diag(FoundLoc, diag::err_expected_end_declare_target_or_variant)
+ << DiagSelection;
+ Diag(BeginLoc, diag::note_matching)
+ << ("'#pragma omp " + getOpenMPDirectiveName(BeginKind) + "'").str();
+ if (SkipUntilOpenMPEnd)
+ SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+}
+
+void Parser::ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind BeginDKind,
+ OpenMPDirectiveKind EndDKind,
+ SourceLocation DKLoc) {
+ parseOMPEndDirective(BeginDKind, OMPD_end_declare_target, EndDKind, DKLoc,
+ Tok.getLocation(),
+ /* SkipUntilOpenMPEnd */ false);
+ // Skip the last annot_pragma_openmp_end.
+ if (Tok.is(tok::annot_pragma_openmp_end))
+ ConsumeAnnotationToken();
+}
+
+/// Parsing of declarative OpenMP directives.
+///
+/// threadprivate-directive:
+/// annot_pragma_openmp 'threadprivate' simple-variable-list
+/// annot_pragma_openmp_end
+///
+/// allocate-directive:
+/// annot_pragma_openmp 'allocate' simple-variable-list [<clause>]
+/// annot_pragma_openmp_end
+///
+/// declare-reduction-directive:
+/// annot_pragma_openmp 'declare' 'reduction' [...]
+/// annot_pragma_openmp_end
+///
+/// declare-mapper-directive:
+/// annot_pragma_openmp 'declare' 'mapper' '(' [<mapper-identifer> ':']
+/// <type> <var> ')' [<clause>[[,] <clause>] ... ]
+/// annot_pragma_openmp_end
+///
+/// declare-simd-directive:
+/// annot_pragma_openmp 'declare simd' {<clause> [,]}
+/// annot_pragma_openmp_end
+/// <function declaration/definition>
+///
+/// requires directive:
+/// annot_pragma_openmp 'requires' <clause> [[[,] <clause>] ... ]
+/// annot_pragma_openmp_end
+///
+/// assumes directive:
+/// annot_pragma_openmp 'assumes' <clause> [[[,] <clause>] ... ]
+/// annot_pragma_openmp_end
+/// or
+/// annot_pragma_openmp 'begin assumes' <clause> [[[,] <clause>] ... ]
+/// annot_pragma_openmp 'end assumes'
+/// annot_pragma_openmp_end
+///
+Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
+ AccessSpecifier &AS, ParsedAttributes &Attrs, bool Delayed,
+ DeclSpec::TST TagType, Decl *Tag) {
+ assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) &&
+ "Not an OpenMP directive!");
+ ParsingOpenMPDirectiveRAII DirScope(*this);
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ SourceLocation Loc;
+ OpenMPDirectiveKind DKind;
+ if (Delayed) {
+ TentativeParsingAction TPA(*this);
+ Loc = ConsumeAnnotationToken();
+ DKind = parseOpenMPDirectiveKind(*this);
+ if (DKind == OMPD_declare_reduction || DKind == OMPD_declare_mapper) {
+ // Need to delay parsing until completion of the parent class.
+ TPA.Revert();
+ CachedTokens Toks;
+ unsigned Cnt = 1;
+ Toks.push_back(Tok);
+ while (Cnt && Tok.isNot(tok::eof)) {
+ (void)ConsumeAnyToken();
+ if (Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp))
+ ++Cnt;
+ else if (Tok.is(tok::annot_pragma_openmp_end))
+ --Cnt;
+ Toks.push_back(Tok);
+ }
+ // Skip last annot_pragma_openmp_end.
+ if (Cnt == 0)
+ (void)ConsumeAnyToken();
+ auto *LP = new LateParsedPragma(this, AS);
+ LP->takeToks(Toks);
+ getCurrentClass().LateParsedDeclarations.push_back(LP);
+ return nullptr;
+ }
+ TPA.Commit();
+ } else {
+ Loc = ConsumeAnnotationToken();
+ DKind = parseOpenMPDirectiveKind(*this);
+ }
+
+ switch (DKind) {
+ case OMPD_threadprivate: {
+ ConsumeToken();
+ DeclDirectiveListParserHelper Helper(this, DKind);
+ if (!ParseOpenMPSimpleVarList(DKind, Helper,
+ /*AllowScopeSpecifier=*/true)) {
+ skipUntilPragmaOpenMPEnd(DKind);
+ // Skip the last annot_pragma_openmp_end.
+ ConsumeAnnotationToken();
+ return Actions.ActOnOpenMPThreadprivateDirective(Loc,
+ Helper.getIdentifiers());
+ }
+ break;
+ }
+ case OMPD_allocate: {
+ ConsumeToken();
+ DeclDirectiveListParserHelper Helper(this, DKind);
+ if (!ParseOpenMPSimpleVarList(DKind, Helper,
+ /*AllowScopeSpecifier=*/true)) {
+ SmallVector<OMPClause *, 1> Clauses;
+ if (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>,
+ llvm::omp::Clause_enumSize + 1>
+ FirstClauses(llvm::omp::Clause_enumSize + 1);
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OpenMPClauseKind CKind =
+ Tok.isAnnotation() ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ Actions.StartOpenMPClause(CKind);
+ OMPClause *Clause = ParseOpenMPClause(
+ OMPD_allocate, CKind, !FirstClauses[unsigned(CKind)].getInt());
+ SkipUntil(tok::comma, tok::identifier, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ FirstClauses[unsigned(CKind)].setInt(true);
+ if (Clause != nullptr)
+ Clauses.push_back(Clause);
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Actions.EndOpenMPClause();
+ break;
+ }
+ // Skip ',' if any.
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ Actions.EndOpenMPClause();
+ }
+ skipUntilPragmaOpenMPEnd(DKind);
+ }
+ // Skip the last annot_pragma_openmp_end.
+ ConsumeAnnotationToken();
+ return Actions.ActOnOpenMPAllocateDirective(Loc, Helper.getIdentifiers(),
+ Clauses);
+ }
+ break;
+ }
+ case OMPD_requires: {
+ SourceLocation StartLoc = ConsumeToken();
+ SmallVector<OMPClause *, 5> Clauses;
+ SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>,
+ llvm::omp::Clause_enumSize + 1>
+ FirstClauses(llvm::omp::Clause_enumSize + 1);
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Diag(Tok, diag::err_omp_expected_clause)
+ << getOpenMPDirectiveName(OMPD_requires);
+ break;
+ }
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ Actions.StartOpenMPClause(CKind);
+ OMPClause *Clause = ParseOpenMPClause(
+ OMPD_requires, CKind, !FirstClauses[unsigned(CKind)].getInt());
+ SkipUntil(tok::comma, tok::identifier, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ FirstClauses[unsigned(CKind)].setInt(true);
+ if (Clause != nullptr)
+ Clauses.push_back(Clause);
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Actions.EndOpenMPClause();
+ break;
+ }
+ // Skip ',' if any.
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ Actions.EndOpenMPClause();
+ }
+ // Consume final annot_pragma_openmp_end
+ if (Clauses.empty()) {
+ Diag(Tok, diag::err_omp_expected_clause)
+ << getOpenMPDirectiveName(OMPD_requires);
+ ConsumeAnnotationToken();
+ return nullptr;
+ }
+ ConsumeAnnotationToken();
+ return Actions.ActOnOpenMPRequiresDirective(StartLoc, Clauses);
+ }
+ case OMPD_error: {
+ SmallVector<OMPClause *, 1> Clauses;
+ SourceLocation StartLoc = ConsumeToken();
+ ParseOpenMPClauses(DKind, Clauses, StartLoc);
+ Actions.ActOnOpenMPErrorDirective(Clauses, StartLoc, SourceLocation(),
+ /*InExContext = */ false);
+ break;
+ }
+ case OMPD_assumes:
+ case OMPD_begin_assumes:
+ ParseOpenMPAssumesDirective(DKind, ConsumeToken());
+ break;
+ case OMPD_end_assumes:
+ ParseOpenMPEndAssumesDirective(ConsumeToken());
+ break;
+ case OMPD_declare_reduction:
+ ConsumeToken();
+ if (DeclGroupPtrTy Res = ParseOpenMPDeclareReductionDirective(AS)) {
+ skipUntilPragmaOpenMPEnd(OMPD_declare_reduction);
+ // Skip the last annot_pragma_openmp_end.
+ ConsumeAnnotationToken();
+ return Res;
+ }
+ break;
+ case OMPD_declare_mapper: {
+ ConsumeToken();
+ if (DeclGroupPtrTy Res = ParseOpenMPDeclareMapperDirective(AS)) {
+ // Skip the last annot_pragma_openmp_end.
+ ConsumeAnnotationToken();
+ return Res;
+ }
+ break;
+ }
+ case OMPD_begin_declare_variant: {
+ // The syntax is:
+ // { #pragma omp begin declare variant clause }
+ // <function-declaration-or-definition-sequence>
+ // { #pragma omp end declare variant }
+ //
+ ConsumeToken();
+ OMPTraitInfo *ParentTI = Actions.getOMPTraitInfoForSurroundingScope();
+ ASTContext &ASTCtx = Actions.getASTContext();
+ OMPTraitInfo &TI = ASTCtx.getNewOMPTraitInfo();
+ if (parseOMPDeclareVariantMatchClause(Loc, TI, ParentTI)) {
+ while (!SkipUntil(tok::annot_pragma_openmp_end, Parser::StopBeforeMatch))
+ ;
+ // Skip the last annot_pragma_openmp_end.
+ (void)ConsumeAnnotationToken();
+ break;
+ }
+
+ // Skip last tokens.
+ skipUntilPragmaOpenMPEnd(OMPD_begin_declare_variant);
+
+ ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
+
+ VariantMatchInfo VMI;
+ TI.getAsVariantMatchInfo(ASTCtx, VMI);
+
+ std::function<void(StringRef)> DiagUnknownTrait =
+ [this, Loc](StringRef ISATrait) {
+ // TODO Track the selector locations in a way that is accessible here
+ // to improve the diagnostic location.
+ Diag(Loc, diag::warn_unknown_declare_variant_isa_trait) << ISATrait;
+ };
+ TargetOMPContext OMPCtx(
+ ASTCtx, std::move(DiagUnknownTrait),
+ /* CurrentFunctionDecl */ nullptr,
+ /* ConstructTraits */ ArrayRef<llvm::omp::TraitProperty>());
+
+ if (isVariantApplicableInContext(VMI, OMPCtx, /* DeviceSetOnly */ true)) {
+ Actions.ActOnOpenMPBeginDeclareVariant(Loc, TI);
+ break;
+ }
+
+ // Elide all the code till the matching end declare variant was found.
+ unsigned Nesting = 1;
+ SourceLocation DKLoc;
+ OpenMPDirectiveKind DK = OMPD_unknown;
+ do {
+ DKLoc = Tok.getLocation();
+ DK = parseOpenMPDirectiveKind(*this);
+ if (DK == OMPD_end_declare_variant)
+ --Nesting;
+ else if (DK == OMPD_begin_declare_variant)
+ ++Nesting;
+ if (!Nesting || isEofOrEom())
+ break;
+ ConsumeAnyToken();
+ } while (true);
+
+ parseOMPEndDirective(OMPD_begin_declare_variant, OMPD_end_declare_variant,
+ DK, Loc, DKLoc, /* SkipUntilOpenMPEnd */ true);
+ if (isEofOrEom())
+ return nullptr;
+ break;
+ }
+ case OMPD_end_declare_variant: {
+ if (Actions.isInOpenMPDeclareVariantScope())
+ Actions.ActOnOpenMPEndDeclareVariant();
+ else
+ Diag(Loc, diag::err_expected_begin_declare_variant);
+ ConsumeToken();
+ break;
+ }
+ case OMPD_declare_variant:
+ case OMPD_declare_simd: {
+ // The syntax is:
+ // { #pragma omp declare {simd|variant} }
+ // <function-declaration-or-definition>
+ //
+ CachedTokens Toks;
+ Toks.push_back(Tok);
+ ConsumeToken();
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ Toks.push_back(Tok);
+ ConsumeAnyToken();
+ }
+ Toks.push_back(Tok);
+ ConsumeAnyToken();
+
+ DeclGroupPtrTy Ptr;
+ if (Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp)) {
+ Ptr = ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, Attrs, Delayed,
+ TagType, Tag);
+ } else if (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
+ // Here we expect to see some function declaration.
+ if (AS == AS_none) {
+ assert(TagType == DeclSpec::TST_unspecified);
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+ ParsingDeclSpec PDS(*this);
+ Ptr = ParseExternalDeclaration(Attrs, EmptyDeclSpecAttrs, &PDS);
+ } else {
+ Ptr =
+ ParseCXXClassMemberDeclarationWithPragmas(AS, Attrs, TagType, Tag);
+ }
+ }
+ if (!Ptr) {
+ Diag(Loc, diag::err_omp_decl_in_declare_simd_variant)
+ << (DKind == OMPD_declare_simd ? 0 : 1);
+ return DeclGroupPtrTy();
+ }
+ if (DKind == OMPD_declare_simd)
+ return ParseOMPDeclareSimdClauses(Ptr, Toks, Loc);
+ assert(DKind == OMPD_declare_variant &&
+ "Expected declare variant directive only");
+ ParseOMPDeclareVariantClauses(Ptr, Toks, Loc);
+ return Ptr;
+ }
+ case OMPD_begin_declare_target:
+ case OMPD_declare_target: {
+ SourceLocation DTLoc = ConsumeAnyToken();
+ bool HasClauses = Tok.isNot(tok::annot_pragma_openmp_end);
+ Sema::DeclareTargetContextInfo DTCI(DKind, DTLoc);
+ if (HasClauses)
+ ParseOMPDeclareTargetClauses(DTCI);
+ bool HasImplicitMappings = DKind == OMPD_begin_declare_target ||
+ !HasClauses ||
+ (DTCI.ExplicitlyMapped.empty() && DTCI.Indirect);
+
+ // Skip the last annot_pragma_openmp_end.
+ ConsumeAnyToken();
+
+ if (HasImplicitMappings) {
+ Actions.ActOnStartOpenMPDeclareTargetContext(DTCI);
+ return nullptr;
+ }
+
+ Actions.ActOnFinishedOpenMPDeclareTargetContext(DTCI);
+ llvm::SmallVector<Decl *, 4> Decls;
+ for (auto &It : DTCI.ExplicitlyMapped)
+ Decls.push_back(It.first);
+ return Actions.BuildDeclaratorGroup(Decls);
+ }
+ case OMPD_end_declare_target: {
+ if (!Actions.isInOpenMPDeclareTargetContext()) {
+ Diag(Tok, diag::err_omp_unexpected_directive)
+ << 1 << getOpenMPDirectiveName(DKind);
+ break;
+ }
+ const Sema::DeclareTargetContextInfo &DTCI =
+ Actions.ActOnOpenMPEndDeclareTargetDirective();
+ ParseOMPEndDeclareTargetDirective(DTCI.Kind, DKind, DTCI.Loc);
+ return nullptr;
+ }
+ case OMPD_unknown:
+ Diag(Tok, diag::err_omp_unknown_directive);
+ break;
+ case OMPD_parallel:
+ case OMPD_simd:
+ case OMPD_tile:
+ case OMPD_unroll:
+ case OMPD_task:
+ case OMPD_taskyield:
+ case OMPD_barrier:
+ case OMPD_taskwait:
+ case OMPD_taskgroup:
+ case OMPD_flush:
+ case OMPD_depobj:
+ case OMPD_scan:
+ case OMPD_for:
+ case OMPD_for_simd:
+ case OMPD_sections:
+ case OMPD_section:
+ case OMPD_single:
+ case OMPD_master:
+ case OMPD_ordered:
+ case OMPD_critical:
+ case OMPD_parallel_for:
+ case OMPD_parallel_for_simd:
+ case OMPD_parallel_sections:
+ case OMPD_parallel_master:
+ case OMPD_parallel_masked:
+ case OMPD_atomic:
+ case OMPD_target:
+ case OMPD_teams:
+ case OMPD_cancellation_point:
+ case OMPD_cancel:
+ case OMPD_target_data:
+ case OMPD_target_enter_data:
+ case OMPD_target_exit_data:
+ case OMPD_target_parallel:
+ case OMPD_target_parallel_for:
+ case OMPD_taskloop:
+ case OMPD_taskloop_simd:
+ case OMPD_master_taskloop:
+ case OMPD_master_taskloop_simd:
+ case OMPD_parallel_master_taskloop:
+ case OMPD_parallel_master_taskloop_simd:
+ case OMPD_masked_taskloop:
+ case OMPD_masked_taskloop_simd:
+ case OMPD_parallel_masked_taskloop:
+ case OMPD_parallel_masked_taskloop_simd:
+ case OMPD_distribute:
+ case OMPD_target_update:
+ case OMPD_distribute_parallel_for:
+ case OMPD_distribute_parallel_for_simd:
+ case OMPD_distribute_simd:
+ case OMPD_target_parallel_for_simd:
+ case OMPD_target_simd:
+ case OMPD_teams_distribute:
+ case OMPD_teams_distribute_simd:
+ case OMPD_teams_distribute_parallel_for_simd:
+ case OMPD_teams_distribute_parallel_for:
+ case OMPD_target_teams:
+ case OMPD_target_teams_distribute:
+ case OMPD_target_teams_distribute_parallel_for:
+ case OMPD_target_teams_distribute_parallel_for_simd:
+ case OMPD_target_teams_distribute_simd:
+ case OMPD_dispatch:
+ case OMPD_masked:
+ case OMPD_metadirective:
+ case OMPD_loop:
+ case OMPD_teams_loop:
+ case OMPD_target_teams_loop:
+ case OMPD_parallel_loop:
+ case OMPD_target_parallel_loop:
+ Diag(Tok, diag::err_omp_unexpected_directive)
+ << 1 << getOpenMPDirectiveName(DKind);
+ break;
+ default:
+ break;
+ }
+ while (Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ ConsumeAnyToken();
+ return nullptr;
+}
+
+/// Parsing of declarative or executable OpenMP directives.
+///
+/// threadprivate-directive:
+/// annot_pragma_openmp 'threadprivate' simple-variable-list
+/// annot_pragma_openmp_end
+///
+/// allocate-directive:
+/// annot_pragma_openmp 'allocate' simple-variable-list
+/// annot_pragma_openmp_end
+///
+/// declare-reduction-directive:
+/// annot_pragma_openmp 'declare' 'reduction' '(' <reduction_id> ':'
+/// <type> {',' <type>} ':' <expression> ')' ['initializer' '('
+/// ('omp_priv' '=' <expression>|<function_call>) ')']
+/// annot_pragma_openmp_end
+///
+/// declare-mapper-directive:
+/// annot_pragma_openmp 'declare' 'mapper' '(' [<mapper-identifer> ':']
+/// <type> <var> ')' [<clause>[[,] <clause>] ... ]
+/// annot_pragma_openmp_end
+///
+/// executable-directive:
+/// annot_pragma_openmp 'parallel' | 'simd' | 'for' | 'sections' |
+/// 'section' | 'single' | 'master' | 'critical' [ '(' <name> ')' ] |
+/// 'parallel for' | 'parallel sections' | 'parallel master' | 'task' |
+/// 'taskyield' | 'barrier' | 'taskwait' | 'flush' | 'ordered' | 'error'
+/// | 'atomic' | 'for simd' | 'parallel for simd' | 'target' | 'target
+/// data' | 'taskgroup' | 'teams' | 'taskloop' | 'taskloop simd' |
+/// 'master taskloop' | 'master taskloop simd' | 'parallel master
+/// taskloop' | 'parallel master taskloop simd' | 'distribute' | 'target
+/// enter data' | 'target exit data' | 'target parallel' | 'target
+/// parallel for' | 'target update' | 'distribute parallel for' |
+/// 'distribute paralle for simd' | 'distribute simd' | 'target parallel
+/// for simd' | 'target simd' | 'teams distribute' | 'teams distribute
+/// simd' | 'teams distribute parallel for simd' | 'teams distribute
+/// parallel for' | 'target teams' | 'target teams distribute' | 'target
+/// teams distribute parallel for' | 'target teams distribute parallel
+/// for simd' | 'target teams distribute simd' | 'masked' {clause}
+/// annot_pragma_openmp_end
+///
+StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
+ ParsedStmtContext StmtCtx, bool ReadDirectiveWithinMetadirective) {
+ if (!ReadDirectiveWithinMetadirective)
+ assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) &&
+ "Not an OpenMP directive!");
+ ParsingOpenMPDirectiveRAII DirScope(*this);
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+ SmallVector<OMPClause *, 5> Clauses;
+ SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>,
+ llvm::omp::Clause_enumSize + 1>
+ FirstClauses(llvm::omp::Clause_enumSize + 1);
+ unsigned ScopeFlags = Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope | Scope::OpenMPDirectiveScope;
+ SourceLocation Loc = ReadDirectiveWithinMetadirective
+ ? Tok.getLocation()
+ : ConsumeAnnotationToken(),
+ EndLoc;
+ OpenMPDirectiveKind DKind = parseOpenMPDirectiveKind(*this);
+ if (ReadDirectiveWithinMetadirective && DKind == OMPD_unknown) {
+ Diag(Tok, diag::err_omp_unknown_directive);
+ return StmtError();
+ }
+ OpenMPDirectiveKind CancelRegion = OMPD_unknown;
+ // Name of critical directive.
+ DeclarationNameInfo DirName;
+ StmtResult Directive = StmtError();
+ bool HasAssociatedStatement = true;
+
+ switch (DKind) {
+ case OMPD_nothing:
+ if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) ==
+ ParsedStmtContext())
+ Diag(Tok, diag::err_omp_immediate_directive)
+ << getOpenMPDirectiveName(DKind) << 0;
+ ConsumeToken();
+ skipUntilPragmaOpenMPEnd(DKind);
+ if (Tok.is(tok::annot_pragma_openmp_end))
+ ConsumeAnnotationToken();
+ break;
+ case OMPD_metadirective: {
+ ConsumeToken();
+ SmallVector<VariantMatchInfo, 4> VMIs;
+
+ // First iteration of parsing all clauses of metadirective.
+ // This iteration only parses and collects all context selector ignoring the
+ // associated directives.
+ TentativeParsingAction TPA(*this);
+ ASTContext &ASTContext = Actions.getASTContext();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ SourceLocation Loc = ConsumeToken();
+
+ // Parse '('.
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPClauseName(CKind).data()))
+ return Directive;
+
+ OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
+ if (CKind == OMPC_when) {
+ // parse and get OMPTraitInfo to pass to the When clause
+ parseOMPContextSelectors(Loc, TI);
+ if (TI.Sets.size() == 0) {
+ Diag(Tok, diag::err_omp_expected_context_selector) << "when clause";
+ TPA.Commit();
+ return Directive;
+ }
+
+ // Parse ':'
+ if (Tok.is(tok::colon))
+ ConsumeAnyToken();
+ else {
+ Diag(Tok, diag::err_omp_expected_colon) << "when clause";
+ TPA.Commit();
+ return Directive;
+ }
+ }
+ // Skip Directive for now. We will parse directive in the second iteration
+ int paren = 0;
+ while (Tok.isNot(tok::r_paren) || paren != 0) {
+ if (Tok.is(tok::l_paren))
+ paren++;
+ if (Tok.is(tok::r_paren))
+ paren--;
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Diag(Tok, diag::err_omp_expected_punc)
+ << getOpenMPClauseName(CKind) << 0;
+ TPA.Commit();
+ return Directive;
+ }
+ ConsumeAnyToken();
+ }
+ // Parse ')'
+ if (Tok.is(tok::r_paren))
+ T.consumeClose();
+
+ VariantMatchInfo VMI;
+ TI.getAsVariantMatchInfo(ASTContext, VMI);
+
+ VMIs.push_back(VMI);
+ }
+
+ TPA.Revert();
+ // End of the first iteration. Parser is reset to the start of metadirective
+
+ std::function<void(StringRef)> DiagUnknownTrait =
+ [this, Loc](StringRef ISATrait) {
+ // TODO Track the selector locations in a way that is accessible here
+ // to improve the diagnostic location.
+ Diag(Loc, diag::warn_unknown_declare_variant_isa_trait) << ISATrait;
+ };
+ TargetOMPContext OMPCtx(ASTContext, std::move(DiagUnknownTrait),
+ /* CurrentFunctionDecl */ nullptr,
+ ArrayRef<llvm::omp::TraitProperty>());
+
+ // A single match is returned for OpenMP 5.0
+ int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx);
+
+ int Idx = 0;
+ // In OpenMP 5.0 metadirective is either replaced by another directive or
+ // ignored.
+ // TODO: In OpenMP 5.1 generate multiple directives based upon the matches
+ // found by getBestWhenMatchForContext.
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ // OpenMP 5.0 implementation - Skip to the best index found.
+ if (Idx++ != BestIdx) {
+ ConsumeToken(); // Consume clause name
+ T.consumeOpen(); // Consume '('
+ int paren = 0;
+ // Skip everything inside the clause
+ while (Tok.isNot(tok::r_paren) || paren != 0) {
+ if (Tok.is(tok::l_paren))
+ paren++;
+ if (Tok.is(tok::r_paren))
+ paren--;
+ ConsumeAnyToken();
+ }
+ // Parse ')'
+ if (Tok.is(tok::r_paren))
+ T.consumeClose();
+ continue;
+ }
+
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ SourceLocation Loc = ConsumeToken();
+
+ // Parse '('.
+ T.consumeOpen();
+
+ // Skip ContextSelectors for when clause
+ if (CKind == OMPC_when) {
+ OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
+ // parse and skip the ContextSelectors
+ parseOMPContextSelectors(Loc, TI);
+
+ // Parse ':'
+ ConsumeAnyToken();
+ }
+
+ // If no directive is passed, skip in OpenMP 5.0.
+ // TODO: Generate nothing directive from OpenMP 5.1.
+ if (Tok.is(tok::r_paren)) {
+ SkipUntil(tok::annot_pragma_openmp_end);
+ break;
+ }
+
+ // Parse Directive
+ Directive = ParseOpenMPDeclarativeOrExecutableDirective(
+ StmtCtx,
+ /*ReadDirectiveWithinMetadirective=*/true);
+ break;
+ }
+ break;
+ }
+ case OMPD_threadprivate: {
+ // FIXME: Should this be permitted in C++?
+ if ((StmtCtx & ParsedStmtContext::AllowDeclarationsInC) ==
+ ParsedStmtContext()) {
+ Diag(Tok, diag::err_omp_immediate_directive)
+ << getOpenMPDirectiveName(DKind) << 0;
+ }
+ ConsumeToken();
+ DeclDirectiveListParserHelper Helper(this, DKind);
+ if (!ParseOpenMPSimpleVarList(DKind, Helper,
+ /*AllowScopeSpecifier=*/false)) {
+ skipUntilPragmaOpenMPEnd(DKind);
+ DeclGroupPtrTy Res = Actions.ActOnOpenMPThreadprivateDirective(
+ Loc, Helper.getIdentifiers());
+ Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation());
+ }
+ SkipUntil(tok::annot_pragma_openmp_end);
+ break;
+ }
+ case OMPD_allocate: {
+ // FIXME: Should this be permitted in C++?
+ if ((StmtCtx & ParsedStmtContext::AllowDeclarationsInC) ==
+ ParsedStmtContext()) {
+ Diag(Tok, diag::err_omp_immediate_directive)
+ << getOpenMPDirectiveName(DKind) << 0;
+ }
+ ConsumeToken();
+ DeclDirectiveListParserHelper Helper(this, DKind);
+ if (!ParseOpenMPSimpleVarList(DKind, Helper,
+ /*AllowScopeSpecifier=*/false)) {
+ SmallVector<OMPClause *, 1> Clauses;
+ if (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>,
+ llvm::omp::Clause_enumSize + 1>
+ FirstClauses(llvm::omp::Clause_enumSize + 1);
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OpenMPClauseKind CKind =
+ Tok.isAnnotation() ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ Actions.StartOpenMPClause(CKind);
+ OMPClause *Clause = ParseOpenMPClause(
+ OMPD_allocate, CKind, !FirstClauses[unsigned(CKind)].getInt());
+ SkipUntil(tok::comma, tok::identifier, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ FirstClauses[unsigned(CKind)].setInt(true);
+ if (Clause != nullptr)
+ Clauses.push_back(Clause);
+ if (Tok.is(tok::annot_pragma_openmp_end)) {
+ Actions.EndOpenMPClause();
+ break;
+ }
+ // Skip ',' if any.
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ Actions.EndOpenMPClause();
+ }
+ skipUntilPragmaOpenMPEnd(DKind);
+ }
+ DeclGroupPtrTy Res = Actions.ActOnOpenMPAllocateDirective(
+ Loc, Helper.getIdentifiers(), Clauses);
+ Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation());
+ }
+ SkipUntil(tok::annot_pragma_openmp_end);
+ break;
+ }
+ case OMPD_declare_reduction:
+ ConsumeToken();
+ if (DeclGroupPtrTy Res =
+ ParseOpenMPDeclareReductionDirective(/*AS=*/AS_none)) {
+ skipUntilPragmaOpenMPEnd(OMPD_declare_reduction);
+ ConsumeAnyToken();
+ Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation());
+ } else {
+ SkipUntil(tok::annot_pragma_openmp_end);
+ }
+ break;
+ case OMPD_declare_mapper: {
+ ConsumeToken();
+ if (DeclGroupPtrTy Res =
+ ParseOpenMPDeclareMapperDirective(/*AS=*/AS_none)) {
+ // Skip the last annot_pragma_openmp_end.
+ ConsumeAnnotationToken();
+ Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation());
+ } else {
+ SkipUntil(tok::annot_pragma_openmp_end);
+ }
+ break;
+ }
+ case OMPD_flush:
+ case OMPD_depobj:
+ case OMPD_scan:
+ case OMPD_taskyield:
+ case OMPD_error:
+ case OMPD_barrier:
+ case OMPD_taskwait:
+ case OMPD_cancellation_point:
+ case OMPD_cancel:
+ case OMPD_target_enter_data:
+ case OMPD_target_exit_data:
+ case OMPD_target_update:
+ case OMPD_interop:
+ if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) ==
+ ParsedStmtContext()) {
+ Diag(Tok, diag::err_omp_immediate_directive)
+ << getOpenMPDirectiveName(DKind) << 0;
+ if (DKind == OMPD_error) {
+ SkipUntil(tok::annot_pragma_openmp_end);
+ break;
+ }
+ }
+ HasAssociatedStatement = false;
+ // Fall through for further analysis.
+ [[fallthrough]];
+ case OMPD_parallel:
+ case OMPD_simd:
+ case OMPD_tile:
+ case OMPD_unroll:
+ case OMPD_for:
+ case OMPD_for_simd:
+ case OMPD_sections:
+ case OMPD_single:
+ case OMPD_section:
+ case OMPD_master:
+ case OMPD_critical:
+ case OMPD_parallel_for:
+ case OMPD_parallel_for_simd:
+ case OMPD_parallel_sections:
+ case OMPD_parallel_master:
+ case OMPD_parallel_masked:
+ case OMPD_task:
+ case OMPD_ordered:
+ case OMPD_atomic:
+ case OMPD_target:
+ case OMPD_teams:
+ case OMPD_taskgroup:
+ case OMPD_target_data:
+ case OMPD_target_parallel:
+ case OMPD_target_parallel_for:
+ case OMPD_loop:
+ case OMPD_teams_loop:
+ case OMPD_target_teams_loop:
+ case OMPD_parallel_loop:
+ case OMPD_target_parallel_loop:
+ case OMPD_taskloop:
+ case OMPD_taskloop_simd:
+ case OMPD_master_taskloop:
+ case OMPD_masked_taskloop:
+ case OMPD_master_taskloop_simd:
+ case OMPD_masked_taskloop_simd:
+ case OMPD_parallel_master_taskloop:
+ case OMPD_parallel_masked_taskloop:
+ case OMPD_parallel_master_taskloop_simd:
+ case OMPD_parallel_masked_taskloop_simd:
+ case OMPD_distribute:
+ case OMPD_distribute_parallel_for:
+ case OMPD_distribute_parallel_for_simd:
+ case OMPD_distribute_simd:
+ case OMPD_target_parallel_for_simd:
+ case OMPD_target_simd:
+ case OMPD_teams_distribute:
+ case OMPD_teams_distribute_simd:
+ case OMPD_teams_distribute_parallel_for_simd:
+ case OMPD_teams_distribute_parallel_for:
+ case OMPD_target_teams:
+ case OMPD_target_teams_distribute:
+ case OMPD_target_teams_distribute_parallel_for:
+ case OMPD_target_teams_distribute_parallel_for_simd:
+ case OMPD_target_teams_distribute_simd:
+ case OMPD_dispatch:
+ case OMPD_masked: {
+ // Special processing for flush and depobj clauses.
+ Token ImplicitTok;
+ bool ImplicitClauseAllowed = false;
+ if (DKind == OMPD_flush || DKind == OMPD_depobj) {
+ ImplicitTok = Tok;
+ ImplicitClauseAllowed = true;
+ }
+ ConsumeToken();
+ // Parse directive name of the 'critical' directive if any.
+ if (DKind == OMPD_critical) {
+ BalancedDelimiterTracker T(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ if (!T.consumeOpen()) {
+ if (Tok.isAnyIdentifier()) {
+ DirName =
+ DeclarationNameInfo(Tok.getIdentifierInfo(), Tok.getLocation());
+ ConsumeAnyToken();
+ } else {
+ Diag(Tok, diag::err_omp_expected_identifier_for_critical);
+ }
+ T.consumeClose();
+ }
+ } else if (DKind == OMPD_cancellation_point || DKind == OMPD_cancel) {
+ CancelRegion = parseOpenMPDirectiveKind(*this);
+ if (Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeToken();
+ }
+
+ if (isOpenMPLoopDirective(DKind))
+ ScopeFlags |= Scope::OpenMPLoopDirectiveScope;
+ if (isOpenMPSimdDirective(DKind))
+ ScopeFlags |= Scope::OpenMPSimdDirectiveScope;
+ ParseScope OMPDirectiveScope(this, ScopeFlags);
+ Actions.StartOpenMPDSABlock(DKind, DirName, Actions.getCurScope(), Loc);
+
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ // If we are parsing for a directive within a metadirective, the directive
+ // ends with a ')'.
+ if (ReadDirectiveWithinMetadirective && Tok.is(tok::r_paren)) {
+ while (Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ break;
+ }
+ bool HasImplicitClause = false;
+ if (ImplicitClauseAllowed && Tok.is(tok::l_paren)) {
+ HasImplicitClause = true;
+ // Push copy of the current token back to stream to properly parse
+ // pseudo-clause OMPFlushClause or OMPDepobjClause.
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+ PP.EnterToken(ImplicitTok, /*IsReinject*/ true);
+ ConsumeAnyToken();
+ }
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ if (HasImplicitClause) {
+ assert(CKind == OMPC_unknown && "Must be unknown implicit clause.");
+ if (DKind == OMPD_flush) {
+ CKind = OMPC_flush;
+ } else {
+ assert(DKind == OMPD_depobj &&
+ "Expected flush or depobj directives.");
+ CKind = OMPC_depobj;
+ }
+ }
+ // No more implicit clauses allowed.
+ ImplicitClauseAllowed = false;
+ Actions.StartOpenMPClause(CKind);
+ HasImplicitClause = false;
+ OMPClause *Clause = ParseOpenMPClause(
+ DKind, CKind, !FirstClauses[unsigned(CKind)].getInt());
+ FirstClauses[unsigned(CKind)].setInt(true);
+ if (Clause) {
+ FirstClauses[unsigned(CKind)].setPointer(Clause);
+ Clauses.push_back(Clause);
+ }
+
+ // Skip ',' if any.
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ Actions.EndOpenMPClause();
+ }
+ // End location of the directive.
+ EndLoc = Tok.getLocation();
+ // Consume final annot_pragma_openmp_end.
+ ConsumeAnnotationToken();
+
+ // OpenMP [2.13.8, ordered Construct, Syntax]
+ // If the depend clause is specified, the ordered construct is a stand-alone
+ // directive.
+ if (DKind == OMPD_ordered && FirstClauses[unsigned(OMPC_depend)].getInt()) {
+ if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) ==
+ ParsedStmtContext()) {
+ Diag(Loc, diag::err_omp_immediate_directive)
+ << getOpenMPDirectiveName(DKind) << 1
+ << getOpenMPClauseName(OMPC_depend);
+ }
+ HasAssociatedStatement = false;
+ }
+
+ if (DKind == OMPD_tile && !FirstClauses[unsigned(OMPC_sizes)].getInt()) {
+ Diag(Loc, diag::err_omp_required_clause)
+ << getOpenMPDirectiveName(OMPD_tile) << "sizes";
+ }
+
+ StmtResult AssociatedStmt;
+ if (HasAssociatedStatement) {
+ // The body is a block scope like in Lambdas and Blocks.
+ Actions.ActOnOpenMPRegionStart(DKind, getCurScope());
+ // FIXME: We create a bogus CompoundStmt scope to hold the contents of
+ // the captured region. Code elsewhere assumes that any FunctionScopeInfo
+ // should have at least one compound statement scope within it.
+ ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
+ {
+ Sema::CompoundScopeRAII Scope(Actions);
+ AssociatedStmt = ParseStatement();
+
+ if (AssociatedStmt.isUsable() && isOpenMPLoopDirective(DKind) &&
+ getLangOpts().OpenMPIRBuilder)
+ AssociatedStmt = Actions.ActOnOpenMPLoopnest(AssociatedStmt.get());
+ }
+ AssociatedStmt = Actions.ActOnOpenMPRegionEnd(AssociatedStmt, Clauses);
+ } else if (DKind == OMPD_target_update || DKind == OMPD_target_enter_data ||
+ DKind == OMPD_target_exit_data) {
+ Actions.ActOnOpenMPRegionStart(DKind, getCurScope());
+ AssociatedStmt = (Sema::CompoundScopeRAII(Actions),
+ Actions.ActOnCompoundStmt(Loc, Loc, std::nullopt,
+ /*isStmtExpr=*/false));
+ AssociatedStmt = Actions.ActOnOpenMPRegionEnd(AssociatedStmt, Clauses);
+ }
+ Directive = Actions.ActOnOpenMPExecutableDirective(
+ DKind, DirName, CancelRegion, Clauses, AssociatedStmt.get(), Loc,
+ EndLoc);
+
+ // Exit scope.
+ Actions.EndOpenMPDSABlock(Directive.get());
+ OMPDirectiveScope.Exit();
+ break;
+ }
+ case OMPD_declare_simd:
+ case OMPD_declare_target:
+ case OMPD_begin_declare_target:
+ case OMPD_end_declare_target:
+ case OMPD_requires:
+ case OMPD_begin_declare_variant:
+ case OMPD_end_declare_variant:
+ case OMPD_declare_variant:
+ Diag(Tok, diag::err_omp_unexpected_directive)
+ << 1 << getOpenMPDirectiveName(DKind);
+ SkipUntil(tok::annot_pragma_openmp_end);
+ break;
+ case OMPD_unknown:
+ default:
+ Diag(Tok, diag::err_omp_unknown_directive);
+ SkipUntil(tok::annot_pragma_openmp_end);
+ break;
+ }
+ return Directive;
+}
+
+// Parses simple list:
+// simple-variable-list:
+// '(' id-expression {, id-expression} ')'
+//
+bool Parser::ParseOpenMPSimpleVarList(
+ OpenMPDirectiveKind Kind,
+ const llvm::function_ref<void(CXXScopeSpec &, DeclarationNameInfo)>
+ &Callback,
+ bool AllowScopeSpecifier) {
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPDirectiveName(Kind).data()))
+ return true;
+ bool IsCorrect = true;
+ bool NoIdentIsFound = true;
+
+ // Read tokens while ')' or annot_pragma_openmp_end is not found.
+ while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) {
+ CXXScopeSpec SS;
+ UnqualifiedId Name;
+ // Read var name.
+ Token PrevTok = Tok;
+ NoIdentIsFound = false;
+
+ if (AllowScopeSpecifier && getLangOpts().CPlusPlus &&
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false, false)) {
+ IsCorrect = false;
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ } else if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, false, false,
+ false, false, nullptr, Name)) {
+ IsCorrect = false;
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ } else if (Tok.isNot(tok::comma) && Tok.isNot(tok::r_paren) &&
+ Tok.isNot(tok::annot_pragma_openmp_end)) {
+ IsCorrect = false;
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ Diag(PrevTok.getLocation(), diag::err_expected)
+ << tok::identifier
+ << SourceRange(PrevTok.getLocation(), PrevTokLocation);
+ } else {
+ Callback(SS, Actions.GetNameFromUnqualifiedId(Name));
+ }
+ // Consume ','.
+ if (Tok.is(tok::comma)) {
+ ConsumeToken();
+ }
+ }
+
+ if (NoIdentIsFound) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ IsCorrect = false;
+ }
+
+ // Parse ')'.
+ IsCorrect = !T.consumeClose() && IsCorrect;
+
+ return !IsCorrect;
+}
+
+OMPClause *Parser::ParseOpenMPSizesClause() {
+ SourceLocation ClauseNameLoc = ConsumeToken();
+ SmallVector<Expr *, 4> ValExprs;
+
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return nullptr;
+ }
+
+ while (true) {
+ ExprResult Val = ParseConstantExpression();
+ if (!Val.isUsable()) {
+ T.skipToEnd();
+ return nullptr;
+ }
+
+ ValExprs.push_back(Val.get());
+
+ if (Tok.is(tok::r_paren) || Tok.is(tok::annot_pragma_openmp_end))
+ break;
+
+ ExpectAndConsume(tok::comma);
+ }
+
+ T.consumeClose();
+
+ return Actions.ActOnOpenMPSizesClause(
+ ValExprs, ClauseNameLoc, T.getOpenLocation(), T.getCloseLocation());
+}
+
+OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) {
+ SourceLocation Loc = Tok.getLocation();
+ ConsumeAnyToken();
+
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "uses_allocator"))
+ return nullptr;
+ SmallVector<Sema::UsesAllocatorsData, 4> Data;
+ do {
+ ExprResult Allocator =
+ getLangOpts().CPlusPlus ? ParseCXXIdExpression() : ParseExpression();
+ if (Allocator.isInvalid()) {
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ break;
+ }
+ Sema::UsesAllocatorsData &D = Data.emplace_back();
+ D.Allocator = Allocator.get();
+ if (Tok.is(tok::l_paren)) {
+ BalancedDelimiterTracker T(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ T.consumeOpen();
+ ExprResult AllocatorTraits =
+ getLangOpts().CPlusPlus ? ParseCXXIdExpression() : ParseExpression();
+ T.consumeClose();
+ if (AllocatorTraits.isInvalid()) {
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ break;
+ }
+ D.AllocatorTraits = AllocatorTraits.get();
+ D.LParenLoc = T.getOpenLocation();
+ D.RParenLoc = T.getCloseLocation();
+ }
+ if (Tok.isNot(tok::comma) && Tok.isNot(tok::r_paren))
+ Diag(Tok, diag::err_omp_expected_punc) << "uses_allocators" << 0;
+ // Parse ','
+ if (Tok.is(tok::comma))
+ ConsumeAnyToken();
+ } while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end));
+ T.consumeClose();
+ return Actions.ActOnOpenMPUsesAllocatorClause(Loc, T.getOpenLocation(),
+ T.getCloseLocation(), Data);
+}
+
+/// Parsing of OpenMP clauses.
+///
+/// clause:
+/// if-clause | final-clause | num_threads-clause | safelen-clause |
+/// default-clause | private-clause | firstprivate-clause | shared-clause
+/// | linear-clause | aligned-clause | collapse-clause | bind-clause |
+/// lastprivate-clause | reduction-clause | proc_bind-clause |
+/// schedule-clause | copyin-clause | copyprivate-clause | untied-clause |
+/// mergeable-clause | flush-clause | read-clause | write-clause |
+/// update-clause | capture-clause | seq_cst-clause | device-clause |
+/// simdlen-clause | threads-clause | simd-clause | num_teams-clause |
+/// thread_limit-clause | priority-clause | grainsize-clause |
+/// nogroup-clause | num_tasks-clause | hint-clause | to-clause |
+/// from-clause | is_device_ptr-clause | task_reduction-clause |
+/// in_reduction-clause | allocator-clause | allocate-clause |
+/// acq_rel-clause | acquire-clause | release-clause | relaxed-clause |
+/// depobj-clause | destroy-clause | detach-clause | inclusive-clause |
+/// exclusive-clause | uses_allocators-clause | use_device_addr-clause |
+/// has_device_addr
+///
+OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind,
+ OpenMPClauseKind CKind, bool FirstClause) {
+ OMPClauseKind = CKind;
+ OMPClause *Clause = nullptr;
+ bool ErrorFound = false;
+ bool WrongDirective = false;
+ // Check if clause is allowed for the given directive.
+ if (CKind != OMPC_unknown &&
+ !isAllowedClauseForDirective(DKind, CKind, getLangOpts().OpenMP)) {
+ Diag(Tok, diag::err_omp_unexpected_clause)
+ << getOpenMPClauseName(CKind) << getOpenMPDirectiveName(DKind);
+ ErrorFound = true;
+ WrongDirective = true;
+ }
+
+ switch (CKind) {
+ case OMPC_final:
+ case OMPC_num_threads:
+ case OMPC_safelen:
+ case OMPC_simdlen:
+ case OMPC_collapse:
+ case OMPC_ordered:
+ case OMPC_num_teams:
+ case OMPC_thread_limit:
+ case OMPC_priority:
+ case OMPC_grainsize:
+ case OMPC_num_tasks:
+ case OMPC_hint:
+ case OMPC_allocator:
+ case OMPC_depobj:
+ case OMPC_detach:
+ case OMPC_novariants:
+ case OMPC_nocontext:
+ case OMPC_filter:
+ case OMPC_partial:
+ case OMPC_align:
+ case OMPC_message:
+ case OMPC_ompx_dyn_cgroup_mem:
+ // OpenMP [2.5, Restrictions]
+ // At most one num_threads clause can appear on the directive.
+ // OpenMP [2.8.1, simd construct, Restrictions]
+ // Only one safelen clause can appear on a simd directive.
+ // Only one simdlen clause can appear on a simd directive.
+ // Only one collapse clause can appear on a simd directive.
+ // OpenMP [2.11.1, task Construct, Restrictions]
+ // At most one if clause can appear on the directive.
+ // At most one final clause can appear on the directive.
+ // OpenMP [teams Construct, Restrictions]
+ // At most one num_teams clause can appear on the directive.
+ // At most one thread_limit clause can appear on the directive.
+ // OpenMP [2.9.1, task Construct, Restrictions]
+ // At most one priority clause can appear on the directive.
+ // OpenMP [2.9.2, taskloop Construct, Restrictions]
+ // At most one grainsize clause can appear on the directive.
+ // OpenMP [2.9.2, taskloop Construct, Restrictions]
+ // At most one num_tasks clause can appear on the directive.
+ // OpenMP [2.11.3, allocate Directive, Restrictions]
+ // At most one allocator clause can appear on the directive.
+ // OpenMP 5.0, 2.10.1 task Construct, Restrictions.
+ // At most one detach clause can appear on the directive.
+ // OpenMP 5.1, 2.3.6 dispatch Construct, Restrictions.
+ // At most one novariants clause can appear on a dispatch directive.
+ // At most one nocontext clause can appear on a dispatch directive.
+ // OpenMP [5.1, error directive, Restrictions]
+ // At most one message clause can appear on the directive
+ if (!FirstClause) {
+ Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
+ ErrorFound = true;
+ }
+
+ if ((CKind == OMPC_ordered || CKind == OMPC_partial) &&
+ PP.LookAhead(/*N=*/0).isNot(tok::l_paren))
+ Clause = ParseOpenMPClause(CKind, WrongDirective);
+ else if (CKind == OMPC_grainsize || CKind == OMPC_num_tasks)
+ Clause = ParseOpenMPSingleExprWithArgClause(DKind, CKind, WrongDirective);
+ else
+ Clause = ParseOpenMPSingleExprClause(CKind, WrongDirective);
+ break;
+ case OMPC_default:
+ case OMPC_proc_bind:
+ case OMPC_atomic_default_mem_order:
+ case OMPC_at:
+ case OMPC_severity:
+ case OMPC_bind:
+ // OpenMP [2.14.3.1, Restrictions]
+ // Only a single default clause may be specified on a parallel, task or
+ // teams directive.
+ // OpenMP [2.5, parallel Construct, Restrictions]
+ // At most one proc_bind clause can appear on the directive.
+ // OpenMP [5.0, Requires directive, Restrictions]
+ // At most one atomic_default_mem_order clause can appear
+ // on the directive
+ // OpenMP [5.1, error directive, Restrictions]
+ // At most one at clause can appear on the directive
+ // At most one severity clause can appear on the directive
+ // OpenMP 5.1, 2.11.7 loop Construct, Restrictions.
+ // At most one bind clause can appear on a loop directive.
+ if (!FirstClause) {
+ Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
+ ErrorFound = true;
+ }
+
+ Clause = ParseOpenMPSimpleClause(CKind, WrongDirective);
+ break;
+ case OMPC_device:
+ case OMPC_schedule:
+ case OMPC_dist_schedule:
+ case OMPC_defaultmap:
+ case OMPC_order:
+ // OpenMP [2.7.1, Restrictions, p. 3]
+ // Only one schedule clause can appear on a loop directive.
+ // OpenMP 4.5 [2.10.4, Restrictions, p. 106]
+ // At most one defaultmap clause can appear on the directive.
+ // OpenMP 5.0 [2.12.5, target construct, Restrictions]
+ // At most one device clause can appear on the directive.
+ // OpenMP 5.1 [2.11.3, order clause, Restrictions]
+ // At most one order clause may appear on a construct.
+ if ((getLangOpts().OpenMP < 50 || CKind != OMPC_defaultmap) &&
+ (CKind != OMPC_order || getLangOpts().OpenMP >= 51) && !FirstClause) {
+ Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
+ ErrorFound = true;
+ }
+ [[fallthrough]];
+ case OMPC_if:
+ Clause = ParseOpenMPSingleExprWithArgClause(DKind, CKind, WrongDirective);
+ break;
+ case OMPC_nowait:
+ case OMPC_untied:
+ case OMPC_mergeable:
+ case OMPC_read:
+ case OMPC_write:
+ case OMPC_capture:
+ case OMPC_compare:
+ case OMPC_seq_cst:
+ case OMPC_acq_rel:
+ case OMPC_acquire:
+ case OMPC_release:
+ case OMPC_relaxed:
+ case OMPC_threads:
+ case OMPC_simd:
+ case OMPC_nogroup:
+ case OMPC_unified_address:
+ case OMPC_unified_shared_memory:
+ case OMPC_reverse_offload:
+ case OMPC_dynamic_allocators:
+ case OMPC_full:
+ // OpenMP [2.7.1, Restrictions, p. 9]
+ // Only one ordered clause can appear on a loop directive.
+ // OpenMP [2.7.1, Restrictions, C/C++, p. 4]
+ // Only one nowait clause can appear on a for directive.
+ // OpenMP [5.0, Requires directive, Restrictions]
+ // Each of the requires clauses can appear at most once on the directive.
+ if (!FirstClause) {
+ Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
+ ErrorFound = true;
+ }
+
+ Clause = ParseOpenMPClause(CKind, WrongDirective);
+ break;
+ case OMPC_update:
+ if (!FirstClause) {
+ Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
+ ErrorFound = true;
+ }
+
+ Clause = (DKind == OMPD_depobj)
+ ? ParseOpenMPSimpleClause(CKind, WrongDirective)
+ : ParseOpenMPClause(CKind, WrongDirective);
+ break;
+ case OMPC_private:
+ case OMPC_firstprivate:
+ case OMPC_lastprivate:
+ case OMPC_shared:
+ case OMPC_reduction:
+ case OMPC_task_reduction:
+ case OMPC_in_reduction:
+ case OMPC_linear:
+ case OMPC_aligned:
+ case OMPC_copyin:
+ case OMPC_copyprivate:
+ case OMPC_flush:
+ case OMPC_depend:
+ case OMPC_map:
+ case OMPC_to:
+ case OMPC_from:
+ case OMPC_use_device_ptr:
+ case OMPC_use_device_addr:
+ case OMPC_is_device_ptr:
+ case OMPC_has_device_addr:
+ case OMPC_allocate:
+ case OMPC_nontemporal:
+ case OMPC_inclusive:
+ case OMPC_exclusive:
+ case OMPC_affinity:
+ Clause = ParseOpenMPVarListClause(DKind, CKind, WrongDirective);
+ break;
+ case OMPC_sizes:
+ if (!FirstClause) {
+ Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
+ ErrorFound = true;
+ }
+
+ Clause = ParseOpenMPSizesClause();
+ break;
+ case OMPC_uses_allocators:
+ Clause = ParseOpenMPUsesAllocatorClause(DKind);
+ break;
+ case OMPC_destroy:
+ if (DKind != OMPD_interop) {
+ if (!FirstClause) {
+ Diag(Tok, diag::err_omp_more_one_clause)
+ << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
+ ErrorFound = true;
+ }
+ Clause = ParseOpenMPClause(CKind, WrongDirective);
+ break;
+ }
+ [[fallthrough]];
+ case OMPC_init:
+ case OMPC_use:
+ Clause = ParseOpenMPInteropClause(CKind, WrongDirective);
+ break;
+ case OMPC_device_type:
+ case OMPC_unknown:
+ skipUntilPragmaOpenMPEnd(DKind);
+ break;
+ case OMPC_threadprivate:
+ case OMPC_uniform:
+ case OMPC_match:
+ if (!WrongDirective)
+ Diag(Tok, diag::err_omp_unexpected_clause)
+ << getOpenMPClauseName(CKind) << getOpenMPDirectiveName(DKind);
+ SkipUntil(tok::comma, tok::annot_pragma_openmp_end, StopBeforeMatch);
+ break;
+ default:
+ break;
+ }
+ return ErrorFound ? nullptr : Clause;
+}
+
+/// Parses simple expression in parens for single-expression clauses of OpenMP
+/// constructs.
+/// \param RLoc Returned location of right paren.
+ExprResult Parser::ParseOpenMPParensExpr(StringRef ClauseName,
+ SourceLocation &RLoc,
+ bool IsAddressOfOperand) {
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, ClauseName.data()))
+ return ExprError();
+
+ SourceLocation ELoc = Tok.getLocation();
+ ExprResult LHS(
+ ParseCastExpression(AnyCastExpr, IsAddressOfOperand, NotTypeCast));
+ ExprResult Val(ParseRHSOfBinaryExpression(LHS, prec::Conditional));
+ Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false);
+
+ // Parse ')'.
+ RLoc = Tok.getLocation();
+ if (!T.consumeClose())
+ RLoc = T.getCloseLocation();
+
+ return Val;
+}
+
+/// Parsing of OpenMP clauses with single expressions like 'final',
+/// 'collapse', 'safelen', 'num_threads', 'simdlen', 'num_teams',
+/// 'thread_limit', 'simdlen', 'priority', 'grainsize', 'num_tasks', 'hint' or
+/// 'detach'.
+///
+/// final-clause:
+/// 'final' '(' expression ')'
+///
+/// num_threads-clause:
+/// 'num_threads' '(' expression ')'
+///
+/// safelen-clause:
+/// 'safelen' '(' expression ')'
+///
+/// simdlen-clause:
+/// 'simdlen' '(' expression ')'
+///
+/// collapse-clause:
+/// 'collapse' '(' expression ')'
+///
+/// priority-clause:
+/// 'priority' '(' expression ')'
+///
+/// grainsize-clause:
+/// 'grainsize' '(' expression ')'
+///
+/// num_tasks-clause:
+/// 'num_tasks' '(' expression ')'
+///
+/// hint-clause:
+/// 'hint' '(' expression ')'
+///
+/// allocator-clause:
+/// 'allocator' '(' expression ')'
+///
+/// detach-clause:
+/// 'detach' '(' event-handler-expression ')'
+///
+/// align-clause
+/// 'align' '(' positive-integer-constant ')'
+///
+OMPClause *Parser::ParseOpenMPSingleExprClause(OpenMPClauseKind Kind,
+ bool ParseOnly) {
+ SourceLocation Loc = ConsumeToken();
+ SourceLocation LLoc = Tok.getLocation();
+ SourceLocation RLoc;
+
+ ExprResult Val = ParseOpenMPParensExpr(getOpenMPClauseName(Kind), RLoc);
+
+ if (Val.isInvalid())
+ return nullptr;
+
+ if (ParseOnly)
+ return nullptr;
+ return Actions.ActOnOpenMPSingleExprClause(Kind, Val.get(), Loc, LLoc, RLoc);
+}
+
+/// Parse indirect clause for '#pragma omp declare target' directive.
+/// 'indirect' '[' '(' invoked-by-fptr ')' ']'
+/// where invoked-by-fptr is a constant boolean expression that evaluates to
+/// true or false at compile time.
+bool Parser::ParseOpenMPIndirectClause(Sema::DeclareTargetContextInfo &DTCI,
+ bool ParseOnly) {
+ SourceLocation Loc = ConsumeToken();
+ SourceLocation RLoc;
+
+ if (Tok.isNot(tok::l_paren)) {
+ if (ParseOnly)
+ return false;
+ DTCI.Indirect = nullptr;
+ return true;
+ }
+
+ ExprResult Val =
+ ParseOpenMPParensExpr(getOpenMPClauseName(OMPC_indirect), RLoc);
+ if (Val.isInvalid())
+ return false;
+
+ if (ParseOnly)
+ return false;
+
+ if (!Val.get()->isValueDependent() && !Val.get()->isTypeDependent() &&
+ !Val.get()->isInstantiationDependent() &&
+ !Val.get()->containsUnexpandedParameterPack()) {
+ ExprResult Ret = Actions.CheckBooleanCondition(Loc, Val.get());
+ if (Ret.isInvalid())
+ return false;
+ llvm::APSInt Result;
+ Ret = Actions.VerifyIntegerConstantExpression(Val.get(), &Result,
+ Sema::AllowFold);
+ if (Ret.isInvalid())
+ return false;
+ DTCI.Indirect = Val.get();
+ return true;
+ }
+ return false;
+}
+
+/// Parses a comma-separated list of interop-types and a prefer_type list.
+///
+bool Parser::ParseOMPInteropInfo(OMPInteropInfo &InteropInfo,
+ OpenMPClauseKind Kind) {
+ const Token &Tok = getCurToken();
+ bool HasError = false;
+ bool IsTarget = false;
+ bool IsTargetSync = false;
+
+ while (Tok.is(tok::identifier)) {
+ // Currently prefer_type is only allowed with 'init' and it must be first.
+ bool PreferTypeAllowed = Kind == OMPC_init &&
+ InteropInfo.PreferTypes.empty() && !IsTarget &&
+ !IsTargetSync;
+ if (Tok.getIdentifierInfo()->isStr("target")) {
+ // OpenMP 5.1 [2.15.1, interop Construct, Restrictions]
+ // Each interop-type may be specified on an action-clause at most
+ // once.
+ if (IsTarget)
+ Diag(Tok, diag::warn_omp_more_one_interop_type) << "target";
+ IsTarget = true;
+ ConsumeToken();
+ } else if (Tok.getIdentifierInfo()->isStr("targetsync")) {
+ if (IsTargetSync)
+ Diag(Tok, diag::warn_omp_more_one_interop_type) << "targetsync";
+ IsTargetSync = true;
+ ConsumeToken();
+ } else if (Tok.getIdentifierInfo()->isStr("prefer_type") &&
+ PreferTypeAllowed) {
+ ConsumeToken();
+ BalancedDelimiterTracker PT(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ if (PT.expectAndConsume(diag::err_expected_lparen_after, "prefer_type"))
+ HasError = true;
+
+ while (Tok.isNot(tok::r_paren)) {
+ SourceLocation Loc = Tok.getLocation();
+ ExprResult LHS = ParseCastExpression(AnyCastExpr);
+ ExprResult PTExpr = Actions.CorrectDelayedTyposInExpr(
+ ParseRHSOfBinaryExpression(LHS, prec::Conditional));
+ PTExpr = Actions.ActOnFinishFullExpr(PTExpr.get(), Loc,
+ /*DiscardedValue=*/false);
+ if (PTExpr.isUsable()) {
+ InteropInfo.PreferTypes.push_back(PTExpr.get());
+ } else {
+ HasError = true;
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ }
+ PT.consumeClose();
+ } else {
+ HasError = true;
+ Diag(Tok, diag::err_omp_expected_interop_type);
+ ConsumeToken();
+ }
+ if (!Tok.is(tok::comma))
+ break;
+ ConsumeToken();
+ }
+
+ if (!HasError && !IsTarget && !IsTargetSync) {
+ Diag(Tok, diag::err_omp_expected_interop_type);
+ HasError = true;
+ }
+
+ if (Kind == OMPC_init) {
+ if (Tok.isNot(tok::colon) && (IsTarget || IsTargetSync))
+ Diag(Tok, diag::warn_pragma_expected_colon) << "interop types";
+ if (Tok.is(tok::colon))
+ ConsumeToken();
+ }
+
+ // As of OpenMP 5.1,there are two interop-types, "target" and
+ // "targetsync". Either or both are allowed for a single interop.
+ InteropInfo.IsTarget = IsTarget;
+ InteropInfo.IsTargetSync = IsTargetSync;
+
+ return HasError;
+}
+
+/// Parsing of OpenMP clauses that use an interop-var.
+///
+/// init-clause:
+/// init([interop-modifier, ]interop-type[[, interop-type] ... ]:interop-var)
+///
+/// destroy-clause:
+/// destroy(interop-var)
+///
+/// use-clause:
+/// use(interop-var)
+///
+/// interop-modifier:
+/// prefer_type(preference-list)
+///
+/// preference-list:
+/// foreign-runtime-id [, foreign-runtime-id]...
+///
+/// foreign-runtime-id:
+/// <string-literal> | <constant-integral-expression>
+///
+/// interop-type:
+/// target | targetsync
+///
+OMPClause *Parser::ParseOpenMPInteropClause(OpenMPClauseKind Kind,
+ bool ParseOnly) {
+ SourceLocation Loc = ConsumeToken();
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPClauseName(Kind).data()))
+ return nullptr;
+
+ bool InteropError = false;
+ OMPInteropInfo InteropInfo;
+ if (Kind == OMPC_init)
+ InteropError = ParseOMPInteropInfo(InteropInfo, OMPC_init);
+
+ // Parse the variable.
+ SourceLocation VarLoc = Tok.getLocation();
+ ExprResult InteropVarExpr =
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+ if (!InteropVarExpr.isUsable()) {
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+
+ // Parse ')'.
+ SourceLocation RLoc = Tok.getLocation();
+ if (!T.consumeClose())
+ RLoc = T.getCloseLocation();
+
+ if (ParseOnly || !InteropVarExpr.isUsable() || InteropError)
+ return nullptr;
+
+ if (Kind == OMPC_init)
+ return Actions.ActOnOpenMPInitClause(InteropVarExpr.get(), InteropInfo, Loc,
+ T.getOpenLocation(), VarLoc, RLoc);
+ if (Kind == OMPC_use)
+ return Actions.ActOnOpenMPUseClause(InteropVarExpr.get(), Loc,
+ T.getOpenLocation(), VarLoc, RLoc);
+
+ if (Kind == OMPC_destroy)
+ return Actions.ActOnOpenMPDestroyClause(InteropVarExpr.get(), Loc,
+ T.getOpenLocation(), VarLoc, RLoc);
+
+ llvm_unreachable("Unexpected interop variable clause.");
+}
+
+/// Parsing of simple OpenMP clauses like 'default' or 'proc_bind'.
+///
+/// default-clause:
+/// 'default' '(' 'none' | 'shared' | 'private' | 'firstprivate' ')'
+///
+/// proc_bind-clause:
+/// 'proc_bind' '(' 'master' | 'close' | 'spread' ')'
+///
+/// bind-clause:
+/// 'bind' '(' 'teams' | 'parallel' | 'thread' ')'
+///
+/// update-clause:
+/// 'update' '(' 'in' | 'out' | 'inout' | 'mutexinoutset' |
+/// 'inoutset' ')'
+///
+OMPClause *Parser::ParseOpenMPSimpleClause(OpenMPClauseKind Kind,
+ bool ParseOnly) {
+ std::optional<SimpleClauseData> Val = parseOpenMPSimpleClause(*this, Kind);
+ if (!Val || ParseOnly)
+ return nullptr;
+ if (getLangOpts().OpenMP < 51 && Kind == OMPC_default &&
+ (static_cast<DefaultKind>(Val->Type) == OMP_DEFAULT_private ||
+ static_cast<DefaultKind>(Val->Type) ==
+ OMP_DEFAULT_firstprivate)) {
+ Diag(Val->LOpen, diag::err_omp_invalid_dsa)
+ << getOpenMPClauseName(static_cast<DefaultKind>(Val->Type) ==
+ OMP_DEFAULT_private
+ ? OMPC_private
+ : OMPC_firstprivate)
+ << getOpenMPClauseName(OMPC_default) << "5.1";
+ return nullptr;
+ }
+ return Actions.ActOnOpenMPSimpleClause(Kind, Val->Type,
+ Val->TypeLoc, Val->LOpen,
+ Val->Loc, Val->RLoc);
+}
+
+/// Parsing of OpenMP clauses like 'ordered'.
+///
+/// ordered-clause:
+/// 'ordered'
+///
+/// nowait-clause:
+/// 'nowait'
+///
+/// untied-clause:
+/// 'untied'
+///
+/// mergeable-clause:
+/// 'mergeable'
+///
+/// read-clause:
+/// 'read'
+///
+/// threads-clause:
+/// 'threads'
+///
+/// simd-clause:
+/// 'simd'
+///
+/// nogroup-clause:
+/// 'nogroup'
+///
+OMPClause *Parser::ParseOpenMPClause(OpenMPClauseKind Kind, bool ParseOnly) {
+ SourceLocation Loc = Tok.getLocation();
+ ConsumeAnyToken();
+
+ if (ParseOnly)
+ return nullptr;
+ return Actions.ActOnOpenMPClause(Kind, Loc, Tok.getLocation());
+}
+
+/// Parsing of OpenMP clauses with single expressions and some additional
+/// argument like 'schedule' or 'dist_schedule'.
+///
+/// schedule-clause:
+/// 'schedule' '(' [ modifier [ ',' modifier ] ':' ] kind [',' expression ]
+/// ')'
+///
+/// if-clause:
+/// 'if' '(' [ directive-name-modifier ':' ] expression ')'
+///
+/// defaultmap:
+/// 'defaultmap' '(' modifier [ ':' kind ] ')'
+///
+/// device-clause:
+/// 'device' '(' [ device-modifier ':' ] expression ')'
+///
+OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPDirectiveKind DKind,
+ OpenMPClauseKind Kind,
+ bool ParseOnly) {
+ SourceLocation Loc = ConsumeToken();
+ SourceLocation DelimLoc;
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPClauseName(Kind).data()))
+ return nullptr;
+
+ ExprResult Val;
+ SmallVector<unsigned, 4> Arg;
+ SmallVector<SourceLocation, 4> KLoc;
+ if (Kind == OMPC_schedule) {
+ enum { Modifier1, Modifier2, ScheduleKind, NumberOfElements };
+ Arg.resize(NumberOfElements);
+ KLoc.resize(NumberOfElements);
+ Arg[Modifier1] = OMPC_SCHEDULE_MODIFIER_unknown;
+ Arg[Modifier2] = OMPC_SCHEDULE_MODIFIER_unknown;
+ Arg[ScheduleKind] = OMPC_SCHEDULE_unknown;
+ unsigned KindModifier = getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts());
+ if (KindModifier > OMPC_SCHEDULE_unknown) {
+ // Parse 'modifier'
+ Arg[Modifier1] = KindModifier;
+ KLoc[Modifier1] = Tok.getLocation();
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ if (Tok.is(tok::comma)) {
+ // Parse ',' 'modifier'
+ ConsumeAnyToken();
+ KindModifier = getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts());
+ Arg[Modifier2] = KindModifier > OMPC_SCHEDULE_unknown
+ ? KindModifier
+ : (unsigned)OMPC_SCHEDULE_unknown;
+ KLoc[Modifier2] = Tok.getLocation();
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ }
+ // Parse ':'
+ if (Tok.is(tok::colon))
+ ConsumeAnyToken();
+ else
+ Diag(Tok, diag::warn_pragma_expected_colon) << "schedule modifier";
+ KindModifier = getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts());
+ }
+ Arg[ScheduleKind] = KindModifier;
+ KLoc[ScheduleKind] = Tok.getLocation();
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ if ((Arg[ScheduleKind] == OMPC_SCHEDULE_static ||
+ Arg[ScheduleKind] == OMPC_SCHEDULE_dynamic ||
+ Arg[ScheduleKind] == OMPC_SCHEDULE_guided) &&
+ Tok.is(tok::comma))
+ DelimLoc = ConsumeAnyToken();
+ } else if (Kind == OMPC_dist_schedule) {
+ Arg.push_back(getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts()));
+ KLoc.push_back(Tok.getLocation());
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ if (Arg.back() == OMPC_DIST_SCHEDULE_static && Tok.is(tok::comma))
+ DelimLoc = ConsumeAnyToken();
+ } else if (Kind == OMPC_defaultmap) {
+ // Get a defaultmap modifier
+ unsigned Modifier = getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts());
+ // Set defaultmap modifier to unknown if it is either scalar, aggregate, or
+ // pointer
+ if (Modifier < OMPC_DEFAULTMAP_MODIFIER_unknown)
+ Modifier = OMPC_DEFAULTMAP_MODIFIER_unknown;
+ Arg.push_back(Modifier);
+ KLoc.push_back(Tok.getLocation());
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ // Parse ':'
+ if (Tok.is(tok::colon) || getLangOpts().OpenMP < 50) {
+ if (Tok.is(tok::colon))
+ ConsumeAnyToken();
+ else if (Arg.back() != OMPC_DEFAULTMAP_MODIFIER_unknown)
+ Diag(Tok, diag::warn_pragma_expected_colon) << "defaultmap modifier";
+ // Get a defaultmap kind
+ Arg.push_back(getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts()));
+ KLoc.push_back(Tok.getLocation());
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ } else {
+ Arg.push_back(OMPC_DEFAULTMAP_unknown);
+ KLoc.push_back(SourceLocation());
+ }
+ } else if (Kind == OMPC_order) {
+ enum { Modifier, OrderKind, NumberOfElements };
+ Arg.resize(NumberOfElements);
+ KLoc.resize(NumberOfElements);
+ Arg[Modifier] = OMPC_ORDER_MODIFIER_unknown;
+ Arg[OrderKind] = OMPC_ORDER_unknown;
+ unsigned KindModifier = getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts());
+ if (KindModifier > OMPC_ORDER_unknown) {
+ // Parse 'modifier'
+ Arg[Modifier] = KindModifier;
+ KLoc[Modifier] = Tok.getLocation();
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ // Parse ':'
+ if (Tok.is(tok::colon))
+ ConsumeAnyToken();
+ else
+ Diag(Tok, diag::warn_pragma_expected_colon) << "order modifier";
+ KindModifier = getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts());
+ }
+ Arg[OrderKind] = KindModifier;
+ KLoc[OrderKind] = Tok.getLocation();
+ if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))
+ ConsumeAnyToken();
+ } else if (Kind == OMPC_device) {
+ // Only target executable directives support extended device construct.
+ if (isOpenMPTargetExecutionDirective(DKind) && getLangOpts().OpenMP >= 50 &&
+ NextToken().is(tok::colon)) {
+ // Parse optional <device modifier> ':'
+ Arg.push_back(getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok), getLangOpts()));
+ KLoc.push_back(Tok.getLocation());
+ ConsumeAnyToken();
+ // Parse ':'
+ ConsumeAnyToken();
+ } else {
+ Arg.push_back(OMPC_DEVICE_unknown);
+ KLoc.emplace_back();
+ }
+ } else if (Kind == OMPC_grainsize) {
+ // Parse optional <grainsize modifier> ':'
+ OpenMPGrainsizeClauseModifier Modifier =
+ static_cast<OpenMPGrainsizeClauseModifier>(getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok),
+ getLangOpts()));
+ if (getLangOpts().OpenMP >= 51) {
+ if (NextToken().is(tok::colon)) {
+ Arg.push_back(Modifier);
+ KLoc.push_back(Tok.getLocation());
+ // Parse modifier
+ ConsumeAnyToken();
+ // Parse ':'
+ ConsumeAnyToken();
+ } else {
+ if (Modifier == OMPC_GRAINSIZE_strict) {
+ Diag(Tok, diag::err_modifier_expected_colon) << "strict";
+ // Parse modifier
+ ConsumeAnyToken();
+ }
+ Arg.push_back(OMPC_GRAINSIZE_unknown);
+ KLoc.emplace_back();
+ }
+ } else {
+ Arg.push_back(OMPC_GRAINSIZE_unknown);
+ KLoc.emplace_back();
+ }
+ } else if (Kind == OMPC_num_tasks) {
+ // Parse optional <num_tasks modifier> ':'
+ OpenMPNumTasksClauseModifier Modifier =
+ static_cast<OpenMPNumTasksClauseModifier>(getOpenMPSimpleClauseType(
+ Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok),
+ getLangOpts()));
+ if (getLangOpts().OpenMP >= 51) {
+ if (NextToken().is(tok::colon)) {
+ Arg.push_back(Modifier);
+ KLoc.push_back(Tok.getLocation());
+ // Parse modifier
+ ConsumeAnyToken();
+ // Parse ':'
+ ConsumeAnyToken();
+ } else {
+ if (Modifier == OMPC_NUMTASKS_strict) {
+ Diag(Tok, diag::err_modifier_expected_colon) << "strict";
+ // Parse modifier
+ ConsumeAnyToken();
+ }
+ Arg.push_back(OMPC_NUMTASKS_unknown);
+ KLoc.emplace_back();
+ }
+ } else {
+ Arg.push_back(OMPC_NUMTASKS_unknown);
+ KLoc.emplace_back();
+ }
+ } else {
+ assert(Kind == OMPC_if);
+ KLoc.push_back(Tok.getLocation());
+ TentativeParsingAction TPA(*this);
+ auto DK = parseOpenMPDirectiveKind(*this);
+ Arg.push_back(DK);
+ if (DK != OMPD_unknown) {
+ ConsumeToken();
+ if (Tok.is(tok::colon) && getLangOpts().OpenMP > 40) {
+ TPA.Commit();
+ DelimLoc = ConsumeToken();
+ } else {
+ TPA.Revert();
+ Arg.back() = unsigned(OMPD_unknown);
+ }
+ } else {
+ TPA.Revert();
+ }
+ }
+
+ bool NeedAnExpression = (Kind == OMPC_schedule && DelimLoc.isValid()) ||
+ (Kind == OMPC_dist_schedule && DelimLoc.isValid()) ||
+ Kind == OMPC_if || Kind == OMPC_device ||
+ Kind == OMPC_grainsize || Kind == OMPC_num_tasks;
+ if (NeedAnExpression) {
+ SourceLocation ELoc = Tok.getLocation();
+ ExprResult LHS(ParseCastExpression(AnyCastExpr, false, NotTypeCast));
+ Val = ParseRHSOfBinaryExpression(LHS, prec::Conditional);
+ Val =
+ Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false);
+ }
+
+ // Parse ')'.
+ SourceLocation RLoc = Tok.getLocation();
+ if (!T.consumeClose())
+ RLoc = T.getCloseLocation();
+
+ if (NeedAnExpression && Val.isInvalid())
+ return nullptr;
+
+ if (ParseOnly)
+ return nullptr;
+ return Actions.ActOnOpenMPSingleExprWithArgClause(
+ Kind, Arg, Val.get(), Loc, T.getOpenLocation(), KLoc, DelimLoc, RLoc);
+}
+
+static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec,
+ UnqualifiedId &ReductionId) {
+ if (ReductionIdScopeSpec.isEmpty()) {
+ auto OOK = OO_None;
+ switch (P.getCurToken().getKind()) {
+ case tok::plus:
+ OOK = OO_Plus;
+ break;
+ case tok::minus:
+ OOK = OO_Minus;
+ break;
+ case tok::star:
+ OOK = OO_Star;
+ break;
+ case tok::amp:
+ OOK = OO_Amp;
+ break;
+ case tok::pipe:
+ OOK = OO_Pipe;
+ break;
+ case tok::caret:
+ OOK = OO_Caret;
+ break;
+ case tok::ampamp:
+ OOK = OO_AmpAmp;
+ break;
+ case tok::pipepipe:
+ OOK = OO_PipePipe;
+ break;
+ default:
+ break;
+ }
+ if (OOK != OO_None) {
+ SourceLocation OpLoc = P.ConsumeToken();
+ SourceLocation SymbolLocations[] = {OpLoc, OpLoc, SourceLocation()};
+ ReductionId.setOperatorFunctionId(OpLoc, OOK, SymbolLocations);
+ return false;
+ }
+ }
+ return P.ParseUnqualifiedId(
+ ReductionIdScopeSpec, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext*/ false,
+ /*AllowDestructorName*/ false,
+ /*AllowConstructorName*/ false,
+ /*AllowDeductionGuide*/ false, nullptr, ReductionId);
+}
+
+/// Checks if the token is a valid map-type-modifier.
+/// FIXME: It will return an OpenMPMapClauseKind if that's what it parses.
+static OpenMPMapModifierKind isMapModifier(Parser &P) {
+ Token Tok = P.getCurToken();
+ if (!Tok.is(tok::identifier))
+ return OMPC_MAP_MODIFIER_unknown;
+
+ Preprocessor &PP = P.getPreprocessor();
+ OpenMPMapModifierKind TypeModifier =
+ static_cast<OpenMPMapModifierKind>(getOpenMPSimpleClauseType(
+ OMPC_map, PP.getSpelling(Tok), P.getLangOpts()));
+ return TypeModifier;
+}
+
+/// Parse the mapper modifier in map, to, and from clauses.
+bool Parser::parseMapperModifier(Sema::OpenMPVarListDataTy &Data) {
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::colon);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "mapper")) {
+ SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ return true;
+ }
+ // Parse mapper-identifier
+ if (getLangOpts().CPlusPlus)
+ ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec,
+ /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false);
+ if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_default)) {
+ Diag(Tok.getLocation(), diag::err_omp_mapper_illegal_identifier);
+ SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ return true;
+ }
+ auto &DeclNames = Actions.getASTContext().DeclarationNames;
+ Data.ReductionOrMapperId = DeclarationNameInfo(
+ DeclNames.getIdentifier(Tok.getIdentifierInfo()), Tok.getLocation());
+ ConsumeToken();
+ // Parse ')'.
+ return T.consumeClose();
+}
+
+/// Parse map-type-modifiers in map clause.
+/// map([ [map-type-modifier[,] [map-type-modifier[,] ...] map-type : ] list)
+/// where, map-type-modifier ::= always | close | mapper(mapper-identifier) |
+/// present
+bool Parser::parseMapTypeModifiers(Sema::OpenMPVarListDataTy &Data) {
+ while (getCurToken().isNot(tok::colon)) {
+ OpenMPMapModifierKind TypeModifier = isMapModifier(*this);
+ if (TypeModifier == OMPC_MAP_MODIFIER_always ||
+ TypeModifier == OMPC_MAP_MODIFIER_close ||
+ TypeModifier == OMPC_MAP_MODIFIER_present ||
+ TypeModifier == OMPC_MAP_MODIFIER_ompx_hold) {
+ Data.MapTypeModifiers.push_back(TypeModifier);
+ Data.MapTypeModifiersLoc.push_back(Tok.getLocation());
+ ConsumeToken();
+ } else if (TypeModifier == OMPC_MAP_MODIFIER_mapper) {
+ Data.MapTypeModifiers.push_back(TypeModifier);
+ Data.MapTypeModifiersLoc.push_back(Tok.getLocation());
+ ConsumeToken();
+ if (parseMapperModifier(Data))
+ return true;
+ } else {
+ // For the case of unknown map-type-modifier or a map-type.
+ // Map-type is followed by a colon; the function returns when it
+ // encounters a token followed by a colon.
+ if (Tok.is(tok::comma)) {
+ Diag(Tok, diag::err_omp_map_type_modifier_missing);
+ ConsumeToken();
+ continue;
+ }
+ // Potential map-type token as it is followed by a colon.
+ if (PP.LookAhead(0).is(tok::colon))
+ return false;
+ Diag(Tok, diag::err_omp_unknown_map_type_modifier)
+ << (getLangOpts().OpenMP >= 51 ? (getLangOpts().OpenMP >= 52 ? 2 : 1)
+ : 0)
+ << getLangOpts().OpenMPExtensions;
+ ConsumeToken();
+ }
+ if (getCurToken().is(tok::comma))
+ ConsumeToken();
+ }
+ return false;
+}
+
+/// Checks if the token is a valid map-type.
+/// FIXME: It will return an OpenMPMapModifierKind if that's what it parses.
+static OpenMPMapClauseKind isMapType(Parser &P) {
+ Token Tok = P.getCurToken();
+ // The map-type token can be either an identifier or the C++ delete keyword.
+ if (!Tok.isOneOf(tok::identifier, tok::kw_delete))
+ return OMPC_MAP_unknown;
+ Preprocessor &PP = P.getPreprocessor();
+ OpenMPMapClauseKind MapType =
+ static_cast<OpenMPMapClauseKind>(getOpenMPSimpleClauseType(
+ OMPC_map, PP.getSpelling(Tok), P.getLangOpts()));
+ return MapType;
+}
+
+/// Parse map-type in map clause.
+/// map([ [map-type-modifier[,] [map-type-modifier[,] ...] map-type : ] list)
+/// where, map-type ::= to | from | tofrom | alloc | release | delete
+static void parseMapType(Parser &P, Sema::OpenMPVarListDataTy &Data) {
+ Token Tok = P.getCurToken();
+ if (Tok.is(tok::colon)) {
+ P.Diag(Tok, diag::err_omp_map_type_missing);
+ return;
+ }
+ Data.ExtraModifier = isMapType(P);
+ if (Data.ExtraModifier == OMPC_MAP_unknown)
+ P.Diag(Tok, diag::err_omp_unknown_map_type);
+ P.ConsumeToken();
+}
+
+/// Parses simple expression in parens for single-expression clauses of OpenMP
+/// constructs.
+ExprResult Parser::ParseOpenMPIteratorsExpr() {
+ assert(Tok.is(tok::identifier) && PP.getSpelling(Tok) == "iterator" &&
+ "Expected 'iterator' token.");
+ SourceLocation IteratorKwLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "iterator"))
+ return ExprError();
+
+ SourceLocation LLoc = T.getOpenLocation();
+ SmallVector<Sema::OMPIteratorData, 4> Data;
+ while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) {
+ // Check if the type parsing is required.
+ ParsedType IteratorType;
+ if (Tok.isNot(tok::identifier) || NextToken().isNot(tok::equal)) {
+ // identifier '=' is not found - parse type.
+ TypeResult TR = ParseTypeName();
+ if (TR.isInvalid()) {
+ T.skipToEnd();
+ return ExprError();
+ }
+ IteratorType = TR.get();
+ }
+
+ // Parse identifier.
+ IdentifierInfo *II = nullptr;
+ SourceLocation IdLoc;
+ if (Tok.is(tok::identifier)) {
+ II = Tok.getIdentifierInfo();
+ IdLoc = ConsumeToken();
+ } else {
+ Diag(Tok, diag::err_expected_unqualified_id) << 0;
+ }
+
+ // Parse '='.
+ SourceLocation AssignLoc;
+ if (Tok.is(tok::equal))
+ AssignLoc = ConsumeToken();
+ else
+ Diag(Tok, diag::err_omp_expected_equal_in_iterator);
+
+ // Parse range-specification - <begin> ':' <end> [ ':' <step> ]
+ ColonProtectionRAIIObject ColonRAII(*this);
+ // Parse <begin>
+ SourceLocation Loc = Tok.getLocation();
+ ExprResult LHS = ParseCastExpression(AnyCastExpr);
+ ExprResult Begin = Actions.CorrectDelayedTyposInExpr(
+ ParseRHSOfBinaryExpression(LHS, prec::Conditional));
+ Begin = Actions.ActOnFinishFullExpr(Begin.get(), Loc,
+ /*DiscardedValue=*/false);
+ // Parse ':'.
+ SourceLocation ColonLoc;
+ if (Tok.is(tok::colon))
+ ColonLoc = ConsumeToken();
+
+ // Parse <end>
+ Loc = Tok.getLocation();
+ LHS = ParseCastExpression(AnyCastExpr);
+ ExprResult End = Actions.CorrectDelayedTyposInExpr(
+ ParseRHSOfBinaryExpression(LHS, prec::Conditional));
+ End = Actions.ActOnFinishFullExpr(End.get(), Loc,
+ /*DiscardedValue=*/false);
+
+ SourceLocation SecColonLoc;
+ ExprResult Step;
+ // Parse optional step.
+ if (Tok.is(tok::colon)) {
+ // Parse ':'
+ SecColonLoc = ConsumeToken();
+ // Parse <step>
+ Loc = Tok.getLocation();
+ LHS = ParseCastExpression(AnyCastExpr);
+ Step = Actions.CorrectDelayedTyposInExpr(
+ ParseRHSOfBinaryExpression(LHS, prec::Conditional));
+ Step = Actions.ActOnFinishFullExpr(Step.get(), Loc,
+ /*DiscardedValue=*/false);
+ }
+
+ // Parse ',' or ')'
+ if (Tok.isNot(tok::comma) && Tok.isNot(tok::r_paren))
+ Diag(Tok, diag::err_omp_expected_punc_after_iterator);
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+
+ Sema::OMPIteratorData &D = Data.emplace_back();
+ D.DeclIdent = II;
+ D.DeclIdentLoc = IdLoc;
+ D.Type = IteratorType;
+ D.AssignLoc = AssignLoc;
+ D.ColonLoc = ColonLoc;
+ D.SecColonLoc = SecColonLoc;
+ D.Range.Begin = Begin.get();
+ D.Range.End = End.get();
+ D.Range.Step = Step.get();
+ }
+
+ // Parse ')'.
+ SourceLocation RLoc = Tok.getLocation();
+ if (!T.consumeClose())
+ RLoc = T.getCloseLocation();
+
+ return Actions.ActOnOMPIteratorExpr(getCurScope(), IteratorKwLoc, LLoc, RLoc,
+ Data);
+}
+
+bool Parser::ParseOpenMPReservedLocator(OpenMPClauseKind Kind,
+ Sema::OpenMPVarListDataTy &Data,
+ const LangOptions &LangOpts) {
+ // Currently the only reserved locator is 'omp_all_memory' which is only
+ // allowed on a depend clause.
+ if (Kind != OMPC_depend || LangOpts.OpenMP < 51)
+ return false;
+
+ if (Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo()->isStr("omp_all_memory")) {
+
+ if (Data.ExtraModifier == OMPC_DEPEND_outallmemory ||
+ Data.ExtraModifier == OMPC_DEPEND_inoutallmemory)
+ Diag(Tok, diag::warn_omp_more_one_omp_all_memory);
+ else if (Data.ExtraModifier != OMPC_DEPEND_out &&
+ Data.ExtraModifier != OMPC_DEPEND_inout)
+ Diag(Tok, diag::err_omp_requires_out_inout_depend_type);
+ else
+ Data.ExtraModifier = Data.ExtraModifier == OMPC_DEPEND_out
+ ? OMPC_DEPEND_outallmemory
+ : OMPC_DEPEND_inoutallmemory;
+ ConsumeToken();
+ return true;
+ }
+ return false;
+}
+
+/// Parses clauses with list.
+bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
+ OpenMPClauseKind Kind,
+ SmallVectorImpl<Expr *> &Vars,
+ Sema::OpenMPVarListDataTy &Data) {
+ UnqualifiedId UnqualifiedReductionId;
+ bool InvalidReductionId = false;
+ bool IsInvalidMapperModifier = false;
+
+ // Parse '('.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ getOpenMPClauseName(Kind).data()))
+ return true;
+
+ bool HasIterator = false;
+ bool InvalidIterator = false;
+ bool NeedRParenForLinear = false;
+ BalancedDelimiterTracker LinearT(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ // Handle reduction-identifier for reduction clause.
+ if (Kind == OMPC_reduction || Kind == OMPC_task_reduction ||
+ Kind == OMPC_in_reduction) {
+ Data.ExtraModifier = OMPC_REDUCTION_unknown;
+ if (Kind == OMPC_reduction && getLangOpts().OpenMP >= 50 &&
+ (Tok.is(tok::identifier) || Tok.is(tok::kw_default)) &&
+ NextToken().is(tok::comma)) {
+ // Parse optional reduction modifier.
+ Data.ExtraModifier =
+ getOpenMPSimpleClauseType(Kind, PP.getSpelling(Tok), getLangOpts());
+ Data.ExtraModifierLoc = Tok.getLocation();
+ ConsumeToken();
+ assert(Tok.is(tok::comma) && "Expected comma.");
+ (void)ConsumeToken();
+ }
+ ColonProtectionRAIIObject ColonRAII(*this);
+ if (getLangOpts().CPlusPlus)
+ ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec,
+ /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false);
+ InvalidReductionId = ParseReductionId(
+ *this, Data.ReductionOrMapperIdScopeSpec, UnqualifiedReductionId);
+ if (InvalidReductionId) {
+ SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+ if (Tok.is(tok::colon))
+ Data.ColonLoc = ConsumeToken();
+ else
+ Diag(Tok, diag::warn_pragma_expected_colon) << "reduction identifier";
+ if (!InvalidReductionId)
+ Data.ReductionOrMapperId =
+ Actions.GetNameFromUnqualifiedId(UnqualifiedReductionId);
+ } else if (Kind == OMPC_depend) {
+ if (getLangOpts().OpenMP >= 50) {
+ if (Tok.is(tok::identifier) && PP.getSpelling(Tok) == "iterator") {
+ // Handle optional dependence modifier.
+ // iterator(iterators-definition)
+ // where iterators-definition is iterator-specifier [,
+ // iterators-definition ]
+ // where iterator-specifier is [ iterator-type ] identifier =
+ // range-specification
+ HasIterator = true;
+ EnterScope(Scope::OpenMPDirectiveScope | Scope::DeclScope);
+ ExprResult IteratorRes = ParseOpenMPIteratorsExpr();
+ Data.DepModOrTailExpr = IteratorRes.get();
+ // Parse ','
+ ExpectAndConsume(tok::comma);
+ }
+ }
+ // Handle dependency type for depend clause.
+ ColonProtectionRAIIObject ColonRAII(*this);
+ Data.ExtraModifier = getOpenMPSimpleClauseType(
+ Kind, Tok.is(tok::identifier) ? PP.getSpelling(Tok) : "",
+ getLangOpts());
+ Data.ExtraModifierLoc = Tok.getLocation();
+ if (Data.ExtraModifier == OMPC_DEPEND_unknown) {
+ SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ } else {
+ ConsumeToken();
+ // Special processing for depend(source) clause.
+ if (DKind == OMPD_ordered && Data.ExtraModifier == OMPC_DEPEND_source) {
+ // Parse ')'.
+ T.consumeClose();
+ return false;
+ }
+ }
+ if (Tok.is(tok::colon)) {
+ Data.ColonLoc = ConsumeToken();
+ } else {
+ Diag(Tok, DKind == OMPD_ordered ? diag::warn_pragma_expected_colon_r_paren
+ : diag::warn_pragma_expected_colon)
+ << "dependency type";
+ }
+ } else if (Kind == OMPC_linear) {
+ // Try to parse modifier if any.
+ Data.ExtraModifier = OMPC_LINEAR_val;
+ if (Tok.is(tok::identifier) && PP.LookAhead(0).is(tok::l_paren)) {
+ Data.ExtraModifier =
+ getOpenMPSimpleClauseType(Kind, PP.getSpelling(Tok), getLangOpts());
+ Data.ExtraModifierLoc = ConsumeToken();
+ LinearT.consumeOpen();
+ NeedRParenForLinear = true;
+ }
+ } else if (Kind == OMPC_lastprivate) {
+ // Try to parse modifier if any.
+ Data.ExtraModifier = OMPC_LASTPRIVATE_unknown;
+ // Conditional modifier allowed only in OpenMP 5.0 and not supported in
+ // distribute and taskloop based directives.
+ if ((getLangOpts().OpenMP >= 50 && !isOpenMPDistributeDirective(DKind) &&
+ !isOpenMPTaskLoopDirective(DKind)) &&
+ Tok.is(tok::identifier) && PP.LookAhead(0).is(tok::colon)) {
+ Data.ExtraModifier =
+ getOpenMPSimpleClauseType(Kind, PP.getSpelling(Tok), getLangOpts());
+ Data.ExtraModifierLoc = Tok.getLocation();
+ ConsumeToken();
+ assert(Tok.is(tok::colon) && "Expected colon.");
+ Data.ColonLoc = ConsumeToken();
+ }
+ } else if (Kind == OMPC_map) {
+ // Handle optional iterator map modifier.
+ if (Tok.is(tok::identifier) && PP.getSpelling(Tok) == "iterator") {
+ HasIterator = true;
+ EnterScope(Scope::OpenMPDirectiveScope | Scope::DeclScope);
+ Data.MapTypeModifiers.push_back(OMPC_MAP_MODIFIER_iterator);
+ Data.MapTypeModifiersLoc.push_back(Tok.getLocation());
+ ExprResult IteratorRes = ParseOpenMPIteratorsExpr();
+ Data.IteratorExpr = IteratorRes.get();
+ // Parse ','
+ ExpectAndConsume(tok::comma);
+ if (getLangOpts().OpenMP < 52) {
+ Diag(Tok, diag::err_omp_unknown_map_type_modifier)
+ << (getLangOpts().OpenMP >= 51 ? 1 : 0)
+ << getLangOpts().OpenMPExtensions;
+ InvalidIterator = true;
+ }
+ }
+ // Handle map type for map clause.
+ ColonProtectionRAIIObject ColonRAII(*this);
+
+ // The first identifier may be a list item, a map-type or a
+ // map-type-modifier. The map-type can also be delete which has the same
+ // spelling of the C++ delete keyword.
+ Data.ExtraModifier = OMPC_MAP_unknown;
+ Data.ExtraModifierLoc = Tok.getLocation();
+
+ // Check for presence of a colon in the map clause.
+ TentativeParsingAction TPA(*this);
+ bool ColonPresent = false;
+ if (SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch)) {
+ if (Tok.is(tok::colon))
+ ColonPresent = true;
+ }
+ TPA.Revert();
+ // Only parse map-type-modifier[s] and map-type if a colon is present in
+ // the map clause.
+ if (ColonPresent) {
+ IsInvalidMapperModifier = parseMapTypeModifiers(Data);
+ if (!IsInvalidMapperModifier)
+ parseMapType(*this, Data);
+ else
+ SkipUntil(tok::colon, tok::annot_pragma_openmp_end, StopBeforeMatch);
+ }
+ if (Data.ExtraModifier == OMPC_MAP_unknown) {
+ Data.ExtraModifier = OMPC_MAP_tofrom;
+ if (getLangOpts().OpenMP >= 52) {
+ if (DKind == OMPD_target_enter_data)
+ Data.ExtraModifier = OMPC_MAP_to;
+ else if (DKind == OMPD_target_exit_data)
+ Data.ExtraModifier = OMPC_MAP_from;
+ }
+ Data.IsMapTypeImplicit = true;
+ }
+
+ if (Tok.is(tok::colon))
+ Data.ColonLoc = ConsumeToken();
+ } else if (Kind == OMPC_to || Kind == OMPC_from) {
+ while (Tok.is(tok::identifier)) {
+ auto Modifier = static_cast<OpenMPMotionModifierKind>(
+ getOpenMPSimpleClauseType(Kind, PP.getSpelling(Tok), getLangOpts()));
+ if (Modifier == OMPC_MOTION_MODIFIER_unknown)
+ break;
+ Data.MotionModifiers.push_back(Modifier);
+ Data.MotionModifiersLoc.push_back(Tok.getLocation());
+ ConsumeToken();
+ if (Modifier == OMPC_MOTION_MODIFIER_mapper) {
+ IsInvalidMapperModifier = parseMapperModifier(Data);
+ if (IsInvalidMapperModifier)
+ break;
+ }
+ // OpenMP < 5.1 doesn't permit a ',' or additional modifiers.
+ if (getLangOpts().OpenMP < 51)
+ break;
+ // OpenMP 5.1 accepts an optional ',' even if the next character is ':'.
+ // TODO: Is that intentional?
+ if (Tok.is(tok::comma))
+ ConsumeToken();
+ }
+ if (!Data.MotionModifiers.empty() && Tok.isNot(tok::colon)) {
+ if (!IsInvalidMapperModifier) {
+ if (getLangOpts().OpenMP < 51)
+ Diag(Tok, diag::warn_pragma_expected_colon) << ")";
+ else
+ Diag(Tok, diag::warn_pragma_expected_colon) << "motion modifier";
+ }
+ SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+ // OpenMP 5.1 permits a ':' even without a preceding modifier. TODO: Is
+ // that intentional?
+ if ((!Data.MotionModifiers.empty() || getLangOpts().OpenMP >= 51) &&
+ Tok.is(tok::colon))
+ Data.ColonLoc = ConsumeToken();
+ } else if (Kind == OMPC_allocate ||
+ (Kind == OMPC_affinity && Tok.is(tok::identifier) &&
+ PP.getSpelling(Tok) == "iterator")) {
+ // Handle optional allocator expression followed by colon delimiter.
+ ColonProtectionRAIIObject ColonRAII(*this);
+ TentativeParsingAction TPA(*this);
+ // OpenMP 5.0, 2.10.1, task Construct.
+ // where aff-modifier is one of the following:
+ // iterator(iterators-definition)
+ ExprResult Tail;
+ if (Kind == OMPC_allocate) {
+ Tail = ParseAssignmentExpression();
+ } else {
+ HasIterator = true;
+ EnterScope(Scope::OpenMPDirectiveScope | Scope::DeclScope);
+ Tail = ParseOpenMPIteratorsExpr();
+ }
+ Tail = Actions.CorrectDelayedTyposInExpr(Tail);
+ Tail = Actions.ActOnFinishFullExpr(Tail.get(), T.getOpenLocation(),
+ /*DiscardedValue=*/false);
+ if (Tail.isUsable()) {
+ if (Tok.is(tok::colon)) {
+ Data.DepModOrTailExpr = Tail.get();
+ Data.ColonLoc = ConsumeToken();
+ TPA.Commit();
+ } else {
+ // Colon not found, parse only list of variables.
+ TPA.Revert();
+ }
+ } else {
+ // Parsing was unsuccessfull, revert and skip to the end of clause or
+ // directive.
+ TPA.Revert();
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+ } else if (Kind == OMPC_adjust_args) {
+ // Handle adjust-op for adjust_args clause.
+ ColonProtectionRAIIObject ColonRAII(*this);
+ Data.ExtraModifier = getOpenMPSimpleClauseType(
+ Kind, Tok.is(tok::identifier) ? PP.getSpelling(Tok) : "",
+ getLangOpts());
+ Data.ExtraModifierLoc = Tok.getLocation();
+ if (Data.ExtraModifier == OMPC_ADJUST_ARGS_unknown) {
+ SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ } else {
+ ConsumeToken();
+ if (Tok.is(tok::colon))
+ Data.ColonLoc = Tok.getLocation();
+ ExpectAndConsume(tok::colon, diag::warn_pragma_expected_colon,
+ "adjust-op");
+ }
+ }
+
+ bool IsComma =
+ (Kind != OMPC_reduction && Kind != OMPC_task_reduction &&
+ Kind != OMPC_in_reduction && Kind != OMPC_depend && Kind != OMPC_map) ||
+ (Kind == OMPC_reduction && !InvalidReductionId) ||
+ (Kind == OMPC_map && Data.ExtraModifier != OMPC_MAP_unknown) ||
+ (Kind == OMPC_depend && Data.ExtraModifier != OMPC_DEPEND_unknown) ||
+ (Kind == OMPC_adjust_args &&
+ Data.ExtraModifier != OMPC_ADJUST_ARGS_unknown);
+ const bool MayHaveTail = (Kind == OMPC_linear || Kind == OMPC_aligned);
+ while (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) &&
+ Tok.isNot(tok::annot_pragma_openmp_end))) {
+ ParseScope OMPListScope(this, Scope::OpenMPDirectiveScope);
+ ColonProtectionRAIIObject ColonRAII(*this, MayHaveTail);
+ if (!ParseOpenMPReservedLocator(Kind, Data, getLangOpts())) {
+ // Parse variable
+ ExprResult VarExpr =
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+ if (VarExpr.isUsable()) {
+ Vars.push_back(VarExpr.get());
+ } else {
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+ }
+ // Skip ',' if any
+ IsComma = Tok.is(tok::comma);
+ if (IsComma)
+ ConsumeToken();
+ else if (Tok.isNot(tok::r_paren) &&
+ Tok.isNot(tok::annot_pragma_openmp_end) &&
+ (!MayHaveTail || Tok.isNot(tok::colon)))
+ Diag(Tok, diag::err_omp_expected_punc)
+ << ((Kind == OMPC_flush) ? getOpenMPDirectiveName(OMPD_flush)
+ : getOpenMPClauseName(Kind))
+ << (Kind == OMPC_flush);
+ }
+
+ // Parse ')' for linear clause with modifier.
+ if (NeedRParenForLinear)
+ LinearT.consumeClose();
+
+ // Parse ':' linear-step (or ':' alignment).
+ const bool MustHaveTail = MayHaveTail && Tok.is(tok::colon);
+ if (MustHaveTail) {
+ Data.ColonLoc = Tok.getLocation();
+ SourceLocation ELoc = ConsumeToken();
+ ExprResult Tail = ParseAssignmentExpression();
+ Tail =
+ Actions.ActOnFinishFullExpr(Tail.get(), ELoc, /*DiscardedValue*/ false);
+ if (Tail.isUsable())
+ Data.DepModOrTailExpr = Tail.get();
+ else
+ SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+ StopBeforeMatch);
+ }
+
+ // Parse ')'.
+ Data.RLoc = Tok.getLocation();
+ if (!T.consumeClose())
+ Data.RLoc = T.getCloseLocation();
+ // Exit from scope when the iterator is used in depend clause.
+ if (HasIterator)
+ ExitScope();
+ return (Kind != OMPC_depend && Kind != OMPC_map && Vars.empty()) ||
+ (MustHaveTail && !Data.DepModOrTailExpr) || InvalidReductionId ||
+ IsInvalidMapperModifier || InvalidIterator;
+}
+
+/// Parsing of OpenMP clause 'private', 'firstprivate', 'lastprivate',
+/// 'shared', 'copyin', 'copyprivate', 'flush', 'reduction', 'task_reduction',
+/// 'in_reduction', 'nontemporal', 'exclusive' or 'inclusive'.
+///
+/// private-clause:
+/// 'private' '(' list ')'
+/// firstprivate-clause:
+/// 'firstprivate' '(' list ')'
+/// lastprivate-clause:
+/// 'lastprivate' '(' list ')'
+/// shared-clause:
+/// 'shared' '(' list ')'
+/// linear-clause:
+/// 'linear' '(' linear-list [ ':' linear-step ] ')'
+/// aligned-clause:
+/// 'aligned' '(' list [ ':' alignment ] ')'
+/// reduction-clause:
+/// 'reduction' '(' [ modifier ',' ] reduction-identifier ':' list ')'
+/// task_reduction-clause:
+/// 'task_reduction' '(' reduction-identifier ':' list ')'
+/// in_reduction-clause:
+/// 'in_reduction' '(' reduction-identifier ':' list ')'
+/// copyprivate-clause:
+/// 'copyprivate' '(' list ')'
+/// flush-clause:
+/// 'flush' '(' list ')'
+/// depend-clause:
+/// 'depend' '(' in | out | inout : list | source ')'
+/// map-clause:
+/// 'map' '(' [ [ always [,] ] [ close [,] ]
+/// [ mapper '(' mapper-identifier ')' [,] ]
+/// to | from | tofrom | alloc | release | delete ':' ] list ')';
+/// to-clause:
+/// 'to' '(' [ mapper '(' mapper-identifier ')' ':' ] list ')'
+/// from-clause:
+/// 'from' '(' [ mapper '(' mapper-identifier ')' ':' ] list ')'
+/// use_device_ptr-clause:
+/// 'use_device_ptr' '(' list ')'
+/// use_device_addr-clause:
+/// 'use_device_addr' '(' list ')'
+/// is_device_ptr-clause:
+/// 'is_device_ptr' '(' list ')'
+/// has_device_addr-clause:
+/// 'has_device_addr' '(' list ')'
+/// allocate-clause:
+/// 'allocate' '(' [ allocator ':' ] list ')'
+/// nontemporal-clause:
+/// 'nontemporal' '(' list ')'
+/// inclusive-clause:
+/// 'inclusive' '(' list ')'
+/// exclusive-clause:
+/// 'exclusive' '(' list ')'
+///
+/// For 'linear' clause linear-list may have the following forms:
+/// list
+/// modifier(list)
+/// where modifier is 'val' (C) or 'ref', 'val' or 'uval'(C++).
+OMPClause *Parser::ParseOpenMPVarListClause(OpenMPDirectiveKind DKind,
+ OpenMPClauseKind Kind,
+ bool ParseOnly) {
+ SourceLocation Loc = Tok.getLocation();
+ SourceLocation LOpen = ConsumeToken();
+ SmallVector<Expr *, 4> Vars;
+ Sema::OpenMPVarListDataTy Data;
+
+ if (ParseOpenMPVarList(DKind, Kind, Vars, Data))
+ return nullptr;
+
+ if (ParseOnly)
+ return nullptr;
+ OMPVarListLocTy Locs(Loc, LOpen, Data.RLoc);
+ return Actions.ActOnOpenMPVarListClause(Kind, Vars, Locs, Data);
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParsePragma.cpp b/contrib/libs/clang16/lib/Parse/ParsePragma.cpp
new file mode 100644
index 0000000000..658853d42b
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParsePragma.cpp
@@ -0,0 +1,4037 @@
+//===--- ParsePragma.cpp - Language specific pragma parsing ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the language specific #pragma handlers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/PragmaKinds.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/Token.h"
+#include "clang/Parse/LoopHint.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/Scope.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include <optional>
+using namespace clang;
+
+namespace {
+
+struct PragmaAlignHandler : public PragmaHandler {
+ explicit PragmaAlignHandler() : PragmaHandler("align") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaGCCVisibilityHandler : public PragmaHandler {
+ explicit PragmaGCCVisibilityHandler() : PragmaHandler("visibility") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaOptionsHandler : public PragmaHandler {
+ explicit PragmaOptionsHandler() : PragmaHandler("options") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaPackHandler : public PragmaHandler {
+ explicit PragmaPackHandler() : PragmaHandler("pack") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaClangSectionHandler : public PragmaHandler {
+ explicit PragmaClangSectionHandler(Sema &S)
+ : PragmaHandler("section"), Actions(S) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+
+private:
+ Sema &Actions;
+};
+
+struct PragmaMSStructHandler : public PragmaHandler {
+ explicit PragmaMSStructHandler() : PragmaHandler("ms_struct") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaUnusedHandler : public PragmaHandler {
+ PragmaUnusedHandler() : PragmaHandler("unused") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaWeakHandler : public PragmaHandler {
+ explicit PragmaWeakHandler() : PragmaHandler("weak") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaRedefineExtnameHandler : public PragmaHandler {
+ explicit PragmaRedefineExtnameHandler() : PragmaHandler("redefine_extname") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaOpenCLExtensionHandler : public PragmaHandler {
+ PragmaOpenCLExtensionHandler() : PragmaHandler("EXTENSION") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+
+struct PragmaFPContractHandler : public PragmaHandler {
+ PragmaFPContractHandler() : PragmaHandler("FP_CONTRACT") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+// Pragma STDC implementations.
+
+/// PragmaSTDC_FENV_ACCESSHandler - "\#pragma STDC FENV_ACCESS ...".
+struct PragmaSTDC_FENV_ACCESSHandler : public PragmaHandler {
+ PragmaSTDC_FENV_ACCESSHandler() : PragmaHandler("FENV_ACCESS") {}
+
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &Tok) override {
+ Token PragmaName = Tok;
+ if (!PP.getTargetInfo().hasStrictFP() && !PP.getLangOpts().ExpStrictFP) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_fp_ignored)
+ << PragmaName.getIdentifierInfo()->getName();
+ return;
+ }
+ tok::OnOffSwitch OOS;
+ if (PP.LexOnOffSwitch(OOS))
+ return;
+
+ MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
+ 1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_fenv_access);
+ Toks[0].setLocation(Tok.getLocation());
+ Toks[0].setAnnotationEndLoc(Tok.getLocation());
+ Toks[0].setAnnotationValue(reinterpret_cast<void*>(
+ static_cast<uintptr_t>(OOS)));
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+ }
+};
+
+/// PragmaSTDC_CX_LIMITED_RANGEHandler - "\#pragma STDC CX_LIMITED_RANGE ...".
+struct PragmaSTDC_CX_LIMITED_RANGEHandler : public PragmaHandler {
+ PragmaSTDC_CX_LIMITED_RANGEHandler() : PragmaHandler("CX_LIMITED_RANGE") {}
+
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &Tok) override {
+ tok::OnOffSwitch OOS;
+ PP.LexOnOffSwitch(OOS);
+ }
+};
+
+/// Handler for "\#pragma STDC FENV_ROUND ...".
+struct PragmaSTDC_FENV_ROUNDHandler : public PragmaHandler {
+ PragmaSTDC_FENV_ROUNDHandler() : PragmaHandler("FENV_ROUND") {}
+
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &Tok) override;
+};
+
+/// PragmaSTDC_UnknownHandler - "\#pragma STDC ...".
+struct PragmaSTDC_UnknownHandler : public PragmaHandler {
+ PragmaSTDC_UnknownHandler() = default;
+
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &UnknownTok) override {
+ // C99 6.10.6p2, unknown forms are not allowed.
+ PP.Diag(UnknownTok, diag::ext_stdc_pragma_ignored);
+ }
+};
+
+struct PragmaFPHandler : public PragmaHandler {
+ PragmaFPHandler() : PragmaHandler("fp") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaNoOpenMPHandler : public PragmaHandler {
+ PragmaNoOpenMPHandler() : PragmaHandler("omp") { }
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaOpenMPHandler : public PragmaHandler {
+ PragmaOpenMPHandler() : PragmaHandler("omp") { }
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+/// PragmaCommentHandler - "\#pragma comment ...".
+struct PragmaCommentHandler : public PragmaHandler {
+ PragmaCommentHandler(Sema &Actions)
+ : PragmaHandler("comment"), Actions(Actions) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+
+private:
+ Sema &Actions;
+};
+
+struct PragmaDetectMismatchHandler : public PragmaHandler {
+ PragmaDetectMismatchHandler(Sema &Actions)
+ : PragmaHandler("detect_mismatch"), Actions(Actions) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+
+private:
+ Sema &Actions;
+};
+
+struct PragmaFloatControlHandler : public PragmaHandler {
+ PragmaFloatControlHandler(Sema &Actions)
+ : PragmaHandler("float_control") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaMSPointersToMembers : public PragmaHandler {
+ explicit PragmaMSPointersToMembers() : PragmaHandler("pointers_to_members") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaMSVtorDisp : public PragmaHandler {
+ explicit PragmaMSVtorDisp() : PragmaHandler("vtordisp") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaMSPragma : public PragmaHandler {
+ explicit PragmaMSPragma(const char *name) : PragmaHandler(name) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+/// PragmaOptimizeHandler - "\#pragma clang optimize on/off".
+struct PragmaOptimizeHandler : public PragmaHandler {
+ PragmaOptimizeHandler(Sema &S)
+ : PragmaHandler("optimize"), Actions(S) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+
+private:
+ Sema &Actions;
+};
+
+struct PragmaLoopHintHandler : public PragmaHandler {
+ PragmaLoopHintHandler() : PragmaHandler("loop") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaUnrollHintHandler : public PragmaHandler {
+ PragmaUnrollHintHandler(const char *name) : PragmaHandler(name) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaMSRuntimeChecksHandler : public EmptyPragmaHandler {
+ PragmaMSRuntimeChecksHandler() : EmptyPragmaHandler("runtime_checks") {}
+};
+
+struct PragmaMSIntrinsicHandler : public PragmaHandler {
+ PragmaMSIntrinsicHandler() : PragmaHandler("intrinsic") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+// "\#pragma fenv_access (on)".
+struct PragmaMSFenvAccessHandler : public PragmaHandler {
+ PragmaMSFenvAccessHandler() : PragmaHandler("fenv_access") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override {
+ StringRef PragmaName = FirstToken.getIdentifierInfo()->getName();
+ if (!PP.getTargetInfo().hasStrictFP() && !PP.getLangOpts().ExpStrictFP) {
+ PP.Diag(FirstToken.getLocation(), diag::warn_pragma_fp_ignored)
+ << PragmaName;
+ return;
+ }
+
+ Token Tok;
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
+ << PragmaName;
+ return;
+ }
+ PP.Lex(Tok); // Consume the l_paren.
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_ms_fenv_access);
+ return;
+ }
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ tok::OnOffSwitch OOS;
+ if (II->isStr("on")) {
+ OOS = tok::OOS_ON;
+ PP.Lex(Tok);
+ } else if (II->isStr("off")) {
+ OOS = tok::OOS_OFF;
+ PP.Lex(Tok);
+ } else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_ms_fenv_access);
+ return;
+ }
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
+ << PragmaName;
+ return;
+ }
+ PP.Lex(Tok); // Consume the r_paren.
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaName;
+ return;
+ }
+
+ MutableArrayRef<Token> Toks(
+ PP.getPreprocessorAllocator().Allocate<Token>(1), 1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_fenv_access_ms);
+ Toks[0].setLocation(FirstToken.getLocation());
+ Toks[0].setAnnotationEndLoc(Tok.getLocation());
+ Toks[0].setAnnotationValue(
+ reinterpret_cast<void*>(static_cast<uintptr_t>(OOS)));
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+ }
+};
+
+struct PragmaForceCUDAHostDeviceHandler : public PragmaHandler {
+ PragmaForceCUDAHostDeviceHandler(Sema &Actions)
+ : PragmaHandler("force_cuda_host_device"), Actions(Actions) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+
+private:
+ Sema &Actions;
+};
+
+/// PragmaAttributeHandler - "\#pragma clang attribute ...".
+struct PragmaAttributeHandler : public PragmaHandler {
+ PragmaAttributeHandler(AttributeFactory &AttrFactory)
+ : PragmaHandler("attribute"), AttributesForPragmaAttribute(AttrFactory) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+
+ /// A pool of attributes that were parsed in \#pragma clang attribute.
+ ParsedAttributes AttributesForPragmaAttribute;
+};
+
+struct PragmaMaxTokensHereHandler : public PragmaHandler {
+ PragmaMaxTokensHereHandler() : PragmaHandler("max_tokens_here") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaMaxTokensTotalHandler : public PragmaHandler {
+ PragmaMaxTokensTotalHandler() : PragmaHandler("max_tokens_total") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
+struct PragmaRISCVHandler : public PragmaHandler {
+ PragmaRISCVHandler(Sema &Actions)
+ : PragmaHandler("riscv"), Actions(Actions) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+
+private:
+ Sema &Actions;
+};
+
+void markAsReinjectedForRelexing(llvm::MutableArrayRef<clang::Token> Toks) {
+ for (auto &T : Toks)
+ T.setFlag(clang::Token::IsReinjected);
+}
+} // end namespace
+
+void Parser::initializePragmaHandlers() {
+ AlignHandler = std::make_unique<PragmaAlignHandler>();
+ PP.AddPragmaHandler(AlignHandler.get());
+
+ GCCVisibilityHandler = std::make_unique<PragmaGCCVisibilityHandler>();
+ PP.AddPragmaHandler("GCC", GCCVisibilityHandler.get());
+
+ OptionsHandler = std::make_unique<PragmaOptionsHandler>();
+ PP.AddPragmaHandler(OptionsHandler.get());
+
+ PackHandler = std::make_unique<PragmaPackHandler>();
+ PP.AddPragmaHandler(PackHandler.get());
+
+ MSStructHandler = std::make_unique<PragmaMSStructHandler>();
+ PP.AddPragmaHandler(MSStructHandler.get());
+
+ UnusedHandler = std::make_unique<PragmaUnusedHandler>();
+ PP.AddPragmaHandler(UnusedHandler.get());
+
+ WeakHandler = std::make_unique<PragmaWeakHandler>();
+ PP.AddPragmaHandler(WeakHandler.get());
+
+ RedefineExtnameHandler = std::make_unique<PragmaRedefineExtnameHandler>();
+ PP.AddPragmaHandler(RedefineExtnameHandler.get());
+
+ FPContractHandler = std::make_unique<PragmaFPContractHandler>();
+ PP.AddPragmaHandler("STDC", FPContractHandler.get());
+
+ STDCFenvAccessHandler = std::make_unique<PragmaSTDC_FENV_ACCESSHandler>();
+ PP.AddPragmaHandler("STDC", STDCFenvAccessHandler.get());
+
+ STDCFenvRoundHandler = std::make_unique<PragmaSTDC_FENV_ROUNDHandler>();
+ PP.AddPragmaHandler("STDC", STDCFenvRoundHandler.get());
+
+ STDCCXLIMITHandler = std::make_unique<PragmaSTDC_CX_LIMITED_RANGEHandler>();
+ PP.AddPragmaHandler("STDC", STDCCXLIMITHandler.get());
+
+ STDCUnknownHandler = std::make_unique<PragmaSTDC_UnknownHandler>();
+ PP.AddPragmaHandler("STDC", STDCUnknownHandler.get());
+
+ PCSectionHandler = std::make_unique<PragmaClangSectionHandler>(Actions);
+ PP.AddPragmaHandler("clang", PCSectionHandler.get());
+
+ if (getLangOpts().OpenCL) {
+ OpenCLExtensionHandler = std::make_unique<PragmaOpenCLExtensionHandler>();
+ PP.AddPragmaHandler("OPENCL", OpenCLExtensionHandler.get());
+
+ PP.AddPragmaHandler("OPENCL", FPContractHandler.get());
+ }
+ if (getLangOpts().OpenMP)
+ OpenMPHandler = std::make_unique<PragmaOpenMPHandler>();
+ else
+ OpenMPHandler = std::make_unique<PragmaNoOpenMPHandler>();
+ PP.AddPragmaHandler(OpenMPHandler.get());
+
+ if (getLangOpts().MicrosoftExt ||
+ getTargetInfo().getTriple().isOSBinFormatELF()) {
+ MSCommentHandler = std::make_unique<PragmaCommentHandler>(Actions);
+ PP.AddPragmaHandler(MSCommentHandler.get());
+ }
+
+ FloatControlHandler = std::make_unique<PragmaFloatControlHandler>(Actions);
+ PP.AddPragmaHandler(FloatControlHandler.get());
+ if (getLangOpts().MicrosoftExt) {
+ MSDetectMismatchHandler =
+ std::make_unique<PragmaDetectMismatchHandler>(Actions);
+ PP.AddPragmaHandler(MSDetectMismatchHandler.get());
+ MSPointersToMembers = std::make_unique<PragmaMSPointersToMembers>();
+ PP.AddPragmaHandler(MSPointersToMembers.get());
+ MSVtorDisp = std::make_unique<PragmaMSVtorDisp>();
+ PP.AddPragmaHandler(MSVtorDisp.get());
+ MSInitSeg = std::make_unique<PragmaMSPragma>("init_seg");
+ PP.AddPragmaHandler(MSInitSeg.get());
+ MSDataSeg = std::make_unique<PragmaMSPragma>("data_seg");
+ PP.AddPragmaHandler(MSDataSeg.get());
+ MSBSSSeg = std::make_unique<PragmaMSPragma>("bss_seg");
+ PP.AddPragmaHandler(MSBSSSeg.get());
+ MSConstSeg = std::make_unique<PragmaMSPragma>("const_seg");
+ PP.AddPragmaHandler(MSConstSeg.get());
+ MSCodeSeg = std::make_unique<PragmaMSPragma>("code_seg");
+ PP.AddPragmaHandler(MSCodeSeg.get());
+ MSSection = std::make_unique<PragmaMSPragma>("section");
+ PP.AddPragmaHandler(MSSection.get());
+ MSStrictGuardStackCheck =
+ std::make_unique<PragmaMSPragma>("strict_gs_check");
+ PP.AddPragmaHandler(MSStrictGuardStackCheck.get());
+ MSFunction = std::make_unique<PragmaMSPragma>("function");
+ PP.AddPragmaHandler(MSFunction.get());
+ MSAllocText = std::make_unique<PragmaMSPragma>("alloc_text");
+ PP.AddPragmaHandler(MSAllocText.get());
+ MSOptimize = std::make_unique<PragmaMSPragma>("optimize");
+ PP.AddPragmaHandler(MSOptimize.get());
+ MSRuntimeChecks = std::make_unique<PragmaMSRuntimeChecksHandler>();
+ PP.AddPragmaHandler(MSRuntimeChecks.get());
+ MSIntrinsic = std::make_unique<PragmaMSIntrinsicHandler>();
+ PP.AddPragmaHandler(MSIntrinsic.get());
+ MSFenvAccess = std::make_unique<PragmaMSFenvAccessHandler>();
+ PP.AddPragmaHandler(MSFenvAccess.get());
+ }
+
+ if (getLangOpts().CUDA) {
+ CUDAForceHostDeviceHandler =
+ std::make_unique<PragmaForceCUDAHostDeviceHandler>(Actions);
+ PP.AddPragmaHandler("clang", CUDAForceHostDeviceHandler.get());
+ }
+
+ OptimizeHandler = std::make_unique<PragmaOptimizeHandler>(Actions);
+ PP.AddPragmaHandler("clang", OptimizeHandler.get());
+
+ LoopHintHandler = std::make_unique<PragmaLoopHintHandler>();
+ PP.AddPragmaHandler("clang", LoopHintHandler.get());
+
+ UnrollHintHandler = std::make_unique<PragmaUnrollHintHandler>("unroll");
+ PP.AddPragmaHandler(UnrollHintHandler.get());
+ PP.AddPragmaHandler("GCC", UnrollHintHandler.get());
+
+ NoUnrollHintHandler = std::make_unique<PragmaUnrollHintHandler>("nounroll");
+ PP.AddPragmaHandler(NoUnrollHintHandler.get());
+ PP.AddPragmaHandler("GCC", NoUnrollHintHandler.get());
+
+ UnrollAndJamHintHandler =
+ std::make_unique<PragmaUnrollHintHandler>("unroll_and_jam");
+ PP.AddPragmaHandler(UnrollAndJamHintHandler.get());
+
+ NoUnrollAndJamHintHandler =
+ std::make_unique<PragmaUnrollHintHandler>("nounroll_and_jam");
+ PP.AddPragmaHandler(NoUnrollAndJamHintHandler.get());
+
+ FPHandler = std::make_unique<PragmaFPHandler>();
+ PP.AddPragmaHandler("clang", FPHandler.get());
+
+ AttributePragmaHandler =
+ std::make_unique<PragmaAttributeHandler>(AttrFactory);
+ PP.AddPragmaHandler("clang", AttributePragmaHandler.get());
+
+ MaxTokensHerePragmaHandler = std::make_unique<PragmaMaxTokensHereHandler>();
+ PP.AddPragmaHandler("clang", MaxTokensHerePragmaHandler.get());
+
+ MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>();
+ PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
+
+ if (getTargetInfo().getTriple().isRISCV()) {
+ RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
+ PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
+ }
+}
+
+void Parser::resetPragmaHandlers() {
+ // Remove the pragma handlers we installed.
+ PP.RemovePragmaHandler(AlignHandler.get());
+ AlignHandler.reset();
+ PP.RemovePragmaHandler("GCC", GCCVisibilityHandler.get());
+ GCCVisibilityHandler.reset();
+ PP.RemovePragmaHandler(OptionsHandler.get());
+ OptionsHandler.reset();
+ PP.RemovePragmaHandler(PackHandler.get());
+ PackHandler.reset();
+ PP.RemovePragmaHandler(MSStructHandler.get());
+ MSStructHandler.reset();
+ PP.RemovePragmaHandler(UnusedHandler.get());
+ UnusedHandler.reset();
+ PP.RemovePragmaHandler(WeakHandler.get());
+ WeakHandler.reset();
+ PP.RemovePragmaHandler(RedefineExtnameHandler.get());
+ RedefineExtnameHandler.reset();
+
+ if (getLangOpts().OpenCL) {
+ PP.RemovePragmaHandler("OPENCL", OpenCLExtensionHandler.get());
+ OpenCLExtensionHandler.reset();
+ PP.RemovePragmaHandler("OPENCL", FPContractHandler.get());
+ }
+ PP.RemovePragmaHandler(OpenMPHandler.get());
+ OpenMPHandler.reset();
+
+ if (getLangOpts().MicrosoftExt ||
+ getTargetInfo().getTriple().isOSBinFormatELF()) {
+ PP.RemovePragmaHandler(MSCommentHandler.get());
+ MSCommentHandler.reset();
+ }
+
+ PP.RemovePragmaHandler("clang", PCSectionHandler.get());
+ PCSectionHandler.reset();
+
+ PP.RemovePragmaHandler(FloatControlHandler.get());
+ FloatControlHandler.reset();
+ if (getLangOpts().MicrosoftExt) {
+ PP.RemovePragmaHandler(MSDetectMismatchHandler.get());
+ MSDetectMismatchHandler.reset();
+ PP.RemovePragmaHandler(MSPointersToMembers.get());
+ MSPointersToMembers.reset();
+ PP.RemovePragmaHandler(MSVtorDisp.get());
+ MSVtorDisp.reset();
+ PP.RemovePragmaHandler(MSInitSeg.get());
+ MSInitSeg.reset();
+ PP.RemovePragmaHandler(MSDataSeg.get());
+ MSDataSeg.reset();
+ PP.RemovePragmaHandler(MSBSSSeg.get());
+ MSBSSSeg.reset();
+ PP.RemovePragmaHandler(MSConstSeg.get());
+ MSConstSeg.reset();
+ PP.RemovePragmaHandler(MSCodeSeg.get());
+ MSCodeSeg.reset();
+ PP.RemovePragmaHandler(MSSection.get());
+ MSSection.reset();
+ PP.RemovePragmaHandler(MSStrictGuardStackCheck.get());
+ MSStrictGuardStackCheck.reset();
+ PP.RemovePragmaHandler(MSFunction.get());
+ MSFunction.reset();
+ PP.RemovePragmaHandler(MSAllocText.get());
+ MSAllocText.reset();
+ PP.RemovePragmaHandler(MSRuntimeChecks.get());
+ MSRuntimeChecks.reset();
+ PP.RemovePragmaHandler(MSIntrinsic.get());
+ MSIntrinsic.reset();
+ PP.RemovePragmaHandler(MSOptimize.get());
+ MSOptimize.reset();
+ PP.RemovePragmaHandler(MSFenvAccess.get());
+ MSFenvAccess.reset();
+ }
+
+ if (getLangOpts().CUDA) {
+ PP.RemovePragmaHandler("clang", CUDAForceHostDeviceHandler.get());
+ CUDAForceHostDeviceHandler.reset();
+ }
+
+ PP.RemovePragmaHandler("STDC", FPContractHandler.get());
+ FPContractHandler.reset();
+
+ PP.RemovePragmaHandler("STDC", STDCFenvAccessHandler.get());
+ STDCFenvAccessHandler.reset();
+
+ PP.RemovePragmaHandler("STDC", STDCFenvRoundHandler.get());
+ STDCFenvRoundHandler.reset();
+
+ PP.RemovePragmaHandler("STDC", STDCCXLIMITHandler.get());
+ STDCCXLIMITHandler.reset();
+
+ PP.RemovePragmaHandler("STDC", STDCUnknownHandler.get());
+ STDCUnknownHandler.reset();
+
+ PP.RemovePragmaHandler("clang", OptimizeHandler.get());
+ OptimizeHandler.reset();
+
+ PP.RemovePragmaHandler("clang", LoopHintHandler.get());
+ LoopHintHandler.reset();
+
+ PP.RemovePragmaHandler(UnrollHintHandler.get());
+ PP.RemovePragmaHandler("GCC", UnrollHintHandler.get());
+ UnrollHintHandler.reset();
+
+ PP.RemovePragmaHandler(NoUnrollHintHandler.get());
+ PP.RemovePragmaHandler("GCC", NoUnrollHintHandler.get());
+ NoUnrollHintHandler.reset();
+
+ PP.RemovePragmaHandler(UnrollAndJamHintHandler.get());
+ UnrollAndJamHintHandler.reset();
+
+ PP.RemovePragmaHandler(NoUnrollAndJamHintHandler.get());
+ NoUnrollAndJamHintHandler.reset();
+
+ PP.RemovePragmaHandler("clang", FPHandler.get());
+ FPHandler.reset();
+
+ PP.RemovePragmaHandler("clang", AttributePragmaHandler.get());
+ AttributePragmaHandler.reset();
+
+ PP.RemovePragmaHandler("clang", MaxTokensHerePragmaHandler.get());
+ MaxTokensHerePragmaHandler.reset();
+
+ PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
+ MaxTokensTotalPragmaHandler.reset();
+
+ if (getTargetInfo().getTriple().isRISCV()) {
+ PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get());
+ RISCVPragmaHandler.reset();
+ }
+}
+
+/// Handle the annotation token produced for #pragma unused(...)
+///
+/// Each annot_pragma_unused is followed by the argument token so e.g.
+/// "#pragma unused(x,y)" becomes:
+/// annot_pragma_unused 'x' annot_pragma_unused 'y'
+void Parser::HandlePragmaUnused() {
+ assert(Tok.is(tok::annot_pragma_unused));
+ SourceLocation UnusedLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaUnused(Tok, getCurScope(), UnusedLoc);
+ ConsumeToken(); // The argument token.
+}
+
+void Parser::HandlePragmaVisibility() {
+ assert(Tok.is(tok::annot_pragma_vis));
+ const IdentifierInfo *VisType =
+ static_cast<IdentifierInfo *>(Tok.getAnnotationValue());
+ SourceLocation VisLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaVisibility(VisType, VisLoc);
+}
+
+namespace {
+struct PragmaPackInfo {
+ Sema::PragmaMsStackAction Action;
+ StringRef SlotLabel;
+ Token Alignment;
+};
+} // end anonymous namespace
+
+void Parser::HandlePragmaPack() {
+ assert(Tok.is(tok::annot_pragma_pack));
+ PragmaPackInfo *Info =
+ static_cast<PragmaPackInfo *>(Tok.getAnnotationValue());
+ SourceLocation PragmaLoc = Tok.getLocation();
+ ExprResult Alignment;
+ if (Info->Alignment.is(tok::numeric_constant)) {
+ Alignment = Actions.ActOnNumericConstant(Info->Alignment);
+ if (Alignment.isInvalid()) {
+ ConsumeAnnotationToken();
+ return;
+ }
+ }
+ Actions.ActOnPragmaPack(PragmaLoc, Info->Action, Info->SlotLabel,
+ Alignment.get());
+ // Consume the token after processing the pragma to enable pragma-specific
+ // #include warnings.
+ ConsumeAnnotationToken();
+}
+
+void Parser::HandlePragmaMSStruct() {
+ assert(Tok.is(tok::annot_pragma_msstruct));
+ PragmaMSStructKind Kind = static_cast<PragmaMSStructKind>(
+ reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
+ Actions.ActOnPragmaMSStruct(Kind);
+ ConsumeAnnotationToken();
+}
+
+void Parser::HandlePragmaAlign() {
+ assert(Tok.is(tok::annot_pragma_align));
+ Sema::PragmaOptionsAlignKind Kind =
+ static_cast<Sema::PragmaOptionsAlignKind>(
+ reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
+ Actions.ActOnPragmaOptionsAlign(Kind, Tok.getLocation());
+ // Consume the token after processing the pragma to enable pragma-specific
+ // #include warnings.
+ ConsumeAnnotationToken();
+}
+
+void Parser::HandlePragmaDump() {
+ assert(Tok.is(tok::annot_pragma_dump));
+ IdentifierInfo *II =
+ reinterpret_cast<IdentifierInfo *>(Tok.getAnnotationValue());
+ Actions.ActOnPragmaDump(getCurScope(), Tok.getLocation(), II);
+ ConsumeAnnotationToken();
+}
+
+void Parser::HandlePragmaWeak() {
+ assert(Tok.is(tok::annot_pragma_weak));
+ SourceLocation PragmaLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaWeakID(Tok.getIdentifierInfo(), PragmaLoc,
+ Tok.getLocation());
+ ConsumeToken(); // The weak name.
+}
+
+void Parser::HandlePragmaWeakAlias() {
+ assert(Tok.is(tok::annot_pragma_weakalias));
+ SourceLocation PragmaLoc = ConsumeAnnotationToken();
+ IdentifierInfo *WeakName = Tok.getIdentifierInfo();
+ SourceLocation WeakNameLoc = Tok.getLocation();
+ ConsumeToken();
+ IdentifierInfo *AliasName = Tok.getIdentifierInfo();
+ SourceLocation AliasNameLoc = Tok.getLocation();
+ ConsumeToken();
+ Actions.ActOnPragmaWeakAlias(WeakName, AliasName, PragmaLoc,
+ WeakNameLoc, AliasNameLoc);
+
+}
+
+void Parser::HandlePragmaRedefineExtname() {
+ assert(Tok.is(tok::annot_pragma_redefine_extname));
+ SourceLocation RedefLoc = ConsumeAnnotationToken();
+ IdentifierInfo *RedefName = Tok.getIdentifierInfo();
+ SourceLocation RedefNameLoc = Tok.getLocation();
+ ConsumeToken();
+ IdentifierInfo *AliasName = Tok.getIdentifierInfo();
+ SourceLocation AliasNameLoc = Tok.getLocation();
+ ConsumeToken();
+ Actions.ActOnPragmaRedefineExtname(RedefName, AliasName, RedefLoc,
+ RedefNameLoc, AliasNameLoc);
+}
+
+void Parser::HandlePragmaFPContract() {
+ assert(Tok.is(tok::annot_pragma_fp_contract));
+ tok::OnOffSwitch OOS =
+ static_cast<tok::OnOffSwitch>(
+ reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
+
+ LangOptions::FPModeKind FPC;
+ switch (OOS) {
+ case tok::OOS_ON:
+ FPC = LangOptions::FPM_On;
+ break;
+ case tok::OOS_OFF:
+ FPC = LangOptions::FPM_Off;
+ break;
+ case tok::OOS_DEFAULT:
+ FPC = getLangOpts().getDefaultFPContractMode();
+ break;
+ }
+
+ SourceLocation PragmaLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaFPContract(PragmaLoc, FPC);
+}
+
+void Parser::HandlePragmaFloatControl() {
+ assert(Tok.is(tok::annot_pragma_float_control));
+
+ // The value that is held on the PragmaFloatControlStack encodes
+ // the PragmaFloatControl kind and the MSStackAction kind
+ // into a single 32-bit word. The MsStackAction is the high 16 bits
+ // and the FloatControl is the lower 16 bits. Use shift and bit-and
+ // to decode the parts.
+ uintptr_t Value = reinterpret_cast<uintptr_t>(Tok.getAnnotationValue());
+ Sema::PragmaMsStackAction Action =
+ static_cast<Sema::PragmaMsStackAction>((Value >> 16) & 0xFFFF);
+ PragmaFloatControlKind Kind = PragmaFloatControlKind(Value & 0xFFFF);
+ SourceLocation PragmaLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaFloatControl(PragmaLoc, Action, Kind);
+}
+
+void Parser::HandlePragmaFEnvAccess() {
+ assert(Tok.is(tok::annot_pragma_fenv_access) ||
+ Tok.is(tok::annot_pragma_fenv_access_ms));
+ tok::OnOffSwitch OOS =
+ static_cast<tok::OnOffSwitch>(
+ reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
+
+ bool IsEnabled;
+ switch (OOS) {
+ case tok::OOS_ON:
+ IsEnabled = true;
+ break;
+ case tok::OOS_OFF:
+ IsEnabled = false;
+ break;
+ case tok::OOS_DEFAULT: // FIXME: Add this cli option when it makes sense.
+ IsEnabled = false;
+ break;
+ }
+
+ SourceLocation PragmaLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaFEnvAccess(PragmaLoc, IsEnabled);
+}
+
+void Parser::HandlePragmaFEnvRound() {
+ assert(Tok.is(tok::annot_pragma_fenv_round));
+ auto RM = static_cast<llvm::RoundingMode>(
+ reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
+
+ SourceLocation PragmaLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaFEnvRound(PragmaLoc, RM);
+}
+
+StmtResult Parser::HandlePragmaCaptured()
+{
+ assert(Tok.is(tok::annot_pragma_captured));
+ ConsumeAnnotationToken();
+
+ if (Tok.isNot(tok::l_brace)) {
+ PP.Diag(Tok, diag::err_expected) << tok::l_brace;
+ return StmtError();
+ }
+
+ SourceLocation Loc = Tok.getLocation();
+
+ ParseScope CapturedRegionScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+ Actions.ActOnCapturedRegionStart(Loc, getCurScope(), CR_Default,
+ /*NumParams=*/1);
+
+ StmtResult R = ParseCompoundStatement();
+ CapturedRegionScope.Exit();
+
+ if (R.isInvalid()) {
+ Actions.ActOnCapturedRegionError();
+ return StmtError();
+ }
+
+ return Actions.ActOnCapturedRegionEnd(R.get());
+}
+
+namespace {
+ enum OpenCLExtState : char {
+ Disable, Enable, Begin, End
+ };
+ typedef std::pair<const IdentifierInfo *, OpenCLExtState> OpenCLExtData;
+}
+
+void Parser::HandlePragmaOpenCLExtension() {
+ assert(Tok.is(tok::annot_pragma_opencl_extension));
+ OpenCLExtData *Data = static_cast<OpenCLExtData*>(Tok.getAnnotationValue());
+ auto State = Data->second;
+ auto Ident = Data->first;
+ SourceLocation NameLoc = Tok.getLocation();
+ ConsumeAnnotationToken();
+
+ auto &Opt = Actions.getOpenCLOptions();
+ auto Name = Ident->getName();
+ // OpenCL 1.1 9.1: "The all variant sets the behavior for all extensions,
+ // overriding all previously issued extension directives, but only if the
+ // behavior is set to disable."
+ if (Name == "all") {
+ if (State == Disable)
+ Opt.disableAll();
+ else
+ PP.Diag(NameLoc, diag::warn_pragma_expected_predicate) << 1;
+ } else if (State == Begin) {
+ if (!Opt.isKnown(Name) || !Opt.isSupported(Name, getLangOpts())) {
+ Opt.support(Name);
+ // FIXME: Default behavior of the extension pragma is not defined.
+ // Therefore, it should never be added by default.
+ Opt.acceptsPragma(Name);
+ }
+ } else if (State == End) {
+ // There is no behavior for this directive. We only accept this for
+ // backward compatibility.
+ } else if (!Opt.isKnown(Name) || !Opt.isWithPragma(Name))
+ PP.Diag(NameLoc, diag::warn_pragma_unknown_extension) << Ident;
+ else if (Opt.isSupportedExtension(Name, getLangOpts()))
+ Opt.enable(Name, State == Enable);
+ else if (Opt.isSupportedCoreOrOptionalCore(Name, getLangOpts()))
+ PP.Diag(NameLoc, diag::warn_pragma_extension_is_core) << Ident;
+ else
+ PP.Diag(NameLoc, diag::warn_pragma_unsupported_extension) << Ident;
+}
+
+void Parser::HandlePragmaMSPointersToMembers() {
+ assert(Tok.is(tok::annot_pragma_ms_pointers_to_members));
+ LangOptions::PragmaMSPointersToMembersKind RepresentationMethod =
+ static_cast<LangOptions::PragmaMSPointersToMembersKind>(
+ reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
+ SourceLocation PragmaLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaMSPointersToMembers(RepresentationMethod, PragmaLoc);
+}
+
+void Parser::HandlePragmaMSVtorDisp() {
+ assert(Tok.is(tok::annot_pragma_ms_vtordisp));
+ uintptr_t Value = reinterpret_cast<uintptr_t>(Tok.getAnnotationValue());
+ Sema::PragmaMsStackAction Action =
+ static_cast<Sema::PragmaMsStackAction>((Value >> 16) & 0xFFFF);
+ MSVtorDispMode Mode = MSVtorDispMode(Value & 0xFFFF);
+ SourceLocation PragmaLoc = ConsumeAnnotationToken();
+ Actions.ActOnPragmaMSVtorDisp(Action, PragmaLoc, Mode);
+}
+
+void Parser::HandlePragmaMSPragma() {
+ assert(Tok.is(tok::annot_pragma_ms_pragma));
+ // Grab the tokens out of the annotation and enter them into the stream.
+ auto TheTokens =
+ (std::pair<std::unique_ptr<Token[]>, size_t> *)Tok.getAnnotationValue();
+ PP.EnterTokenStream(std::move(TheTokens->first), TheTokens->second, true,
+ /*IsReinject=*/true);
+ SourceLocation PragmaLocation = ConsumeAnnotationToken();
+ assert(Tok.isAnyIdentifier());
+ StringRef PragmaName = Tok.getIdentifierInfo()->getName();
+ PP.Lex(Tok); // pragma kind
+
+ // Figure out which #pragma we're dealing with. The switch has no default
+ // because lex shouldn't emit the annotation token for unrecognized pragmas.
+ typedef bool (Parser::*PragmaHandler)(StringRef, SourceLocation);
+ PragmaHandler Handler =
+ llvm::StringSwitch<PragmaHandler>(PragmaName)
+ .Case("data_seg", &Parser::HandlePragmaMSSegment)
+ .Case("bss_seg", &Parser::HandlePragmaMSSegment)
+ .Case("const_seg", &Parser::HandlePragmaMSSegment)
+ .Case("code_seg", &Parser::HandlePragmaMSSegment)
+ .Case("section", &Parser::HandlePragmaMSSection)
+ .Case("init_seg", &Parser::HandlePragmaMSInitSeg)
+ .Case("strict_gs_check", &Parser::HandlePragmaMSStrictGuardStackCheck)
+ .Case("function", &Parser::HandlePragmaMSFunction)
+ .Case("alloc_text", &Parser::HandlePragmaMSAllocText)
+ .Case("optimize", &Parser::HandlePragmaMSOptimize);
+
+ if (!(this->*Handler)(PragmaName, PragmaLocation)) {
+ // Pragma handling failed, and has been diagnosed. Slurp up the tokens
+ // until eof (really end of line) to prevent follow-on errors.
+ while (Tok.isNot(tok::eof))
+ PP.Lex(Tok);
+ PP.Lex(Tok);
+ }
+}
+
+bool Parser::HandlePragmaMSSection(StringRef PragmaName,
+ SourceLocation PragmaLocation) {
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_lparen) << PragmaName;
+ return false;
+ }
+ PP.Lex(Tok); // (
+ // Parsing code for pragma section
+ if (Tok.isNot(tok::string_literal)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_section_name)
+ << PragmaName;
+ return false;
+ }
+ ExprResult StringResult = ParseStringLiteralExpression();
+ if (StringResult.isInvalid())
+ return false; // Already diagnosed.
+ StringLiteral *SegmentName = cast<StringLiteral>(StringResult.get());
+ if (SegmentName->getCharByteWidth() != 1) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string)
+ << PragmaName;
+ return false;
+ }
+ int SectionFlags = ASTContext::PSF_Read;
+ bool SectionFlagsAreDefault = true;
+ while (Tok.is(tok::comma)) {
+ PP.Lex(Tok); // ,
+ // Ignore "long" and "short".
+ // They are undocumented, but widely used, section attributes which appear
+ // to do nothing.
+ if (Tok.is(tok::kw_long) || Tok.is(tok::kw_short)) {
+ PP.Lex(Tok); // long/short
+ continue;
+ }
+
+ if (!Tok.isAnyIdentifier()) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_action_or_r_paren)
+ << PragmaName;
+ return false;
+ }
+ ASTContext::PragmaSectionFlag Flag =
+ llvm::StringSwitch<ASTContext::PragmaSectionFlag>(
+ Tok.getIdentifierInfo()->getName())
+ .Case("read", ASTContext::PSF_Read)
+ .Case("write", ASTContext::PSF_Write)
+ .Case("execute", ASTContext::PSF_Execute)
+ .Case("shared", ASTContext::PSF_Invalid)
+ .Case("nopage", ASTContext::PSF_Invalid)
+ .Case("nocache", ASTContext::PSF_Invalid)
+ .Case("discard", ASTContext::PSF_Invalid)
+ .Case("remove", ASTContext::PSF_Invalid)
+ .Default(ASTContext::PSF_None);
+ if (Flag == ASTContext::PSF_None || Flag == ASTContext::PSF_Invalid) {
+ PP.Diag(PragmaLocation, Flag == ASTContext::PSF_None
+ ? diag::warn_pragma_invalid_specific_action
+ : diag::warn_pragma_unsupported_action)
+ << PragmaName << Tok.getIdentifierInfo()->getName();
+ return false;
+ }
+ SectionFlags |= Flag;
+ SectionFlagsAreDefault = false;
+ PP.Lex(Tok); // Identifier
+ }
+ // If no section attributes are specified, the section will be marked as
+ // read/write.
+ if (SectionFlagsAreDefault)
+ SectionFlags |= ASTContext::PSF_Write;
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_rparen) << PragmaName;
+ return false;
+ }
+ PP.Lex(Tok); // )
+ if (Tok.isNot(tok::eof)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaName;
+ return false;
+ }
+ PP.Lex(Tok); // eof
+ Actions.ActOnPragmaMSSection(PragmaLocation, SectionFlags, SegmentName);
+ return true;
+}
+
+bool Parser::HandlePragmaMSSegment(StringRef PragmaName,
+ SourceLocation PragmaLocation) {
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_lparen) << PragmaName;
+ return false;
+ }
+ PP.Lex(Tok); // (
+ Sema::PragmaMsStackAction Action = Sema::PSK_Reset;
+ StringRef SlotLabel;
+ if (Tok.isAnyIdentifier()) {
+ StringRef PushPop = Tok.getIdentifierInfo()->getName();
+ if (PushPop == "push")
+ Action = Sema::PSK_Push;
+ else if (PushPop == "pop")
+ Action = Sema::PSK_Pop;
+ else {
+ PP.Diag(PragmaLocation,
+ diag::warn_pragma_expected_section_push_pop_or_name)
+ << PragmaName;
+ return false;
+ }
+ if (Action != Sema::PSK_Reset) {
+ PP.Lex(Tok); // push | pop
+ if (Tok.is(tok::comma)) {
+ PP.Lex(Tok); // ,
+ // If we've got a comma, we either need a label or a string.
+ if (Tok.isAnyIdentifier()) {
+ SlotLabel = Tok.getIdentifierInfo()->getName();
+ PP.Lex(Tok); // identifier
+ if (Tok.is(tok::comma))
+ PP.Lex(Tok);
+ else if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_punc)
+ << PragmaName;
+ return false;
+ }
+ }
+ } else if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_punc) << PragmaName;
+ return false;
+ }
+ }
+ }
+ // Grab the string literal for our section name.
+ StringLiteral *SegmentName = nullptr;
+ if (Tok.isNot(tok::r_paren)) {
+ if (Tok.isNot(tok::string_literal)) {
+ unsigned DiagID = Action != Sema::PSK_Reset ? !SlotLabel.empty() ?
+ diag::warn_pragma_expected_section_name :
+ diag::warn_pragma_expected_section_label_or_name :
+ diag::warn_pragma_expected_section_push_pop_or_name;
+ PP.Diag(PragmaLocation, DiagID) << PragmaName;
+ return false;
+ }
+ ExprResult StringResult = ParseStringLiteralExpression();
+ if (StringResult.isInvalid())
+ return false; // Already diagnosed.
+ SegmentName = cast<StringLiteral>(StringResult.get());
+ if (SegmentName->getCharByteWidth() != 1) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string)
+ << PragmaName;
+ return false;
+ }
+ // Setting section "" has no effect
+ if (SegmentName->getLength())
+ Action = (Sema::PragmaMsStackAction)(Action | Sema::PSK_Set);
+ }
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_rparen) << PragmaName;
+ return false;
+ }
+ PP.Lex(Tok); // )
+ if (Tok.isNot(tok::eof)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaName;
+ return false;
+ }
+ PP.Lex(Tok); // eof
+ Actions.ActOnPragmaMSSeg(PragmaLocation, Action, SlotLabel,
+ SegmentName, PragmaName);
+ return true;
+}
+
+// #pragma init_seg({ compiler | lib | user | "section-name" [, func-name]} )
+bool Parser::HandlePragmaMSInitSeg(StringRef PragmaName,
+ SourceLocation PragmaLocation) {
+ if (getTargetInfo().getTriple().getEnvironment() != llvm::Triple::MSVC) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_init_seg_unsupported_target);
+ return false;
+ }
+
+ if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
+ PragmaName))
+ return false;
+
+ // Parse either the known section names or the string section name.
+ StringLiteral *SegmentName = nullptr;
+ if (Tok.isAnyIdentifier()) {
+ auto *II = Tok.getIdentifierInfo();
+ StringRef Section = llvm::StringSwitch<StringRef>(II->getName())
+ .Case("compiler", "\".CRT$XCC\"")
+ .Case("lib", "\".CRT$XCL\"")
+ .Case("user", "\".CRT$XCU\"")
+ .Default("");
+
+ if (!Section.empty()) {
+ // Pretend the user wrote the appropriate string literal here.
+ Token Toks[1];
+ Toks[0].startToken();
+ Toks[0].setKind(tok::string_literal);
+ Toks[0].setLocation(Tok.getLocation());
+ Toks[0].setLiteralData(Section.data());
+ Toks[0].setLength(Section.size());
+ SegmentName =
+ cast<StringLiteral>(Actions.ActOnStringLiteral(Toks, nullptr).get());
+ PP.Lex(Tok);
+ }
+ } else if (Tok.is(tok::string_literal)) {
+ ExprResult StringResult = ParseStringLiteralExpression();
+ if (StringResult.isInvalid())
+ return false;
+ SegmentName = cast<StringLiteral>(StringResult.get());
+ if (SegmentName->getCharByteWidth() != 1) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string)
+ << PragmaName;
+ return false;
+ }
+ // FIXME: Add support for the '[, func-name]' part of the pragma.
+ }
+
+ if (!SegmentName) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_init_seg) << PragmaName;
+ return false;
+ }
+
+ if (ExpectAndConsume(tok::r_paren, diag::warn_pragma_expected_rparen,
+ PragmaName) ||
+ ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol,
+ PragmaName))
+ return false;
+
+ Actions.ActOnPragmaMSInitSeg(PragmaLocation, SegmentName);
+ return true;
+}
+
+// #pragma strict_gs_check(pop)
+// #pragma strict_gs_check(push, "on" | "off")
+// #pragma strict_gs_check("on" | "off")
+bool Parser::HandlePragmaMSStrictGuardStackCheck(
+ StringRef PragmaName, SourceLocation PragmaLocation) {
+ if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
+ PragmaName))
+ return false;
+
+ Sema::PragmaMsStackAction Action = Sema::PSK_Set;
+ if (Tok.is(tok::identifier)) {
+ StringRef PushPop = Tok.getIdentifierInfo()->getName();
+ if (PushPop == "push") {
+ PP.Lex(Tok);
+ Action = Sema::PSK_Push;
+ if (ExpectAndConsume(tok::comma, diag::warn_pragma_expected_punc,
+ PragmaName))
+ return false;
+ } else if (PushPop == "pop") {
+ PP.Lex(Tok);
+ Action = Sema::PSK_Pop;
+ }
+ }
+
+ bool Value = false;
+ if (Action & Sema::PSK_Push || Action & Sema::PSK_Set) {
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II && II->isStr("off")) {
+ PP.Lex(Tok);
+ Value = false;
+ } else if (II && II->isStr("on")) {
+ PP.Lex(Tok);
+ Value = true;
+ } else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_action)
+ << PragmaName;
+ return false;
+ }
+ }
+
+ // Finish the pragma: ')' $
+ if (ExpectAndConsume(tok::r_paren, diag::warn_pragma_expected_rparen,
+ PragmaName))
+ return false;
+
+ if (ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol,
+ PragmaName))
+ return false;
+
+ Actions.ActOnPragmaMSStrictGuardStackCheck(PragmaLocation, Action, Value);
+ return true;
+}
+
+bool Parser::HandlePragmaMSAllocText(StringRef PragmaName,
+ SourceLocation PragmaLocation) {
+ Token FirstTok = Tok;
+ if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
+ PragmaName))
+ return false;
+
+ StringRef Section;
+ if (Tok.is(tok::string_literal)) {
+ ExprResult StringResult = ParseStringLiteralExpression();
+ if (StringResult.isInvalid())
+ return false; // Already diagnosed.
+ StringLiteral *SegmentName = cast<StringLiteral>(StringResult.get());
+ if (SegmentName->getCharByteWidth() != 1) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string)
+ << PragmaName;
+ return false;
+ }
+ Section = SegmentName->getString();
+ } else if (Tok.is(tok::identifier)) {
+ Section = Tok.getIdentifierInfo()->getName();
+ PP.Lex(Tok);
+ } else {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_section_name)
+ << PragmaName;
+ return false;
+ }
+
+ if (ExpectAndConsume(tok::comma, diag::warn_pragma_expected_comma,
+ PragmaName))
+ return false;
+
+ SmallVector<std::tuple<IdentifierInfo *, SourceLocation>> Functions;
+ while (true) {
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << PragmaName;
+ return false;
+ }
+
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ Functions.emplace_back(II, Tok.getLocation());
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::comma))
+ break;
+ PP.Lex(Tok);
+ }
+
+ if (ExpectAndConsume(tok::r_paren, diag::warn_pragma_expected_rparen,
+ PragmaName) ||
+ ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol,
+ PragmaName))
+ return false;
+
+ Actions.ActOnPragmaMSAllocText(FirstTok.getLocation(), Section, Functions);
+ return true;
+}
+
+static std::string PragmaLoopHintString(Token PragmaName, Token Option) {
+ StringRef Str = PragmaName.getIdentifierInfo()->getName();
+ std::string ClangLoopStr("clang loop ");
+ if (Str == "loop" && Option.getIdentifierInfo())
+ ClangLoopStr += Option.getIdentifierInfo()->getName();
+ return std::string(llvm::StringSwitch<StringRef>(Str)
+ .Case("loop", ClangLoopStr)
+ .Case("unroll_and_jam", Str)
+ .Case("unroll", Str)
+ .Default(""));
+}
+
+bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
+ assert(Tok.is(tok::annot_pragma_loop_hint));
+ PragmaLoopHintInfo *Info =
+ static_cast<PragmaLoopHintInfo *>(Tok.getAnnotationValue());
+
+ IdentifierInfo *PragmaNameInfo = Info->PragmaName.getIdentifierInfo();
+ Hint.PragmaNameLoc = IdentifierLoc::create(
+ Actions.Context, Info->PragmaName.getLocation(), PragmaNameInfo);
+
+ // It is possible that the loop hint has no option identifier, such as
+ // #pragma unroll(4).
+ IdentifierInfo *OptionInfo = Info->Option.is(tok::identifier)
+ ? Info->Option.getIdentifierInfo()
+ : nullptr;
+ Hint.OptionLoc = IdentifierLoc::create(
+ Actions.Context, Info->Option.getLocation(), OptionInfo);
+
+ llvm::ArrayRef<Token> Toks = Info->Toks;
+
+ // Return a valid hint if pragma unroll or nounroll were specified
+ // without an argument.
+ auto IsLoopHint = llvm::StringSwitch<bool>(PragmaNameInfo->getName())
+ .Cases("unroll", "nounroll", "unroll_and_jam",
+ "nounroll_and_jam", true)
+ .Default(false);
+
+ if (Toks.empty() && IsLoopHint) {
+ ConsumeAnnotationToken();
+ Hint.Range = Info->PragmaName.getLocation();
+ return true;
+ }
+
+ // The constant expression is always followed by an eof token, which increases
+ // the TokSize by 1.
+ assert(!Toks.empty() &&
+ "PragmaLoopHintInfo::Toks must contain at least one token.");
+
+ // If no option is specified the argument is assumed to be a constant expr.
+ bool OptionUnroll = false;
+ bool OptionUnrollAndJam = false;
+ bool OptionDistribute = false;
+ bool OptionPipelineDisabled = false;
+ bool StateOption = false;
+ if (OptionInfo) { // Pragma Unroll does not specify an option.
+ OptionUnroll = OptionInfo->isStr("unroll");
+ OptionUnrollAndJam = OptionInfo->isStr("unroll_and_jam");
+ OptionDistribute = OptionInfo->isStr("distribute");
+ OptionPipelineDisabled = OptionInfo->isStr("pipeline");
+ StateOption = llvm::StringSwitch<bool>(OptionInfo->getName())
+ .Case("vectorize", true)
+ .Case("interleave", true)
+ .Case("vectorize_predicate", true)
+ .Default(false) ||
+ OptionUnroll || OptionUnrollAndJam || OptionDistribute ||
+ OptionPipelineDisabled;
+ }
+
+ bool AssumeSafetyArg = !OptionUnroll && !OptionUnrollAndJam &&
+ !OptionDistribute && !OptionPipelineDisabled;
+ // Verify loop hint has an argument.
+ if (Toks[0].is(tok::eof)) {
+ ConsumeAnnotationToken();
+ Diag(Toks[0].getLocation(), diag::err_pragma_loop_missing_argument)
+ << /*StateArgument=*/StateOption
+ << /*FullKeyword=*/(OptionUnroll || OptionUnrollAndJam)
+ << /*AssumeSafetyKeyword=*/AssumeSafetyArg;
+ return false;
+ }
+
+ // Validate the argument.
+ if (StateOption) {
+ ConsumeAnnotationToken();
+ SourceLocation StateLoc = Toks[0].getLocation();
+ IdentifierInfo *StateInfo = Toks[0].getIdentifierInfo();
+
+ bool Valid = StateInfo &&
+ llvm::StringSwitch<bool>(StateInfo->getName())
+ .Case("disable", true)
+ .Case("enable", !OptionPipelineDisabled)
+ .Case("full", OptionUnroll || OptionUnrollAndJam)
+ .Case("assume_safety", AssumeSafetyArg)
+ .Default(false);
+ if (!Valid) {
+ if (OptionPipelineDisabled) {
+ Diag(Toks[0].getLocation(), diag::err_pragma_pipeline_invalid_keyword);
+ } else {
+ Diag(Toks[0].getLocation(), diag::err_pragma_invalid_keyword)
+ << /*FullKeyword=*/(OptionUnroll || OptionUnrollAndJam)
+ << /*AssumeSafetyKeyword=*/AssumeSafetyArg;
+ }
+ return false;
+ }
+ if (Toks.size() > 2)
+ Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaLoopHintString(Info->PragmaName, Info->Option);
+ Hint.StateLoc = IdentifierLoc::create(Actions.Context, StateLoc, StateInfo);
+ } else if (OptionInfo && OptionInfo->getName() == "vectorize_width") {
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/false,
+ /*IsReinject=*/false);
+ ConsumeAnnotationToken();
+
+ SourceLocation StateLoc = Toks[0].getLocation();
+ IdentifierInfo *StateInfo = Toks[0].getIdentifierInfo();
+ StringRef IsScalableStr = StateInfo ? StateInfo->getName() : "";
+
+ // Look for vectorize_width(fixed|scalable)
+ if (IsScalableStr == "scalable" || IsScalableStr == "fixed") {
+ PP.Lex(Tok); // Identifier
+
+ if (Toks.size() > 2) {
+ Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaLoopHintString(Info->PragmaName, Info->Option);
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+ }
+
+ Hint.StateLoc =
+ IdentifierLoc::create(Actions.Context, StateLoc, StateInfo);
+
+ ConsumeToken(); // Consume the constant expression eof terminator.
+ } else {
+ // Enter constant expression including eof terminator into token stream.
+ ExprResult R = ParseConstantExpression();
+
+ if (R.isInvalid() && !Tok.is(tok::comma))
+ Diag(Toks[0].getLocation(),
+ diag::note_pragma_loop_invalid_vectorize_option);
+
+ bool Arg2Error = false;
+ if (Tok.is(tok::comma)) {
+ PP.Lex(Tok); // ,
+
+ StateInfo = Tok.getIdentifierInfo();
+ IsScalableStr = StateInfo->getName();
+
+ if (IsScalableStr != "scalable" && IsScalableStr != "fixed") {
+ Diag(Tok.getLocation(),
+ diag::err_pragma_loop_invalid_vectorize_option);
+ Arg2Error = true;
+ } else
+ Hint.StateLoc =
+ IdentifierLoc::create(Actions.Context, StateLoc, StateInfo);
+
+ PP.Lex(Tok); // Identifier
+ }
+
+ // Tokens following an error in an ill-formed constant expression will
+ // remain in the token stream and must be removed.
+ if (Tok.isNot(tok::eof)) {
+ Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaLoopHintString(Info->PragmaName, Info->Option);
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+ }
+
+ ConsumeToken(); // Consume the constant expression eof terminator.
+
+ if (Arg2Error || R.isInvalid() ||
+ Actions.CheckLoopHintExpr(R.get(), Toks[0].getLocation()))
+ return false;
+
+ // Argument is a constant expression with an integer type.
+ Hint.ValueExpr = R.get();
+ }
+ } else {
+ // Enter constant expression including eof terminator into token stream.
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/false,
+ /*IsReinject=*/false);
+ ConsumeAnnotationToken();
+ ExprResult R = ParseConstantExpression();
+
+ // Tokens following an error in an ill-formed constant expression will
+ // remain in the token stream and must be removed.
+ if (Tok.isNot(tok::eof)) {
+ Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaLoopHintString(Info->PragmaName, Info->Option);
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+ }
+
+ ConsumeToken(); // Consume the constant expression eof terminator.
+
+ if (R.isInvalid() ||
+ Actions.CheckLoopHintExpr(R.get(), Toks[0].getLocation()))
+ return false;
+
+ // Argument is a constant expression with an integer type.
+ Hint.ValueExpr = R.get();
+ }
+
+ Hint.Range = SourceRange(Info->PragmaName.getLocation(),
+ Info->Toks.back().getLocation());
+ return true;
+}
+
+namespace {
+struct PragmaAttributeInfo {
+ enum ActionType { Push, Pop, Attribute };
+ ParsedAttributes &Attributes;
+ ActionType Action;
+ const IdentifierInfo *Namespace = nullptr;
+ ArrayRef<Token> Tokens;
+
+ PragmaAttributeInfo(ParsedAttributes &Attributes) : Attributes(Attributes) {}
+};
+
+#include "clang/Parse/AttrSubMatchRulesParserStringSwitches.inc"
+
+} // end anonymous namespace
+
+static StringRef getIdentifier(const Token &Tok) {
+ if (Tok.is(tok::identifier))
+ return Tok.getIdentifierInfo()->getName();
+ const char *S = tok::getKeywordSpelling(Tok.getKind());
+ if (!S)
+ return "";
+ return S;
+}
+
+static bool isAbstractAttrMatcherRule(attr::SubjectMatchRule Rule) {
+ using namespace attr;
+ switch (Rule) {
+#define ATTR_MATCH_RULE(Value, Spelling, IsAbstract) \
+ case Value: \
+ return IsAbstract;
+#include "clang/Basic/AttrSubMatchRulesList.inc"
+ }
+ llvm_unreachable("Invalid attribute subject match rule");
+ return false;
+}
+
+static void diagnoseExpectedAttributeSubjectSubRule(
+ Parser &PRef, attr::SubjectMatchRule PrimaryRule, StringRef PrimaryRuleName,
+ SourceLocation SubRuleLoc) {
+ auto Diagnostic =
+ PRef.Diag(SubRuleLoc,
+ diag::err_pragma_attribute_expected_subject_sub_identifier)
+ << PrimaryRuleName;
+ if (const char *SubRules = validAttributeSubjectMatchSubRules(PrimaryRule))
+ Diagnostic << /*SubRulesSupported=*/1 << SubRules;
+ else
+ Diagnostic << /*SubRulesSupported=*/0;
+}
+
+static void diagnoseUnknownAttributeSubjectSubRule(
+ Parser &PRef, attr::SubjectMatchRule PrimaryRule, StringRef PrimaryRuleName,
+ StringRef SubRuleName, SourceLocation SubRuleLoc) {
+
+ auto Diagnostic =
+ PRef.Diag(SubRuleLoc, diag::err_pragma_attribute_unknown_subject_sub_rule)
+ << SubRuleName << PrimaryRuleName;
+ if (const char *SubRules = validAttributeSubjectMatchSubRules(PrimaryRule))
+ Diagnostic << /*SubRulesSupported=*/1 << SubRules;
+ else
+ Diagnostic << /*SubRulesSupported=*/0;
+}
+
+bool Parser::ParsePragmaAttributeSubjectMatchRuleSet(
+ attr::ParsedSubjectMatchRuleSet &SubjectMatchRules, SourceLocation &AnyLoc,
+ SourceLocation &LastMatchRuleEndLoc) {
+ bool IsAny = false;
+ BalancedDelimiterTracker AnyParens(*this, tok::l_paren);
+ if (getIdentifier(Tok) == "any") {
+ AnyLoc = ConsumeToken();
+ IsAny = true;
+ if (AnyParens.expectAndConsume())
+ return true;
+ }
+
+ do {
+ // Parse the subject matcher rule.
+ StringRef Name = getIdentifier(Tok);
+ if (Name.empty()) {
+ Diag(Tok, diag::err_pragma_attribute_expected_subject_identifier);
+ return true;
+ }
+ std::pair<std::optional<attr::SubjectMatchRule>,
+ std::optional<attr::SubjectMatchRule> (*)(StringRef, bool)>
+ Rule = isAttributeSubjectMatchRule(Name);
+ if (!Rule.first) {
+ Diag(Tok, diag::err_pragma_attribute_unknown_subject_rule) << Name;
+ return true;
+ }
+ attr::SubjectMatchRule PrimaryRule = *Rule.first;
+ SourceLocation RuleLoc = ConsumeToken();
+
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ if (isAbstractAttrMatcherRule(PrimaryRule)) {
+ if (Parens.expectAndConsume())
+ return true;
+ } else if (Parens.consumeOpen()) {
+ if (!SubjectMatchRules
+ .insert(
+ std::make_pair(PrimaryRule, SourceRange(RuleLoc, RuleLoc)))
+ .second)
+ Diag(RuleLoc, diag::err_pragma_attribute_duplicate_subject)
+ << Name
+ << FixItHint::CreateRemoval(SourceRange(
+ RuleLoc, Tok.is(tok::comma) ? Tok.getLocation() : RuleLoc));
+ LastMatchRuleEndLoc = RuleLoc;
+ continue;
+ }
+
+ // Parse the sub-rules.
+ StringRef SubRuleName = getIdentifier(Tok);
+ if (SubRuleName.empty()) {
+ diagnoseExpectedAttributeSubjectSubRule(*this, PrimaryRule, Name,
+ Tok.getLocation());
+ return true;
+ }
+ attr::SubjectMatchRule SubRule;
+ if (SubRuleName == "unless") {
+ SourceLocation SubRuleLoc = ConsumeToken();
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ if (Parens.expectAndConsume())
+ return true;
+ SubRuleName = getIdentifier(Tok);
+ if (SubRuleName.empty()) {
+ diagnoseExpectedAttributeSubjectSubRule(*this, PrimaryRule, Name,
+ SubRuleLoc);
+ return true;
+ }
+ auto SubRuleOrNone = Rule.second(SubRuleName, /*IsUnless=*/true);
+ if (!SubRuleOrNone) {
+ std::string SubRuleUnlessName = "unless(" + SubRuleName.str() + ")";
+ diagnoseUnknownAttributeSubjectSubRule(*this, PrimaryRule, Name,
+ SubRuleUnlessName, SubRuleLoc);
+ return true;
+ }
+ SubRule = *SubRuleOrNone;
+ ConsumeToken();
+ if (Parens.consumeClose())
+ return true;
+ } else {
+ auto SubRuleOrNone = Rule.second(SubRuleName, /*IsUnless=*/false);
+ if (!SubRuleOrNone) {
+ diagnoseUnknownAttributeSubjectSubRule(*this, PrimaryRule, Name,
+ SubRuleName, Tok.getLocation());
+ return true;
+ }
+ SubRule = *SubRuleOrNone;
+ ConsumeToken();
+ }
+ SourceLocation RuleEndLoc = Tok.getLocation();
+ LastMatchRuleEndLoc = RuleEndLoc;
+ if (Parens.consumeClose())
+ return true;
+ if (!SubjectMatchRules
+ .insert(std::make_pair(SubRule, SourceRange(RuleLoc, RuleEndLoc)))
+ .second) {
+ Diag(RuleLoc, diag::err_pragma_attribute_duplicate_subject)
+ << attr::getSubjectMatchRuleSpelling(SubRule)
+ << FixItHint::CreateRemoval(SourceRange(
+ RuleLoc, Tok.is(tok::comma) ? Tok.getLocation() : RuleEndLoc));
+ continue;
+ }
+ } while (IsAny && TryConsumeToken(tok::comma));
+
+ if (IsAny)
+ if (AnyParens.consumeClose())
+ return true;
+
+ return false;
+}
+
+namespace {
+
+/// Describes the stage at which attribute subject rule parsing was interrupted.
+enum class MissingAttributeSubjectRulesRecoveryPoint {
+ Comma,
+ ApplyTo,
+ Equals,
+ Any,
+ None,
+};
+
+MissingAttributeSubjectRulesRecoveryPoint
+getAttributeSubjectRulesRecoveryPointForToken(const Token &Tok) {
+ if (const auto *II = Tok.getIdentifierInfo()) {
+ if (II->isStr("apply_to"))
+ return MissingAttributeSubjectRulesRecoveryPoint::ApplyTo;
+ if (II->isStr("any"))
+ return MissingAttributeSubjectRulesRecoveryPoint::Any;
+ }
+ if (Tok.is(tok::equal))
+ return MissingAttributeSubjectRulesRecoveryPoint::Equals;
+ return MissingAttributeSubjectRulesRecoveryPoint::None;
+}
+
+/// Creates a diagnostic for the attribute subject rule parsing diagnostic that
+/// suggests the possible attribute subject rules in a fix-it together with
+/// any other missing tokens.
+DiagnosticBuilder createExpectedAttributeSubjectRulesTokenDiagnostic(
+ unsigned DiagID, ParsedAttributes &Attrs,
+ MissingAttributeSubjectRulesRecoveryPoint Point, Parser &PRef) {
+ SourceLocation Loc = PRef.getEndOfPreviousToken();
+ if (Loc.isInvalid())
+ Loc = PRef.getCurToken().getLocation();
+ auto Diagnostic = PRef.Diag(Loc, DiagID);
+ std::string FixIt;
+ MissingAttributeSubjectRulesRecoveryPoint EndPoint =
+ getAttributeSubjectRulesRecoveryPointForToken(PRef.getCurToken());
+ if (Point == MissingAttributeSubjectRulesRecoveryPoint::Comma)
+ FixIt = ", ";
+ if (Point <= MissingAttributeSubjectRulesRecoveryPoint::ApplyTo &&
+ EndPoint > MissingAttributeSubjectRulesRecoveryPoint::ApplyTo)
+ FixIt += "apply_to";
+ if (Point <= MissingAttributeSubjectRulesRecoveryPoint::Equals &&
+ EndPoint > MissingAttributeSubjectRulesRecoveryPoint::Equals)
+ FixIt += " = ";
+ SourceRange FixItRange(Loc);
+ if (EndPoint == MissingAttributeSubjectRulesRecoveryPoint::None) {
+ // Gather the subject match rules that are supported by the attribute.
+ // Add all the possible rules initially.
+ llvm::BitVector IsMatchRuleAvailable(attr::SubjectMatchRule_Last + 1, true);
+ // Remove the ones that are not supported by any of the attributes.
+ for (const ParsedAttr &Attribute : Attrs) {
+ SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4> MatchRules;
+ Attribute.getMatchRules(PRef.getLangOpts(), MatchRules);
+ llvm::BitVector IsSupported(attr::SubjectMatchRule_Last + 1);
+ for (const auto &Rule : MatchRules) {
+ // Ensure that the missing rule is reported in the fix-it only when it's
+ // supported in the current language mode.
+ if (!Rule.second)
+ continue;
+ IsSupported[Rule.first] = true;
+ }
+ IsMatchRuleAvailable &= IsSupported;
+ }
+ if (IsMatchRuleAvailable.count() == 0) {
+ // FIXME: We can emit a "fix-it" with a subject list placeholder when
+ // placeholders will be supported by the fix-its.
+ return Diagnostic;
+ }
+ FixIt += "any(";
+ bool NeedsComma = false;
+ for (unsigned I = 0; I <= attr::SubjectMatchRule_Last; I++) {
+ if (!IsMatchRuleAvailable[I])
+ continue;
+ if (NeedsComma)
+ FixIt += ", ";
+ else
+ NeedsComma = true;
+ FixIt += attr::getSubjectMatchRuleSpelling(
+ static_cast<attr::SubjectMatchRule>(I));
+ }
+ FixIt += ")";
+ // Check if we need to remove the range
+ PRef.SkipUntil(tok::eof, Parser::StopBeforeMatch);
+ FixItRange.setEnd(PRef.getCurToken().getLocation());
+ }
+ if (FixItRange.getBegin() == FixItRange.getEnd())
+ Diagnostic << FixItHint::CreateInsertion(FixItRange.getBegin(), FixIt);
+ else
+ Diagnostic << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(FixItRange), FixIt);
+ return Diagnostic;
+}
+
+} // end anonymous namespace
+
+void Parser::HandlePragmaAttribute() {
+ assert(Tok.is(tok::annot_pragma_attribute) &&
+ "Expected #pragma attribute annotation token");
+ SourceLocation PragmaLoc = Tok.getLocation();
+ auto *Info = static_cast<PragmaAttributeInfo *>(Tok.getAnnotationValue());
+ if (Info->Action == PragmaAttributeInfo::Pop) {
+ ConsumeAnnotationToken();
+ Actions.ActOnPragmaAttributePop(PragmaLoc, Info->Namespace);
+ return;
+ }
+ // Parse the actual attribute with its arguments.
+ assert((Info->Action == PragmaAttributeInfo::Push ||
+ Info->Action == PragmaAttributeInfo::Attribute) &&
+ "Unexpected #pragma attribute command");
+
+ if (Info->Action == PragmaAttributeInfo::Push && Info->Tokens.empty()) {
+ ConsumeAnnotationToken();
+ Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc, Info->Namespace);
+ return;
+ }
+
+ PP.EnterTokenStream(Info->Tokens, /*DisableMacroExpansion=*/false,
+ /*IsReinject=*/false);
+ ConsumeAnnotationToken();
+
+ ParsedAttributes &Attrs = Info->Attributes;
+ Attrs.clearListOnly();
+
+ auto SkipToEnd = [this]() {
+ SkipUntil(tok::eof, StopBeforeMatch);
+ ConsumeToken();
+ };
+
+ if (Tok.is(tok::l_square) && NextToken().is(tok::l_square)) {
+ // Parse the CXX11 style attribute.
+ ParseCXX11AttributeSpecifier(Attrs);
+ } else if (Tok.is(tok::kw___attribute)) {
+ ConsumeToken();
+ if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
+ "attribute"))
+ return SkipToEnd();
+ if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, "("))
+ return SkipToEnd();
+
+ // FIXME: The practical usefulness of completion here is limited because
+ // we only get here if the line has balanced parens.
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ // FIXME: suppress completion of unsupported attributes?
+ Actions.CodeCompleteAttribute(AttributeCommonInfo::Syntax::AS_GNU);
+ return SkipToEnd();
+ }
+
+ // Parse the comma-separated list of attributes.
+ do {
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_pragma_attribute_expected_attribute_name);
+ SkipToEnd();
+ return;
+ }
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::l_paren))
+ Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_GNU);
+ else
+ ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, /*EndLoc=*/nullptr,
+ /*ScopeName=*/nullptr,
+ /*ScopeLoc=*/SourceLocation(), ParsedAttr::AS_GNU,
+ /*Declarator=*/nullptr);
+ } while (TryConsumeToken(tok::comma));
+
+ if (ExpectAndConsume(tok::r_paren))
+ return SkipToEnd();
+ if (ExpectAndConsume(tok::r_paren))
+ return SkipToEnd();
+ } else if (Tok.is(tok::kw___declspec)) {
+ ParseMicrosoftDeclSpecs(Attrs);
+ } else {
+ Diag(Tok, diag::err_pragma_attribute_expected_attribute_syntax);
+ if (Tok.getIdentifierInfo()) {
+ // If we suspect that this is an attribute suggest the use of
+ // '__attribute__'.
+ if (ParsedAttr::getParsedKind(
+ Tok.getIdentifierInfo(), /*ScopeName=*/nullptr,
+ ParsedAttr::AS_GNU) != ParsedAttr::UnknownAttribute) {
+ SourceLocation InsertStartLoc = Tok.getLocation();
+ ConsumeToken();
+ if (Tok.is(tok::l_paren)) {
+ ConsumeAnyToken();
+ SkipUntil(tok::r_paren, StopBeforeMatch);
+ if (Tok.isNot(tok::r_paren))
+ return SkipToEnd();
+ }
+ Diag(Tok, diag::note_pragma_attribute_use_attribute_kw)
+ << FixItHint::CreateInsertion(InsertStartLoc, "__attribute__((")
+ << FixItHint::CreateInsertion(Tok.getEndLoc(), "))");
+ }
+ }
+ SkipToEnd();
+ return;
+ }
+
+ if (Attrs.empty() || Attrs.begin()->isInvalid()) {
+ SkipToEnd();
+ return;
+ }
+
+ for (const ParsedAttr &Attribute : Attrs) {
+ if (!Attribute.isSupportedByPragmaAttribute()) {
+ Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute)
+ << Attribute;
+ SkipToEnd();
+ return;
+ }
+ }
+
+ // Parse the subject-list.
+ if (!TryConsumeToken(tok::comma)) {
+ createExpectedAttributeSubjectRulesTokenDiagnostic(
+ diag::err_expected, Attrs,
+ MissingAttributeSubjectRulesRecoveryPoint::Comma, *this)
+ << tok::comma;
+ SkipToEnd();
+ return;
+ }
+
+ if (Tok.isNot(tok::identifier)) {
+ createExpectedAttributeSubjectRulesTokenDiagnostic(
+ diag::err_pragma_attribute_invalid_subject_set_specifier, Attrs,
+ MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
+ SkipToEnd();
+ return;
+ }
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (!II->isStr("apply_to")) {
+ createExpectedAttributeSubjectRulesTokenDiagnostic(
+ diag::err_pragma_attribute_invalid_subject_set_specifier, Attrs,
+ MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
+ SkipToEnd();
+ return;
+ }
+ ConsumeToken();
+
+ if (!TryConsumeToken(tok::equal)) {
+ createExpectedAttributeSubjectRulesTokenDiagnostic(
+ diag::err_expected, Attrs,
+ MissingAttributeSubjectRulesRecoveryPoint::Equals, *this)
+ << tok::equal;
+ SkipToEnd();
+ return;
+ }
+
+ attr::ParsedSubjectMatchRuleSet SubjectMatchRules;
+ SourceLocation AnyLoc, LastMatchRuleEndLoc;
+ if (ParsePragmaAttributeSubjectMatchRuleSet(SubjectMatchRules, AnyLoc,
+ LastMatchRuleEndLoc)) {
+ SkipToEnd();
+ return;
+ }
+
+ // Tokens following an ill-formed attribute will remain in the token stream
+ // and must be removed.
+ if (Tok.isNot(tok::eof)) {
+ Diag(Tok, diag::err_pragma_attribute_extra_tokens_after_attribute);
+ SkipToEnd();
+ return;
+ }
+
+ // Consume the eof terminator token.
+ ConsumeToken();
+
+ // Handle a mixed push/attribute by desurging to a push, then an attribute.
+ if (Info->Action == PragmaAttributeInfo::Push)
+ Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc, Info->Namespace);
+
+ for (ParsedAttr &Attribute : Attrs) {
+ Actions.ActOnPragmaAttributeAttribute(Attribute, PragmaLoc,
+ SubjectMatchRules);
+ }
+}
+
+// #pragma GCC visibility comes in two variants:
+// 'push' '(' [visibility] ')'
+// 'pop'
+void PragmaGCCVisibilityHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &VisTok) {
+ SourceLocation VisLoc = VisTok.getLocation();
+
+ Token Tok;
+ PP.LexUnexpandedToken(Tok);
+
+ const IdentifierInfo *PushPop = Tok.getIdentifierInfo();
+
+ const IdentifierInfo *VisType;
+ if (PushPop && PushPop->isStr("pop")) {
+ VisType = nullptr;
+ } else if (PushPop && PushPop->isStr("push")) {
+ PP.LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
+ << "visibility";
+ return;
+ }
+ PP.LexUnexpandedToken(Tok);
+ VisType = Tok.getIdentifierInfo();
+ if (!VisType) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << "visibility";
+ return;
+ }
+ PP.LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
+ << "visibility";
+ return;
+ }
+ } else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << "visibility";
+ return;
+ }
+ SourceLocation EndLoc = Tok.getLocation();
+ PP.LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "visibility";
+ return;
+ }
+
+ auto Toks = std::make_unique<Token[]>(1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_vis);
+ Toks[0].setLocation(VisLoc);
+ Toks[0].setAnnotationEndLoc(EndLoc);
+ Toks[0].setAnnotationValue(
+ const_cast<void *>(static_cast<const void *>(VisType)));
+ PP.EnterTokenStream(std::move(Toks), 1, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+}
+
+// #pragma pack(...) comes in the following delicious flavors:
+// pack '(' [integer] ')'
+// pack '(' 'show' ')'
+// pack '(' ('push' | 'pop') [',' identifier] [, integer] ')'
+void PragmaPackHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &PackTok) {
+ SourceLocation PackLoc = PackTok.getLocation();
+
+ Token Tok;
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) << "pack";
+ return;
+ }
+
+ Sema::PragmaMsStackAction Action = Sema::PSK_Reset;
+ StringRef SlotLabel;
+ Token Alignment;
+ Alignment.startToken();
+ PP.Lex(Tok);
+ if (Tok.is(tok::numeric_constant)) {
+ Alignment = Tok;
+
+ PP.Lex(Tok);
+
+ // In MSVC/gcc, #pragma pack(4) sets the alignment without affecting
+ // the push/pop stack.
+ // In Apple gcc/XL, #pragma pack(4) is equivalent to #pragma pack(push, 4)
+ Action = (PP.getLangOpts().ApplePragmaPack || PP.getLangOpts().XLPragmaPack)
+ ? Sema::PSK_Push_Set
+ : Sema::PSK_Set;
+ } else if (Tok.is(tok::identifier)) {
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II->isStr("show")) {
+ Action = Sema::PSK_Show;
+ PP.Lex(Tok);
+ } else {
+ if (II->isStr("push")) {
+ Action = Sema::PSK_Push;
+ } else if (II->isStr("pop")) {
+ Action = Sema::PSK_Pop;
+ } else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_action) << "pack";
+ return;
+ }
+ PP.Lex(Tok);
+
+ if (Tok.is(tok::comma)) {
+ PP.Lex(Tok);
+
+ if (Tok.is(tok::numeric_constant)) {
+ Action = (Sema::PragmaMsStackAction)(Action | Sema::PSK_Set);
+ Alignment = Tok;
+
+ PP.Lex(Tok);
+ } else if (Tok.is(tok::identifier)) {
+ SlotLabel = Tok.getIdentifierInfo()->getName();
+ PP.Lex(Tok);
+
+ if (Tok.is(tok::comma)) {
+ PP.Lex(Tok);
+
+ if (Tok.isNot(tok::numeric_constant)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_pack_malformed);
+ return;
+ }
+
+ Action = (Sema::PragmaMsStackAction)(Action | Sema::PSK_Set);
+ Alignment = Tok;
+
+ PP.Lex(Tok);
+ }
+ } else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_pack_malformed);
+ return;
+ }
+ }
+ }
+ } else if (PP.getLangOpts().ApplePragmaPack ||
+ PP.getLangOpts().XLPragmaPack) {
+ // In MSVC/gcc, #pragma pack() resets the alignment without affecting
+ // the push/pop stack.
+ // In Apple gcc and IBM XL, #pragma pack() is equivalent to #pragma
+ // pack(pop).
+ Action = Sema::PSK_Pop;
+ }
+
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) << "pack";
+ return;
+ }
+
+ SourceLocation RParenLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) << "pack";
+ return;
+ }
+
+ PragmaPackInfo *Info =
+ PP.getPreprocessorAllocator().Allocate<PragmaPackInfo>(1);
+ Info->Action = Action;
+ Info->SlotLabel = SlotLabel;
+ Info->Alignment = Alignment;
+
+ MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
+ 1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_pack);
+ Toks[0].setLocation(PackLoc);
+ Toks[0].setAnnotationEndLoc(RParenLoc);
+ Toks[0].setAnnotationValue(static_cast<void*>(Info));
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+}
+
+// #pragma ms_struct on
+// #pragma ms_struct off
+void PragmaMSStructHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &MSStructTok) {
+ PragmaMSStructKind Kind = PMSST_OFF;
+
+ Token Tok;
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_ms_struct);
+ return;
+ }
+ SourceLocation EndLoc = Tok.getLocation();
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II->isStr("on")) {
+ Kind = PMSST_ON;
+ PP.Lex(Tok);
+ }
+ else if (II->isStr("off") || II->isStr("reset"))
+ PP.Lex(Tok);
+ else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_ms_struct);
+ return;
+ }
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "ms_struct";
+ return;
+ }
+
+ MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
+ 1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_msstruct);
+ Toks[0].setLocation(MSStructTok.getLocation());
+ Toks[0].setAnnotationEndLoc(EndLoc);
+ Toks[0].setAnnotationValue(reinterpret_cast<void*>(
+ static_cast<uintptr_t>(Kind)));
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+}
+
+// #pragma clang section bss="abc" data="" rodata="def" text="" relro=""
+void PragmaClangSectionHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &FirstToken) {
+
+ Token Tok;
+ auto SecKind = Sema::PragmaClangSectionKind::PCSK_Invalid;
+
+ PP.Lex(Tok); // eat 'section'
+ while (Tok.isNot(tok::eod)) {
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_expected_clang_section_name) << "clang section";
+ return;
+ }
+
+ const IdentifierInfo *SecType = Tok.getIdentifierInfo();
+ if (SecType->isStr("bss"))
+ SecKind = Sema::PragmaClangSectionKind::PCSK_BSS;
+ else if (SecType->isStr("data"))
+ SecKind = Sema::PragmaClangSectionKind::PCSK_Data;
+ else if (SecType->isStr("rodata"))
+ SecKind = Sema::PragmaClangSectionKind::PCSK_Rodata;
+ else if (SecType->isStr("relro"))
+ SecKind = Sema::PragmaClangSectionKind::PCSK_Relro;
+ else if (SecType->isStr("text"))
+ SecKind = Sema::PragmaClangSectionKind::PCSK_Text;
+ else {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_expected_clang_section_name) << "clang section";
+ return;
+ }
+
+ SourceLocation PragmaLocation = Tok.getLocation();
+ PP.Lex(Tok); // eat ['bss'|'data'|'rodata'|'text']
+ if (Tok.isNot(tok::equal)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_clang_section_expected_equal) << SecKind;
+ return;
+ }
+
+ std::string SecName;
+ if (!PP.LexStringLiteral(Tok, SecName, "pragma clang section", false))
+ return;
+
+ Actions.ActOnPragmaClangSection(
+ PragmaLocation,
+ (SecName.size() ? Sema::PragmaClangSectionAction::PCSA_Set
+ : Sema::PragmaClangSectionAction::PCSA_Clear),
+ SecKind, SecName);
+ }
+}
+
+// #pragma 'align' '=' {'native','natural','mac68k','power','reset'}
+// #pragma 'options 'align' '=' {'native','natural','mac68k','power','reset'}
+// #pragma 'align' '(' {'native','natural','mac68k','power','reset'} ')'
+static void ParseAlignPragma(Preprocessor &PP, Token &FirstTok,
+ bool IsOptions) {
+ Token Tok;
+
+ if (IsOptions) {
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier) ||
+ !Tok.getIdentifierInfo()->isStr("align")) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_options_expected_align);
+ return;
+ }
+ }
+
+ PP.Lex(Tok);
+ if (PP.getLangOpts().XLPragmaPack) {
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) << "align";
+ return;
+ }
+ } else if (Tok.isNot(tok::equal)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_align_expected_equal)
+ << IsOptions;
+ return;
+ }
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << (IsOptions ? "options" : "align");
+ return;
+ }
+
+ Sema::PragmaOptionsAlignKind Kind = Sema::POAK_Natural;
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II->isStr("native"))
+ Kind = Sema::POAK_Native;
+ else if (II->isStr("natural"))
+ Kind = Sema::POAK_Natural;
+ else if (II->isStr("packed"))
+ Kind = Sema::POAK_Packed;
+ else if (II->isStr("power"))
+ Kind = Sema::POAK_Power;
+ else if (II->isStr("mac68k"))
+ Kind = Sema::POAK_Mac68k;
+ else if (II->isStr("reset"))
+ Kind = Sema::POAK_Reset;
+ else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_align_invalid_option)
+ << IsOptions;
+ return;
+ }
+
+ if (PP.getLangOpts().XLPragmaPack) {
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) << "align";
+ return;
+ }
+ }
+
+ SourceLocation EndLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << (IsOptions ? "options" : "align");
+ return;
+ }
+
+ MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
+ 1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_align);
+ Toks[0].setLocation(FirstTok.getLocation());
+ Toks[0].setAnnotationEndLoc(EndLoc);
+ Toks[0].setAnnotationValue(reinterpret_cast<void*>(
+ static_cast<uintptr_t>(Kind)));
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+}
+
+void PragmaAlignHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &AlignTok) {
+ ParseAlignPragma(PP, AlignTok, /*IsOptions=*/false);
+}
+
+void PragmaOptionsHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &OptionsTok) {
+ ParseAlignPragma(PP, OptionsTok, /*IsOptions=*/true);
+}
+
+// #pragma unused(identifier)
+void PragmaUnusedHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &UnusedTok) {
+ // FIXME: Should we be expanding macros here? My guess is no.
+ SourceLocation UnusedLoc = UnusedTok.getLocation();
+
+ // Lex the left '('.
+ Token Tok;
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) << "unused";
+ return;
+ }
+
+ // Lex the declaration reference(s).
+ SmallVector<Token, 5> Identifiers;
+ SourceLocation RParenLoc;
+ bool LexID = true;
+
+ while (true) {
+ PP.Lex(Tok);
+
+ if (LexID) {
+ if (Tok.is(tok::identifier)) {
+ Identifiers.push_back(Tok);
+ LexID = false;
+ continue;
+ }
+
+ // Illegal token!
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_unused_expected_var);
+ return;
+ }
+
+ // We are execting a ')' or a ','.
+ if (Tok.is(tok::comma)) {
+ LexID = true;
+ continue;
+ }
+
+ if (Tok.is(tok::r_paren)) {
+ RParenLoc = Tok.getLocation();
+ break;
+ }
+
+ // Illegal token!
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_punc) << "unused";
+ return;
+ }
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) <<
+ "unused";
+ return;
+ }
+
+ // Verify that we have a location for the right parenthesis.
+ assert(RParenLoc.isValid() && "Valid '#pragma unused' must have ')'");
+ assert(!Identifiers.empty() && "Valid '#pragma unused' must have arguments");
+
+ // For each identifier token, insert into the token stream a
+ // annot_pragma_unused token followed by the identifier token.
+ // This allows us to cache a "#pragma unused" that occurs inside an inline
+ // C++ member function.
+
+ MutableArrayRef<Token> Toks(
+ PP.getPreprocessorAllocator().Allocate<Token>(2 * Identifiers.size()),
+ 2 * Identifiers.size());
+ for (unsigned i=0; i != Identifiers.size(); i++) {
+ Token &pragmaUnusedTok = Toks[2*i], &idTok = Toks[2*i+1];
+ pragmaUnusedTok.startToken();
+ pragmaUnusedTok.setKind(tok::annot_pragma_unused);
+ pragmaUnusedTok.setLocation(UnusedLoc);
+ idTok = Identifiers[i];
+ }
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+}
+
+// #pragma weak identifier
+// #pragma weak identifier '=' identifier
+void PragmaWeakHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &WeakTok) {
+ SourceLocation WeakLoc = WeakTok.getLocation();
+
+ Token Tok;
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) << "weak";
+ return;
+ }
+
+ Token WeakName = Tok;
+ bool HasAlias = false;
+ Token AliasName;
+
+ PP.Lex(Tok);
+ if (Tok.is(tok::equal)) {
+ HasAlias = true;
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << "weak";
+ return;
+ }
+ AliasName = Tok;
+ PP.Lex(Tok);
+ }
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) << "weak";
+ return;
+ }
+
+ if (HasAlias) {
+ MutableArrayRef<Token> Toks(
+ PP.getPreprocessorAllocator().Allocate<Token>(3), 3);
+ Token &pragmaUnusedTok = Toks[0];
+ pragmaUnusedTok.startToken();
+ pragmaUnusedTok.setKind(tok::annot_pragma_weakalias);
+ pragmaUnusedTok.setLocation(WeakLoc);
+ pragmaUnusedTok.setAnnotationEndLoc(AliasName.getLocation());
+ Toks[1] = WeakName;
+ Toks[2] = AliasName;
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+ } else {
+ MutableArrayRef<Token> Toks(
+ PP.getPreprocessorAllocator().Allocate<Token>(2), 2);
+ Token &pragmaUnusedTok = Toks[0];
+ pragmaUnusedTok.startToken();
+ pragmaUnusedTok.setKind(tok::annot_pragma_weak);
+ pragmaUnusedTok.setLocation(WeakLoc);
+ pragmaUnusedTok.setAnnotationEndLoc(WeakLoc);
+ Toks[1] = WeakName;
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+ }
+}
+
+// #pragma redefine_extname identifier identifier
+void PragmaRedefineExtnameHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &RedefToken) {
+ SourceLocation RedefLoc = RedefToken.getLocation();
+
+ Token Tok;
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) <<
+ "redefine_extname";
+ return;
+ }
+
+ Token RedefName = Tok;
+ PP.Lex(Tok);
+
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << "redefine_extname";
+ return;
+ }
+
+ Token AliasName = Tok;
+ PP.Lex(Tok);
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) <<
+ "redefine_extname";
+ return;
+ }
+
+ MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(3),
+ 3);
+ Token &pragmaRedefTok = Toks[0];
+ pragmaRedefTok.startToken();
+ pragmaRedefTok.setKind(tok::annot_pragma_redefine_extname);
+ pragmaRedefTok.setLocation(RedefLoc);
+ pragmaRedefTok.setAnnotationEndLoc(AliasName.getLocation());
+ Toks[1] = RedefName;
+ Toks[2] = AliasName;
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+}
+
+void PragmaFPContractHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ tok::OnOffSwitch OOS;
+ if (PP.LexOnOffSwitch(OOS))
+ return;
+
+ MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
+ 1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_fp_contract);
+ Toks[0].setLocation(Tok.getLocation());
+ Toks[0].setAnnotationEndLoc(Tok.getLocation());
+ Toks[0].setAnnotationValue(reinterpret_cast<void*>(
+ static_cast<uintptr_t>(OOS)));
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+}
+
+void PragmaOpenCLExtensionHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ PP.LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) <<
+ "OPENCL";
+ return;
+ }
+ IdentifierInfo *Ext = Tok.getIdentifierInfo();
+ SourceLocation NameLoc = Tok.getLocation();
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::colon)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_colon) << Ext;
+ return;
+ }
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_predicate) << 0;
+ return;
+ }
+ IdentifierInfo *Pred = Tok.getIdentifierInfo();
+
+ OpenCLExtState State;
+ if (Pred->isStr("enable")) {
+ State = Enable;
+ } else if (Pred->isStr("disable")) {
+ State = Disable;
+ } else if (Pred->isStr("begin"))
+ State = Begin;
+ else if (Pred->isStr("end"))
+ State = End;
+ else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_predicate)
+ << Ext->isStr("all");
+ return;
+ }
+ SourceLocation StateLoc = Tok.getLocation();
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) <<
+ "OPENCL EXTENSION";
+ return;
+ }
+
+ auto Info = PP.getPreprocessorAllocator().Allocate<OpenCLExtData>(1);
+ Info->first = Ext;
+ Info->second = State;
+ MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
+ 1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_opencl_extension);
+ Toks[0].setLocation(NameLoc);
+ Toks[0].setAnnotationValue(static_cast<void*>(Info));
+ Toks[0].setAnnotationEndLoc(StateLoc);
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+
+ if (PP.getPPCallbacks())
+ PP.getPPCallbacks()->PragmaOpenCLExtension(NameLoc, Ext,
+ StateLoc, State);
+}
+
+/// Handle '#pragma omp ...' when OpenMP is disabled.
+///
+void PragmaNoOpenMPHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &FirstTok) {
+ if (!PP.getDiagnostics().isIgnored(diag::warn_pragma_omp_ignored,
+ FirstTok.getLocation())) {
+ PP.Diag(FirstTok, diag::warn_pragma_omp_ignored);
+ PP.getDiagnostics().setSeverity(diag::warn_pragma_omp_ignored,
+ diag::Severity::Ignored, SourceLocation());
+ }
+ PP.DiscardUntilEndOfDirective();
+}
+
+/// Handle '#pragma omp ...' when OpenMP is enabled.
+///
+void PragmaOpenMPHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &FirstTok) {
+ SmallVector<Token, 16> Pragma;
+ Token Tok;
+ Tok.startToken();
+ Tok.setKind(tok::annot_pragma_openmp);
+ Tok.setLocation(Introducer.Loc);
+
+ while (Tok.isNot(tok::eod) && Tok.isNot(tok::eof)) {
+ Pragma.push_back(Tok);
+ PP.Lex(Tok);
+ if (Tok.is(tok::annot_pragma_openmp)) {
+ PP.Diag(Tok, diag::err_omp_unexpected_directive) << 0;
+ unsigned InnerPragmaCnt = 1;
+ while (InnerPragmaCnt != 0) {
+ PP.Lex(Tok);
+ if (Tok.is(tok::annot_pragma_openmp))
+ ++InnerPragmaCnt;
+ else if (Tok.is(tok::annot_pragma_openmp_end))
+ --InnerPragmaCnt;
+ }
+ PP.Lex(Tok);
+ }
+ }
+ SourceLocation EodLoc = Tok.getLocation();
+ Tok.startToken();
+ Tok.setKind(tok::annot_pragma_openmp_end);
+ Tok.setLocation(EodLoc);
+ Pragma.push_back(Tok);
+
+ auto Toks = std::make_unique<Token[]>(Pragma.size());
+ std::copy(Pragma.begin(), Pragma.end(), Toks.get());
+ PP.EnterTokenStream(std::move(Toks), Pragma.size(),
+ /*DisableMacroExpansion=*/false, /*IsReinject=*/false);
+}
+
+/// Handle '#pragma pointers_to_members'
+// The grammar for this pragma is as follows:
+//
+// <inheritance model> ::= ('single' | 'multiple' | 'virtual') '_inheritance'
+//
+// #pragma pointers_to_members '(' 'best_case' ')'
+// #pragma pointers_to_members '(' 'full_generality' [',' inheritance-model] ')'
+// #pragma pointers_to_members '(' inheritance-model ')'
+void PragmaMSPointersToMembers::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ SourceLocation PointersToMembersLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(PointersToMembersLoc, diag::warn_pragma_expected_lparen)
+ << "pointers_to_members";
+ return;
+ }
+ PP.Lex(Tok);
+ const IdentifierInfo *Arg = Tok.getIdentifierInfo();
+ if (!Arg) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << "pointers_to_members";
+ return;
+ }
+ PP.Lex(Tok);
+
+ LangOptions::PragmaMSPointersToMembersKind RepresentationMethod;
+ if (Arg->isStr("best_case")) {
+ RepresentationMethod = LangOptions::PPTMK_BestCase;
+ } else {
+ if (Arg->isStr("full_generality")) {
+ if (Tok.is(tok::comma)) {
+ PP.Lex(Tok);
+
+ Arg = Tok.getIdentifierInfo();
+ if (!Arg) {
+ PP.Diag(Tok.getLocation(),
+ diag::err_pragma_pointers_to_members_unknown_kind)
+ << Tok.getKind() << /*OnlyInheritanceModels*/ 0;
+ return;
+ }
+ PP.Lex(Tok);
+ } else if (Tok.is(tok::r_paren)) {
+ // #pragma pointers_to_members(full_generality) implicitly specifies
+ // virtual_inheritance.
+ Arg = nullptr;
+ RepresentationMethod = LangOptions::PPTMK_FullGeneralityVirtualInheritance;
+ } else {
+ PP.Diag(Tok.getLocation(), diag::err_expected_punc)
+ << "full_generality";
+ return;
+ }
+ }
+
+ if (Arg) {
+ if (Arg->isStr("single_inheritance")) {
+ RepresentationMethod =
+ LangOptions::PPTMK_FullGeneralitySingleInheritance;
+ } else if (Arg->isStr("multiple_inheritance")) {
+ RepresentationMethod =
+ LangOptions::PPTMK_FullGeneralityMultipleInheritance;
+ } else if (Arg->isStr("virtual_inheritance")) {
+ RepresentationMethod =
+ LangOptions::PPTMK_FullGeneralityVirtualInheritance;
+ } else {
+ PP.Diag(Tok.getLocation(),
+ diag::err_pragma_pointers_to_members_unknown_kind)
+ << Arg << /*HasPointerDeclaration*/ 1;
+ return;
+ }
+ }
+ }
+
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_expected_rparen_after)
+ << (Arg ? Arg->getName() : "full_generality");
+ return;
+ }
+
+ SourceLocation EndLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "pointers_to_members";
+ return;
+ }
+
+ Token AnnotTok;
+ AnnotTok.startToken();
+ AnnotTok.setKind(tok::annot_pragma_ms_pointers_to_members);
+ AnnotTok.setLocation(PointersToMembersLoc);
+ AnnotTok.setAnnotationEndLoc(EndLoc);
+ AnnotTok.setAnnotationValue(
+ reinterpret_cast<void *>(static_cast<uintptr_t>(RepresentationMethod)));
+ PP.EnterToken(AnnotTok, /*IsReinject=*/true);
+}
+
+/// Handle '#pragma vtordisp'
+// The grammar for this pragma is as follows:
+//
+// <vtordisp-mode> ::= ('off' | 'on' | '0' | '1' | '2' )
+//
+// #pragma vtordisp '(' ['push' ','] vtordisp-mode ')'
+// #pragma vtordisp '(' 'pop' ')'
+// #pragma vtordisp '(' ')'
+void PragmaMSVtorDisp::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer, Token &Tok) {
+ SourceLocation VtorDispLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(VtorDispLoc, diag::warn_pragma_expected_lparen) << "vtordisp";
+ return;
+ }
+ PP.Lex(Tok);
+
+ Sema::PragmaMsStackAction Action = Sema::PSK_Set;
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II) {
+ if (II->isStr("push")) {
+ // #pragma vtordisp(push, mode)
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::comma)) {
+ PP.Diag(VtorDispLoc, diag::warn_pragma_expected_punc) << "vtordisp";
+ return;
+ }
+ PP.Lex(Tok);
+ Action = Sema::PSK_Push_Set;
+ // not push, could be on/off
+ } else if (II->isStr("pop")) {
+ // #pragma vtordisp(pop)
+ PP.Lex(Tok);
+ Action = Sema::PSK_Pop;
+ }
+ // not push or pop, could be on/off
+ } else {
+ if (Tok.is(tok::r_paren)) {
+ // #pragma vtordisp()
+ Action = Sema::PSK_Reset;
+ }
+ }
+
+
+ uint64_t Value = 0;
+ if (Action & Sema::PSK_Push || Action & Sema::PSK_Set) {
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II && II->isStr("off")) {
+ PP.Lex(Tok);
+ Value = 0;
+ } else if (II && II->isStr("on")) {
+ PP.Lex(Tok);
+ Value = 1;
+ } else if (Tok.is(tok::numeric_constant) &&
+ PP.parseSimpleIntegerLiteral(Tok, Value)) {
+ if (Value > 2) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_integer)
+ << 0 << 2 << "vtordisp";
+ return;
+ }
+ } else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_action)
+ << "vtordisp";
+ return;
+ }
+ }
+
+ // Finish the pragma: ')' $
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(VtorDispLoc, diag::warn_pragma_expected_rparen) << "vtordisp";
+ return;
+ }
+ SourceLocation EndLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "vtordisp";
+ return;
+ }
+
+ // Enter the annotation.
+ Token AnnotTok;
+ AnnotTok.startToken();
+ AnnotTok.setKind(tok::annot_pragma_ms_vtordisp);
+ AnnotTok.setLocation(VtorDispLoc);
+ AnnotTok.setAnnotationEndLoc(EndLoc);
+ AnnotTok.setAnnotationValue(reinterpret_cast<void *>(
+ static_cast<uintptr_t>((Action << 16) | (Value & 0xFFFF))));
+ PP.EnterToken(AnnotTok, /*IsReinject=*/false);
+}
+
+/// Handle all MS pragmas. Simply forwards the tokens after inserting
+/// an annotation token.
+void PragmaMSPragma::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer, Token &Tok) {
+ Token EoF, AnnotTok;
+ EoF.startToken();
+ EoF.setKind(tok::eof);
+ AnnotTok.startToken();
+ AnnotTok.setKind(tok::annot_pragma_ms_pragma);
+ AnnotTok.setLocation(Tok.getLocation());
+ AnnotTok.setAnnotationEndLoc(Tok.getLocation());
+ SmallVector<Token, 8> TokenVector;
+ // Suck up all of the tokens before the eod.
+ for (; Tok.isNot(tok::eod); PP.Lex(Tok)) {
+ TokenVector.push_back(Tok);
+ AnnotTok.setAnnotationEndLoc(Tok.getLocation());
+ }
+ // Add a sentinel EoF token to the end of the list.
+ TokenVector.push_back(EoF);
+ // We must allocate this array with new because EnterTokenStream is going to
+ // delete it later.
+ markAsReinjectedForRelexing(TokenVector);
+ auto TokenArray = std::make_unique<Token[]>(TokenVector.size());
+ std::copy(TokenVector.begin(), TokenVector.end(), TokenArray.get());
+ auto Value = new (PP.getPreprocessorAllocator())
+ std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray),
+ TokenVector.size());
+ AnnotTok.setAnnotationValue(Value);
+ PP.EnterToken(AnnotTok, /*IsReinject*/ false);
+}
+
+/// Handle the \#pragma float_control extension.
+///
+/// The syntax is:
+/// \code
+/// #pragma float_control(keyword[, setting] [,push])
+/// \endcode
+/// Where 'keyword' and 'setting' are identifiers.
+// 'keyword' can be: precise, except, push, pop
+// 'setting' can be: on, off
+/// The optional arguments 'setting' and 'push' are supported only
+/// when the keyword is 'precise' or 'except'.
+void PragmaFloatControlHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ Sema::PragmaMsStackAction Action = Sema::PSK_Set;
+ SourceLocation FloatControlLoc = Tok.getLocation();
+ Token PragmaName = Tok;
+ if (!PP.getTargetInfo().hasStrictFP() && !PP.getLangOpts().ExpStrictFP) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_fp_ignored)
+ << PragmaName.getIdentifierInfo()->getName();
+ return;
+ }
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(FloatControlLoc, diag::err_expected) << tok::l_paren;
+ return;
+ }
+
+ // Read the identifier.
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ }
+
+ // Verify that this is one of the float control options.
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ PragmaFloatControlKind Kind =
+ llvm::StringSwitch<PragmaFloatControlKind>(II->getName())
+ .Case("precise", PFC_Precise)
+ .Case("except", PFC_Except)
+ .Case("push", PFC_Push)
+ .Case("pop", PFC_Pop)
+ .Default(PFC_Unknown);
+ PP.Lex(Tok); // the identifier
+ if (Kind == PFC_Unknown) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ } else if (Kind == PFC_Push || Kind == PFC_Pop) {
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ }
+ PP.Lex(Tok); // Eat the r_paren
+ Action = (Kind == PFC_Pop) ? Sema::PSK_Pop : Sema::PSK_Push;
+ } else {
+ if (Tok.is(tok::r_paren))
+ // Selecting Precise or Except
+ PP.Lex(Tok); // the r_paren
+ else if (Tok.isNot(tok::comma)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ } else {
+ PP.Lex(Tok); // ,
+ if (!Tok.isAnyIdentifier()) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ }
+ StringRef PushOnOff = Tok.getIdentifierInfo()->getName();
+ if (PushOnOff == "on")
+ // Kind is set correctly
+ ;
+ else if (PushOnOff == "off") {
+ if (Kind == PFC_Precise)
+ Kind = PFC_NoPrecise;
+ if (Kind == PFC_Except)
+ Kind = PFC_NoExcept;
+ } else if (PushOnOff == "push") {
+ Action = Sema::PSK_Push_Set;
+ } else {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ }
+ PP.Lex(Tok); // the identifier
+ if (Tok.is(tok::comma)) {
+ PP.Lex(Tok); // ,
+ if (!Tok.isAnyIdentifier()) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ }
+ StringRef ExpectedPush = Tok.getIdentifierInfo()->getName();
+ if (ExpectedPush == "push") {
+ Action = Sema::PSK_Push_Set;
+ } else {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ }
+ PP.Lex(Tok); // the push identifier
+ }
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+ return;
+ }
+ PP.Lex(Tok); // the r_paren
+ }
+ }
+ SourceLocation EndLoc = Tok.getLocation();
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "float_control";
+ return;
+ }
+
+ // Note: there is no accomodation for PP callback for this pragma.
+
+ // Enter the annotation.
+ auto TokenArray = std::make_unique<Token[]>(1);
+ TokenArray[0].startToken();
+ TokenArray[0].setKind(tok::annot_pragma_float_control);
+ TokenArray[0].setLocation(FloatControlLoc);
+ TokenArray[0].setAnnotationEndLoc(EndLoc);
+ // Create an encoding of Action and Value by shifting the Action into
+ // the high 16 bits then union with the Kind.
+ TokenArray[0].setAnnotationValue(reinterpret_cast<void *>(
+ static_cast<uintptr_t>((Action << 16) | (Kind & 0xFFFF))));
+ PP.EnterTokenStream(std::move(TokenArray), 1,
+ /*DisableMacroExpansion=*/false, /*IsReinject=*/false);
+}
+
+/// Handle the Microsoft \#pragma detect_mismatch extension.
+///
+/// The syntax is:
+/// \code
+/// #pragma detect_mismatch("name", "value")
+/// \endcode
+/// Where 'name' and 'value' are quoted strings. The values are embedded in
+/// the object file and passed along to the linker. If the linker detects a
+/// mismatch in the object file's values for the given name, a LNK2038 error
+/// is emitted. See MSDN for more details.
+void PragmaDetectMismatchHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ SourceLocation DetectMismatchLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(DetectMismatchLoc, diag::err_expected) << tok::l_paren;
+ return;
+ }
+
+ // Read the name to embed, which must be a string literal.
+ std::string NameString;
+ if (!PP.LexStringLiteral(Tok, NameString,
+ "pragma detect_mismatch",
+ /*AllowMacroExpansion=*/true))
+ return;
+
+ // Read the comma followed by a second string literal.
+ std::string ValueString;
+ if (Tok.isNot(tok::comma)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_detect_mismatch_malformed);
+ return;
+ }
+
+ if (!PP.LexStringLiteral(Tok, ValueString, "pragma detect_mismatch",
+ /*AllowMacroExpansion=*/true))
+ return;
+
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren;
+ return;
+ }
+ PP.Lex(Tok); // Eat the r_paren.
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_detect_mismatch_malformed);
+ return;
+ }
+
+ // If the pragma is lexically sound, notify any interested PPCallbacks.
+ if (PP.getPPCallbacks())
+ PP.getPPCallbacks()->PragmaDetectMismatch(DetectMismatchLoc, NameString,
+ ValueString);
+
+ Actions.ActOnPragmaDetectMismatch(DetectMismatchLoc, NameString, ValueString);
+}
+
+/// Handle the microsoft \#pragma comment extension.
+///
+/// The syntax is:
+/// \code
+/// #pragma comment(linker, "foo")
+/// \endcode
+/// 'linker' is one of five identifiers: compiler, exestr, lib, linker, user.
+/// "foo" is a string, which is fully macro expanded, and permits string
+/// concatenation, embedded escape characters etc. See MSDN for more details.
+void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ SourceLocation CommentLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(CommentLoc, diag::err_pragma_comment_malformed);
+ return;
+ }
+
+ // Read the identifier.
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(CommentLoc, diag::err_pragma_comment_malformed);
+ return;
+ }
+
+ // Verify that this is one of the 5 explicitly listed options.
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ PragmaMSCommentKind Kind =
+ llvm::StringSwitch<PragmaMSCommentKind>(II->getName())
+ .Case("linker", PCK_Linker)
+ .Case("lib", PCK_Lib)
+ .Case("compiler", PCK_Compiler)
+ .Case("exestr", PCK_ExeStr)
+ .Case("user", PCK_User)
+ .Default(PCK_Unknown);
+ if (Kind == PCK_Unknown) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind);
+ return;
+ }
+
+ if (PP.getTargetInfo().getTriple().isOSBinFormatELF() && Kind != PCK_Lib) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_ignored)
+ << II->getName();
+ return;
+ }
+
+ // Read the optional string if present.
+ PP.Lex(Tok);
+ std::string ArgumentString;
+ if (Tok.is(tok::comma) && !PP.LexStringLiteral(Tok, ArgumentString,
+ "pragma comment",
+ /*AllowMacroExpansion=*/true))
+ return;
+
+ // FIXME: warn that 'exestr' is deprecated.
+ // FIXME: If the kind is "compiler" warn if the string is present (it is
+ // ignored).
+ // The MSDN docs say that "lib" and "linker" require a string and have a short
+ // list of linker options they support, but in practice MSVC doesn't
+ // issue a diagnostic. Therefore neither does clang.
+
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_comment_malformed);
+ return;
+ }
+ PP.Lex(Tok); // eat the r_paren.
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_comment_malformed);
+ return;
+ }
+
+ // If the pragma is lexically sound, notify any interested PPCallbacks.
+ if (PP.getPPCallbacks())
+ PP.getPPCallbacks()->PragmaComment(CommentLoc, II, ArgumentString);
+
+ Actions.ActOnPragmaMSComment(CommentLoc, Kind, ArgumentString);
+}
+
+// #pragma clang optimize off
+// #pragma clang optimize on
+void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &FirstToken) {
+ Token Tok;
+ PP.Lex(Tok);
+ if (Tok.is(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument)
+ << "clang optimize" << /*Expected=*/true << "'on' or 'off'";
+ return;
+ }
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_invalid_argument)
+ << PP.getSpelling(Tok);
+ return;
+ }
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ // The only accepted values are 'on' or 'off'.
+ bool IsOn = false;
+ if (II->isStr("on")) {
+ IsOn = true;
+ } else if (!II->isStr("off")) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_invalid_argument)
+ << PP.getSpelling(Tok);
+ return;
+ }
+ PP.Lex(Tok);
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_extra_argument)
+ << PP.getSpelling(Tok);
+ return;
+ }
+
+ Actions.ActOnPragmaOptimize(IsOn, FirstToken.getLocation());
+}
+
+namespace {
+/// Used as the annotation value for tok::annot_pragma_fp.
+struct TokFPAnnotValue {
+ enum FlagKinds { Contract, Reassociate, Exceptions, EvalMethod };
+ enum FlagValues { On, Off, Fast };
+
+ std::optional<LangOptions::FPModeKind> ContractValue;
+ std::optional<LangOptions::FPModeKind> ReassociateValue;
+ std::optional<LangOptions::FPExceptionModeKind> ExceptionsValue;
+ std::optional<LangOptions::FPEvalMethodKind> EvalMethodValue;
+};
+} // end anonymous namespace
+
+void PragmaFPHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer, Token &Tok) {
+ // fp
+ Token PragmaName = Tok;
+ SmallVector<Token, 1> TokenList;
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option)
+ << /*MissingOption=*/true << "";
+ return;
+ }
+
+ auto *AnnotValue = new (PP.getPreprocessorAllocator()) TokFPAnnotValue;
+ while (Tok.is(tok::identifier)) {
+ IdentifierInfo *OptionInfo = Tok.getIdentifierInfo();
+
+ auto FlagKind =
+ llvm::StringSwitch<std::optional<TokFPAnnotValue::FlagKinds>>(
+ OptionInfo->getName())
+ .Case("contract", TokFPAnnotValue::Contract)
+ .Case("reassociate", TokFPAnnotValue::Reassociate)
+ .Case("exceptions", TokFPAnnotValue::Exceptions)
+ .Case("eval_method", TokFPAnnotValue::EvalMethod)
+ .Default(std::nullopt);
+ if (!FlagKind) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option)
+ << /*MissingOption=*/false << OptionInfo;
+ return;
+ }
+ PP.Lex(Tok);
+
+ // Read '('
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
+ return;
+ }
+ PP.Lex(Tok);
+ bool isEvalMethodDouble =
+ Tok.is(tok::kw_double) && FlagKind == TokFPAnnotValue::EvalMethod;
+
+ // Don't diagnose if we have an eval_metod pragma with "double" kind.
+ if (Tok.isNot(tok::identifier) && !isEvalMethodDouble) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
+ << PP.getSpelling(Tok) << OptionInfo->getName()
+ << static_cast<int>(*FlagKind);
+ return;
+ }
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+
+ if (FlagKind == TokFPAnnotValue::Contract) {
+ AnnotValue->ContractValue =
+ llvm::StringSwitch<std::optional<LangOptions::FPModeKind>>(
+ II->getName())
+ .Case("on", LangOptions::FPModeKind::FPM_On)
+ .Case("off", LangOptions::FPModeKind::FPM_Off)
+ .Case("fast", LangOptions::FPModeKind::FPM_Fast)
+ .Default(std::nullopt);
+ if (!AnnotValue->ContractValue) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
+ << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind;
+ return;
+ }
+ } else if (FlagKind == TokFPAnnotValue::Reassociate) {
+ AnnotValue->ReassociateValue =
+ llvm::StringSwitch<std::optional<LangOptions::FPModeKind>>(
+ II->getName())
+ .Case("on", LangOptions::FPModeKind::FPM_On)
+ .Case("off", LangOptions::FPModeKind::FPM_Off)
+ .Default(std::nullopt);
+ if (!AnnotValue->ReassociateValue) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
+ << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind;
+ return;
+ }
+ } else if (FlagKind == TokFPAnnotValue::Exceptions) {
+ AnnotValue->ExceptionsValue =
+ llvm::StringSwitch<std::optional<LangOptions::FPExceptionModeKind>>(
+ II->getName())
+ .Case("ignore", LangOptions::FPE_Ignore)
+ .Case("maytrap", LangOptions::FPE_MayTrap)
+ .Case("strict", LangOptions::FPE_Strict)
+ .Default(std::nullopt);
+ if (!AnnotValue->ExceptionsValue) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
+ << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind;
+ return;
+ }
+ } else if (FlagKind == TokFPAnnotValue::EvalMethod) {
+ AnnotValue->EvalMethodValue =
+ llvm::StringSwitch<std::optional<LangOptions::FPEvalMethodKind>>(
+ II->getName())
+ .Case("source", LangOptions::FPEvalMethodKind::FEM_Source)
+ .Case("double", LangOptions::FPEvalMethodKind::FEM_Double)
+ .Case("extended", LangOptions::FPEvalMethodKind::FEM_Extended)
+ .Default(std::nullopt);
+ if (!AnnotValue->EvalMethodValue) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
+ << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind;
+ return;
+ }
+ }
+ PP.Lex(Tok);
+
+ // Read ')'
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren;
+ return;
+ }
+ PP.Lex(Tok);
+ }
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "clang fp";
+ return;
+ }
+
+ Token FPTok;
+ FPTok.startToken();
+ FPTok.setKind(tok::annot_pragma_fp);
+ FPTok.setLocation(PragmaName.getLocation());
+ FPTok.setAnnotationEndLoc(PragmaName.getLocation());
+ FPTok.setAnnotationValue(reinterpret_cast<void *>(AnnotValue));
+ TokenList.push_back(FPTok);
+
+ auto TokenArray = std::make_unique<Token[]>(TokenList.size());
+ std::copy(TokenList.begin(), TokenList.end(), TokenArray.get());
+
+ PP.EnterTokenStream(std::move(TokenArray), TokenList.size(),
+ /*DisableMacroExpansion=*/false, /*IsReinject=*/false);
+}
+
+void PragmaSTDC_FENV_ROUNDHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ Token PragmaName = Tok;
+ SmallVector<Token, 1> TokenList;
+ if (!PP.getTargetInfo().hasStrictFP() && !PP.getLangOpts().ExpStrictFP) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_fp_ignored)
+ << PragmaName.getIdentifierInfo()->getName();
+ return;
+ }
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << PragmaName.getIdentifierInfo()->getName();
+ return;
+ }
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+
+ auto RM =
+ llvm::StringSwitch<llvm::RoundingMode>(II->getName())
+ .Case("FE_TOWARDZERO", llvm::RoundingMode::TowardZero)
+ .Case("FE_TONEAREST", llvm::RoundingMode::NearestTiesToEven)
+ .Case("FE_UPWARD", llvm::RoundingMode::TowardPositive)
+ .Case("FE_DOWNWARD", llvm::RoundingMode::TowardNegative)
+ .Case("FE_TONEARESTFROMZERO", llvm::RoundingMode::NearestTiesToAway)
+ .Case("FE_DYNAMIC", llvm::RoundingMode::Dynamic)
+ .Default(llvm::RoundingMode::Invalid);
+ if (RM == llvm::RoundingMode::Invalid) {
+ PP.Diag(Tok.getLocation(), diag::warn_stdc_unknown_rounding_mode);
+ return;
+ }
+ PP.Lex(Tok);
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "STDC FENV_ROUND";
+ return;
+ }
+
+ // Until the pragma is fully implemented, issue a warning.
+ PP.Diag(Tok.getLocation(), diag::warn_stdc_fenv_round_not_supported);
+
+ MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
+ 1);
+ Toks[0].startToken();
+ Toks[0].setKind(tok::annot_pragma_fenv_round);
+ Toks[0].setLocation(Tok.getLocation());
+ Toks[0].setAnnotationEndLoc(Tok.getLocation());
+ Toks[0].setAnnotationValue(
+ reinterpret_cast<void *>(static_cast<uintptr_t>(RM)));
+ PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
+}
+
+void Parser::HandlePragmaFP() {
+ assert(Tok.is(tok::annot_pragma_fp));
+ auto *AnnotValue =
+ reinterpret_cast<TokFPAnnotValue *>(Tok.getAnnotationValue());
+
+ if (AnnotValue->ReassociateValue)
+ Actions.ActOnPragmaFPReassociate(Tok.getLocation(),
+ *AnnotValue->ReassociateValue ==
+ LangOptions::FPModeKind::FPM_On);
+ if (AnnotValue->ContractValue)
+ Actions.ActOnPragmaFPContract(Tok.getLocation(),
+ *AnnotValue->ContractValue);
+ if (AnnotValue->ExceptionsValue)
+ Actions.ActOnPragmaFPExceptions(Tok.getLocation(),
+ *AnnotValue->ExceptionsValue);
+ if (AnnotValue->EvalMethodValue)
+ Actions.ActOnPragmaFPEvalMethod(Tok.getLocation(),
+ *AnnotValue->EvalMethodValue);
+ ConsumeAnnotationToken();
+}
+
+/// Parses loop or unroll pragma hint value and fills in Info.
+static bool ParseLoopHintValue(Preprocessor &PP, Token &Tok, Token PragmaName,
+ Token Option, bool ValueInParens,
+ PragmaLoopHintInfo &Info) {
+ SmallVector<Token, 1> ValueList;
+ int OpenParens = ValueInParens ? 1 : 0;
+ // Read constant expression.
+ while (Tok.isNot(tok::eod)) {
+ if (Tok.is(tok::l_paren))
+ OpenParens++;
+ else if (Tok.is(tok::r_paren)) {
+ OpenParens--;
+ if (OpenParens == 0 && ValueInParens)
+ break;
+ }
+
+ ValueList.push_back(Tok);
+ PP.Lex(Tok);
+ }
+
+ if (ValueInParens) {
+ // Read ')'
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren;
+ return true;
+ }
+ PP.Lex(Tok);
+ }
+
+ Token EOFTok;
+ EOFTok.startToken();
+ EOFTok.setKind(tok::eof);
+ EOFTok.setLocation(Tok.getLocation());
+ ValueList.push_back(EOFTok); // Terminates expression for parsing.
+
+ markAsReinjectedForRelexing(ValueList);
+ Info.Toks = llvm::ArrayRef(ValueList).copy(PP.getPreprocessorAllocator());
+
+ Info.PragmaName = PragmaName;
+ Info.Option = Option;
+ return false;
+}
+
+/// Handle the \#pragma clang loop directive.
+/// #pragma clang 'loop' loop-hints
+///
+/// loop-hints:
+/// loop-hint loop-hints[opt]
+///
+/// loop-hint:
+/// 'vectorize' '(' loop-hint-keyword ')'
+/// 'interleave' '(' loop-hint-keyword ')'
+/// 'unroll' '(' unroll-hint-keyword ')'
+/// 'vectorize_predicate' '(' loop-hint-keyword ')'
+/// 'vectorize_width' '(' loop-hint-value ')'
+/// 'interleave_count' '(' loop-hint-value ')'
+/// 'unroll_count' '(' loop-hint-value ')'
+/// 'pipeline' '(' disable ')'
+/// 'pipeline_initiation_interval' '(' loop-hint-value ')'
+///
+/// loop-hint-keyword:
+/// 'enable'
+/// 'disable'
+/// 'assume_safety'
+///
+/// unroll-hint-keyword:
+/// 'enable'
+/// 'disable'
+/// 'full'
+///
+/// loop-hint-value:
+/// constant-expression
+///
+/// Specifying vectorize(enable) or vectorize_width(_value_) instructs llvm to
+/// try vectorizing the instructions of the loop it precedes. Specifying
+/// interleave(enable) or interleave_count(_value_) instructs llvm to try
+/// interleaving multiple iterations of the loop it precedes. The width of the
+/// vector instructions is specified by vectorize_width() and the number of
+/// interleaved loop iterations is specified by interleave_count(). Specifying a
+/// value of 1 effectively disables vectorization/interleaving, even if it is
+/// possible and profitable, and 0 is invalid. The loop vectorizer currently
+/// only works on inner loops.
+///
+/// The unroll and unroll_count directives control the concatenation
+/// unroller. Specifying unroll(enable) instructs llvm to unroll the loop
+/// completely if the trip count is known at compile time and unroll partially
+/// if the trip count is not known. Specifying unroll(full) is similar to
+/// unroll(enable) but will unroll the loop only if the trip count is known at
+/// compile time. Specifying unroll(disable) disables unrolling for the
+/// loop. Specifying unroll_count(_value_) instructs llvm to try to unroll the
+/// loop the number of times indicated by the value.
+void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ // Incoming token is "loop" from "#pragma clang loop".
+ Token PragmaName = Tok;
+ SmallVector<Token, 1> TokenList;
+
+ // Lex the optimization option and verify it is an identifier.
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_loop_invalid_option)
+ << /*MissingOption=*/true << "";
+ return;
+ }
+
+ while (Tok.is(tok::identifier)) {
+ Token Option = Tok;
+ IdentifierInfo *OptionInfo = Tok.getIdentifierInfo();
+
+ bool OptionValid = llvm::StringSwitch<bool>(OptionInfo->getName())
+ .Case("vectorize", true)
+ .Case("interleave", true)
+ .Case("unroll", true)
+ .Case("distribute", true)
+ .Case("vectorize_predicate", true)
+ .Case("vectorize_width", true)
+ .Case("interleave_count", true)
+ .Case("unroll_count", true)
+ .Case("pipeline", true)
+ .Case("pipeline_initiation_interval", true)
+ .Default(false);
+ if (!OptionValid) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_loop_invalid_option)
+ << /*MissingOption=*/false << OptionInfo;
+ return;
+ }
+ PP.Lex(Tok);
+
+ // Read '('
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
+ return;
+ }
+ PP.Lex(Tok);
+
+ auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo;
+ if (ParseLoopHintValue(PP, Tok, PragmaName, Option, /*ValueInParens=*/true,
+ *Info))
+ return;
+
+ // Generate the loop hint token.
+ Token LoopHintTok;
+ LoopHintTok.startToken();
+ LoopHintTok.setKind(tok::annot_pragma_loop_hint);
+ LoopHintTok.setLocation(Introducer.Loc);
+ LoopHintTok.setAnnotationEndLoc(PragmaName.getLocation());
+ LoopHintTok.setAnnotationValue(static_cast<void *>(Info));
+ TokenList.push_back(LoopHintTok);
+ }
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "clang loop";
+ return;
+ }
+
+ auto TokenArray = std::make_unique<Token[]>(TokenList.size());
+ std::copy(TokenList.begin(), TokenList.end(), TokenArray.get());
+
+ PP.EnterTokenStream(std::move(TokenArray), TokenList.size(),
+ /*DisableMacroExpansion=*/false, /*IsReinject=*/false);
+}
+
+/// Handle the loop unroll optimization pragmas.
+/// #pragma unroll
+/// #pragma unroll unroll-hint-value
+/// #pragma unroll '(' unroll-hint-value ')'
+/// #pragma nounroll
+/// #pragma unroll_and_jam
+/// #pragma unroll_and_jam unroll-hint-value
+/// #pragma unroll_and_jam '(' unroll-hint-value ')'
+/// #pragma nounroll_and_jam
+///
+/// unroll-hint-value:
+/// constant-expression
+///
+/// Loop unrolling hints can be specified with '#pragma unroll' or
+/// '#pragma nounroll'. '#pragma unroll' can take a numeric argument optionally
+/// contained in parentheses. With no argument the directive instructs llvm to
+/// try to unroll the loop completely. A positive integer argument can be
+/// specified to indicate the number of times the loop should be unrolled. To
+/// maximize compatibility with other compilers the unroll count argument can be
+/// specified with or without parentheses. Specifying, '#pragma nounroll'
+/// disables unrolling of the loop.
+void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ // Incoming token is "unroll" for "#pragma unroll", or "nounroll" for
+ // "#pragma nounroll".
+ Token PragmaName = Tok;
+ PP.Lex(Tok);
+ auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo;
+ if (Tok.is(tok::eod)) {
+ // nounroll or unroll pragma without an argument.
+ Info->PragmaName = PragmaName;
+ Info->Option.startToken();
+ } else if (PragmaName.getIdentifierInfo()->getName() == "nounroll" ||
+ PragmaName.getIdentifierInfo()->getName() == "nounroll_and_jam") {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaName.getIdentifierInfo()->getName();
+ return;
+ } else {
+ // Unroll pragma with an argument: "#pragma unroll N" or
+ // "#pragma unroll(N)".
+ // Read '(' if it exists.
+ bool ValueInParens = Tok.is(tok::l_paren);
+ if (ValueInParens)
+ PP.Lex(Tok);
+
+ Token Option;
+ Option.startToken();
+ if (ParseLoopHintValue(PP, Tok, PragmaName, Option, ValueInParens, *Info))
+ return;
+
+ // In CUDA, the argument to '#pragma unroll' should not be contained in
+ // parentheses.
+ if (PP.getLangOpts().CUDA && ValueInParens)
+ PP.Diag(Info->Toks[0].getLocation(),
+ diag::warn_pragma_unroll_cuda_value_in_parens);
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "unroll";
+ return;
+ }
+ }
+
+ // Generate the hint token.
+ auto TokenArray = std::make_unique<Token[]>(1);
+ TokenArray[0].startToken();
+ TokenArray[0].setKind(tok::annot_pragma_loop_hint);
+ TokenArray[0].setLocation(Introducer.Loc);
+ TokenArray[0].setAnnotationEndLoc(PragmaName.getLocation());
+ TokenArray[0].setAnnotationValue(static_cast<void *>(Info));
+ PP.EnterTokenStream(std::move(TokenArray), 1,
+ /*DisableMacroExpansion=*/false, /*IsReinject=*/false);
+}
+
+/// Handle the Microsoft \#pragma intrinsic extension.
+///
+/// The syntax is:
+/// \code
+/// #pragma intrinsic(memset)
+/// #pragma intrinsic(strlen, memcpy)
+/// \endcode
+///
+/// Pragma intrisic tells the compiler to use a builtin version of the
+/// function. Clang does it anyway, so the pragma doesn't really do anything.
+/// Anyway, we emit a warning if the function specified in \#pragma intrinsic
+/// isn't an intrinsic in clang and suggest to include intrin.h.
+void PragmaMSIntrinsicHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ PP.Lex(Tok);
+
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
+ << "intrinsic";
+ return;
+ }
+ PP.Lex(Tok);
+
+ bool SuggestIntrinH = !PP.isMacroDefined("__INTRIN_H");
+
+ while (Tok.is(tok::identifier)) {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (!II->getBuiltinID())
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_intrinsic_builtin)
+ << II << SuggestIntrinH;
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::comma))
+ break;
+ PP.Lex(Tok);
+ }
+
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
+ << "intrinsic";
+ return;
+ }
+ PP.Lex(Tok);
+
+ if (Tok.isNot(tok::eod))
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "intrinsic";
+}
+
+bool Parser::HandlePragmaMSFunction(StringRef PragmaName,
+ SourceLocation PragmaLocation) {
+ Token FirstTok = Tok;
+
+ if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
+ PragmaName))
+ return false;
+
+ bool SuggestIntrinH = !PP.isMacroDefined("__INTRIN_H");
+
+ llvm::SmallVector<StringRef> NoBuiltins;
+ while (Tok.is(tok::identifier)) {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (!II->getBuiltinID())
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_intrinsic_builtin)
+ << II << SuggestIntrinH;
+ else
+ NoBuiltins.emplace_back(II->getName());
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::comma))
+ break;
+ PP.Lex(Tok); // ,
+ }
+
+ if (ExpectAndConsume(tok::r_paren, diag::warn_pragma_expected_rparen,
+ PragmaName) ||
+ ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol,
+ PragmaName))
+ return false;
+
+ Actions.ActOnPragmaMSFunction(FirstTok.getLocation(), NoBuiltins);
+ return true;
+}
+
+// #pragma optimize("gsty", on|off)
+bool Parser::HandlePragmaMSOptimize(StringRef PragmaName,
+ SourceLocation PragmaLocation) {
+ Token FirstTok = Tok;
+ if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
+ PragmaName))
+ return false;
+
+ if (Tok.isNot(tok::string_literal)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_string) << PragmaName;
+ return false;
+ }
+ ExprResult StringResult = ParseStringLiteralExpression();
+ if (StringResult.isInvalid())
+ return false; // Already diagnosed.
+ StringLiteral *OptimizationList = cast<StringLiteral>(StringResult.get());
+ if (OptimizationList->getCharByteWidth() != 1) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string)
+ << PragmaName;
+ return false;
+ }
+
+ if (ExpectAndConsume(tok::comma, diag::warn_pragma_expected_comma,
+ PragmaName))
+ return false;
+
+ if (Tok.is(tok::eof) || Tok.is(tok::r_paren)) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_missing_argument)
+ << PragmaName << /*Expected=*/true << "'on' or 'off'";
+ return false;
+ }
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (!II || (!II->isStr("on") && !II->isStr("off"))) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_invalid_argument)
+ << PP.getSpelling(Tok) << PragmaName << /*Expected=*/true
+ << "'on' or 'off'";
+ return false;
+ }
+ bool IsOn = II->isStr("on");
+ PP.Lex(Tok);
+
+ if (ExpectAndConsume(tok::r_paren, diag::warn_pragma_expected_rparen,
+ PragmaName))
+ return false;
+
+ // TODO: Add support for "sgty"
+ if (!OptimizationList->getString().empty()) {
+ PP.Diag(PragmaLocation, diag::warn_pragma_invalid_argument)
+ << OptimizationList->getString() << PragmaName << /*Expected=*/true
+ << "\"\"";
+ return false;
+ }
+
+ if (ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol,
+ PragmaName))
+ return false;
+
+ Actions.ActOnPragmaMSOptimize(FirstTok.getLocation(), IsOn);
+ return true;
+}
+
+void PragmaForceCUDAHostDeviceHandler::HandlePragma(
+ Preprocessor &PP, PragmaIntroducer Introducer, Token &Tok) {
+ Token FirstTok = Tok;
+
+ PP.Lex(Tok);
+ IdentifierInfo *Info = Tok.getIdentifierInfo();
+ if (!Info || (!Info->isStr("begin") && !Info->isStr("end"))) {
+ PP.Diag(FirstTok.getLocation(),
+ diag::warn_pragma_force_cuda_host_device_bad_arg);
+ return;
+ }
+
+ if (Info->isStr("begin"))
+ Actions.PushForceCUDAHostDevice();
+ else if (!Actions.PopForceCUDAHostDevice())
+ PP.Diag(FirstTok.getLocation(),
+ diag::err_pragma_cannot_end_force_cuda_host_device);
+
+ PP.Lex(Tok);
+ if (!Tok.is(tok::eod))
+ PP.Diag(FirstTok.getLocation(),
+ diag::warn_pragma_force_cuda_host_device_bad_arg);
+}
+
+/// Handle the #pragma clang attribute directive.
+///
+/// The syntax is:
+/// \code
+/// #pragma clang attribute push (attribute, subject-set)
+/// #pragma clang attribute push
+/// #pragma clang attribute (attribute, subject-set)
+/// #pragma clang attribute pop
+/// \endcode
+///
+/// There are also 'namespace' variants of push and pop directives. The bare
+/// '#pragma clang attribute (attribute, subject-set)' version doesn't require a
+/// namespace, since it always applies attributes to the most recently pushed
+/// group, regardless of namespace.
+/// \code
+/// #pragma clang attribute namespace.push (attribute, subject-set)
+/// #pragma clang attribute namespace.push
+/// #pragma clang attribute namespace.pop
+/// \endcode
+///
+/// The subject-set clause defines the set of declarations which receive the
+/// attribute. Its exact syntax is described in the LanguageExtensions document
+/// in Clang's documentation.
+///
+/// This directive instructs the compiler to begin/finish applying the specified
+/// attribute to the set of attribute-specific declarations in the active range
+/// of the pragma.
+void PragmaAttributeHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &FirstToken) {
+ Token Tok;
+ PP.Lex(Tok);
+ auto *Info = new (PP.getPreprocessorAllocator())
+ PragmaAttributeInfo(AttributesForPragmaAttribute);
+
+ // Parse the optional namespace followed by a period.
+ if (Tok.is(tok::identifier)) {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (!II->isStr("push") && !II->isStr("pop")) {
+ Info->Namespace = II;
+ PP.Lex(Tok);
+
+ if (!Tok.is(tok::period)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_expected_period)
+ << II;
+ return;
+ }
+ PP.Lex(Tok);
+ }
+ }
+
+ if (!Tok.isOneOf(tok::identifier, tok::l_paren)) {
+ PP.Diag(Tok.getLocation(),
+ diag::err_pragma_attribute_expected_push_pop_paren);
+ return;
+ }
+
+ // Determine what action this pragma clang attribute represents.
+ if (Tok.is(tok::l_paren)) {
+ if (Info->Namespace) {
+ PP.Diag(Tok.getLocation(),
+ diag::err_pragma_attribute_namespace_on_attribute);
+ PP.Diag(Tok.getLocation(),
+ diag::note_pragma_attribute_namespace_on_attribute);
+ return;
+ }
+ Info->Action = PragmaAttributeInfo::Attribute;
+ } else {
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II->isStr("push"))
+ Info->Action = PragmaAttributeInfo::Push;
+ else if (II->isStr("pop"))
+ Info->Action = PragmaAttributeInfo::Pop;
+ else {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_invalid_argument)
+ << PP.getSpelling(Tok);
+ return;
+ }
+
+ PP.Lex(Tok);
+ }
+
+ // Parse the actual attribute.
+ if ((Info->Action == PragmaAttributeInfo::Push && Tok.isNot(tok::eod)) ||
+ Info->Action == PragmaAttributeInfo::Attribute) {
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
+ return;
+ }
+ PP.Lex(Tok);
+
+ // Lex the attribute tokens.
+ SmallVector<Token, 16> AttributeTokens;
+ int OpenParens = 1;
+ while (Tok.isNot(tok::eod)) {
+ if (Tok.is(tok::l_paren))
+ OpenParens++;
+ else if (Tok.is(tok::r_paren)) {
+ OpenParens--;
+ if (OpenParens == 0)
+ break;
+ }
+
+ AttributeTokens.push_back(Tok);
+ PP.Lex(Tok);
+ }
+
+ if (AttributeTokens.empty()) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_expected_attribute);
+ return;
+ }
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren;
+ return;
+ }
+ SourceLocation EndLoc = Tok.getLocation();
+ PP.Lex(Tok);
+
+ // Terminate the attribute for parsing.
+ Token EOFTok;
+ EOFTok.startToken();
+ EOFTok.setKind(tok::eof);
+ EOFTok.setLocation(EndLoc);
+ AttributeTokens.push_back(EOFTok);
+
+ markAsReinjectedForRelexing(AttributeTokens);
+ Info->Tokens =
+ llvm::ArrayRef(AttributeTokens).copy(PP.getPreprocessorAllocator());
+ }
+
+ if (Tok.isNot(tok::eod))
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "clang attribute";
+
+ // Generate the annotated pragma token.
+ auto TokenArray = std::make_unique<Token[]>(1);
+ TokenArray[0].startToken();
+ TokenArray[0].setKind(tok::annot_pragma_attribute);
+ TokenArray[0].setLocation(FirstToken.getLocation());
+ TokenArray[0].setAnnotationEndLoc(FirstToken.getLocation());
+ TokenArray[0].setAnnotationValue(static_cast<void *>(Info));
+ PP.EnterTokenStream(std::move(TokenArray), 1,
+ /*DisableMacroExpansion=*/false, /*IsReinject=*/false);
+}
+
+// Handle '#pragma clang max_tokens 12345'.
+void PragmaMaxTokensHereHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ PP.Lex(Tok);
+ if (Tok.is(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument)
+ << "clang max_tokens_here" << /*Expected=*/true << "integer";
+ return;
+ }
+
+ SourceLocation Loc = Tok.getLocation();
+ uint64_t MaxTokens;
+ if (Tok.isNot(tok::numeric_constant) ||
+ !PP.parseSimpleIntegerLiteral(Tok, MaxTokens)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_expected_integer)
+ << "clang max_tokens_here";
+ return;
+ }
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "clang max_tokens_here";
+ return;
+ }
+
+ if (PP.getTokenCount() > MaxTokens) {
+ PP.Diag(Loc, diag::warn_max_tokens)
+ << PP.getTokenCount() << (unsigned)MaxTokens;
+ }
+}
+
+// Handle '#pragma clang max_tokens_total 12345'.
+void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &Tok) {
+ PP.Lex(Tok);
+ if (Tok.is(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument)
+ << "clang max_tokens_total" << /*Expected=*/true << "integer";
+ return;
+ }
+
+ SourceLocation Loc = Tok.getLocation();
+ uint64_t MaxTokens;
+ if (Tok.isNot(tok::numeric_constant) ||
+ !PP.parseSimpleIntegerLiteral(Tok, MaxTokens)) {
+ PP.Diag(Tok.getLocation(), diag::err_pragma_expected_integer)
+ << "clang max_tokens_total";
+ return;
+ }
+
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "clang max_tokens_total";
+ return;
+ }
+
+ PP.overrideMaxTokens(MaxTokens, Loc);
+}
+
+// Handle '#pragma clang riscv intrinsic vector'.
+void PragmaRISCVHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &FirstToken) {
+ Token Tok;
+ PP.Lex(Tok);
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+
+ if (!II || !II->isStr("intrinsic")) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
+ << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'intrinsic'";
+ return;
+ }
+
+ PP.Lex(Tok);
+ II = Tok.getIdentifierInfo();
+ if (!II || !II->isStr("vector")) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
+ << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'vector'";
+ return;
+ }
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "clang riscv intrinsic";
+ return;
+ }
+
+ Actions.DeclareRISCVVBuiltins = true;
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseStmt.cpp b/contrib/libs/clang16/lib/Parse/ParseStmt.cpp
new file mode 100644
index 0000000000..1c8441fafc
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseStmt.cpp
@@ -0,0 +1,2741 @@
+//===--- ParseStmt.cpp - Statement and Block Parser -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Statement and Block portions of the Parser
+// interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/PrettyDeclStackTrace.h"
+#include "clang/Basic/Attributes.h"
+#include "clang/Basic/PrettyStackTrace.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Parse/LoopHint.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/Scope.h"
+#include "clang/Sema/TypoCorrection.h"
+#include "llvm/ADT/STLExtras.h"
+#include <optional>
+
+using namespace clang;
+
+//===----------------------------------------------------------------------===//
+// C99 6.8: Statements and Blocks.
+//===----------------------------------------------------------------------===//
+
+/// Parse a standalone statement (for instance, as the body of an 'if',
+/// 'while', or 'for').
+StmtResult Parser::ParseStatement(SourceLocation *TrailingElseLoc,
+ ParsedStmtContext StmtCtx) {
+ StmtResult Res;
+
+ // We may get back a null statement if we found a #pragma. Keep going until
+ // we get an actual statement.
+ StmtVector Stmts;
+ do {
+ Res = ParseStatementOrDeclaration(Stmts, StmtCtx, TrailingElseLoc);
+ } while (!Res.isInvalid() && !Res.get());
+
+ return Res;
+}
+
+/// ParseStatementOrDeclaration - Read 'statement' or 'declaration'.
+/// StatementOrDeclaration:
+/// statement
+/// declaration
+///
+/// statement:
+/// labeled-statement
+/// compound-statement
+/// expression-statement
+/// selection-statement
+/// iteration-statement
+/// jump-statement
+/// [C++] declaration-statement
+/// [C++] try-block
+/// [MS] seh-try-block
+/// [OBC] objc-throw-statement
+/// [OBC] objc-try-catch-statement
+/// [OBC] objc-synchronized-statement
+/// [GNU] asm-statement
+/// [OMP] openmp-construct [TODO]
+///
+/// labeled-statement:
+/// identifier ':' statement
+/// 'case' constant-expression ':' statement
+/// 'default' ':' statement
+///
+/// selection-statement:
+/// if-statement
+/// switch-statement
+///
+/// iteration-statement:
+/// while-statement
+/// do-statement
+/// for-statement
+///
+/// expression-statement:
+/// expression[opt] ';'
+///
+/// jump-statement:
+/// 'goto' identifier ';'
+/// 'continue' ';'
+/// 'break' ';'
+/// 'return' expression[opt] ';'
+/// [GNU] 'goto' '*' expression ';'
+///
+/// [OBC] objc-throw-statement:
+/// [OBC] '@' 'throw' expression ';'
+/// [OBC] '@' 'throw' ';'
+///
+StmtResult
+Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
+ ParsedStmtContext StmtCtx,
+ SourceLocation *TrailingElseLoc) {
+
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ // Because we're parsing either a statement or a declaration, the order of
+ // attribute parsing is important. [[]] attributes at the start of a
+ // statement are different from [[]] attributes that follow an __attribute__
+ // at the start of the statement. Thus, we're not using MaybeParseAttributes
+ // here because we don't want to allow arbitrary orderings.
+ ParsedAttributes CXX11Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
+ ParsedAttributes GNUAttrs(AttrFactory);
+ if (getLangOpts().OpenCL)
+ MaybeParseGNUAttributes(GNUAttrs);
+
+ StmtResult Res = ParseStatementOrDeclarationAfterAttributes(
+ Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs, GNUAttrs);
+ MaybeDestroyTemplateIds();
+
+ // Attributes that are left should all go on the statement, so concatenate the
+ // two lists.
+ ParsedAttributes Attrs(AttrFactory);
+ takeAndConcatenateAttrs(CXX11Attrs, GNUAttrs, Attrs);
+
+ assert((Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
+ "attributes on empty statement");
+
+ if (Attrs.empty() || Res.isInvalid())
+ return Res;
+
+ return Actions.ActOnAttributedStmt(Attrs, Res.get());
+}
+
+namespace {
+class StatementFilterCCC final : public CorrectionCandidateCallback {
+public:
+ StatementFilterCCC(Token nextTok) : NextToken(nextTok) {
+ WantTypeSpecifiers = nextTok.isOneOf(tok::l_paren, tok::less, tok::l_square,
+ tok::identifier, tok::star, tok::amp);
+ WantExpressionKeywords =
+ nextTok.isOneOf(tok::l_paren, tok::identifier, tok::arrow, tok::period);
+ WantRemainingKeywords =
+ nextTok.isOneOf(tok::l_paren, tok::semi, tok::identifier, tok::l_brace);
+ WantCXXNamedCasts = false;
+ }
+
+ bool ValidateCandidate(const TypoCorrection &candidate) override {
+ if (FieldDecl *FD = candidate.getCorrectionDeclAs<FieldDecl>())
+ return !candidate.getCorrectionSpecifier() || isa<ObjCIvarDecl>(FD);
+ if (NextToken.is(tok::equal))
+ return candidate.getCorrectionDeclAs<VarDecl>();
+ if (NextToken.is(tok::period) &&
+ candidate.getCorrectionDeclAs<NamespaceDecl>())
+ return false;
+ return CorrectionCandidateCallback::ValidateCandidate(candidate);
+ }
+
+ std::unique_ptr<CorrectionCandidateCallback> clone() override {
+ return std::make_unique<StatementFilterCCC>(*this);
+ }
+
+private:
+ Token NextToken;
+};
+}
+
+StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
+ StmtVector &Stmts, ParsedStmtContext StmtCtx,
+ SourceLocation *TrailingElseLoc, ParsedAttributes &CXX11Attrs,
+ ParsedAttributes &GNUAttrs) {
+ const char *SemiError = nullptr;
+ StmtResult Res;
+ SourceLocation GNUAttributeLoc;
+
+ // Cases in this switch statement should fall through if the parser expects
+ // the token to end in a semicolon (in which case SemiError should be set),
+ // or they directly 'return;' if not.
+Retry:
+ tok::TokenKind Kind = Tok.getKind();
+ SourceLocation AtLoc;
+ switch (Kind) {
+ case tok::at: // May be a @try or @throw statement
+ {
+ AtLoc = ConsumeToken(); // consume @
+ return ParseObjCAtStatement(AtLoc, StmtCtx);
+ }
+
+ case tok::code_completion:
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Statement);
+ return StmtError();
+
+ case tok::identifier:
+ ParseIdentifier: {
+ Token Next = NextToken();
+ if (Next.is(tok::colon)) { // C99 6.8.1: labeled-statement
+ // Both C++11 and GNU attributes preceding the label appertain to the
+ // label, so put them in a single list to pass on to
+ // ParseLabeledStatement().
+ ParsedAttributes Attrs(AttrFactory);
+ takeAndConcatenateAttrs(CXX11Attrs, GNUAttrs, Attrs);
+
+ // identifier ':' statement
+ return ParseLabeledStatement(Attrs, StmtCtx);
+ }
+
+ // Look up the identifier, and typo-correct it to a keyword if it's not
+ // found.
+ if (Next.isNot(tok::coloncolon)) {
+ // Try to limit which sets of keywords should be included in typo
+ // correction based on what the next token is.
+ StatementFilterCCC CCC(Next);
+ if (TryAnnotateName(&CCC) == ANK_Error) {
+ // Handle errors here by skipping up to the next semicolon or '}', and
+ // eat the semicolon if that's what stopped us.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ return StmtError();
+ }
+
+ // If the identifier was typo-corrected, try again.
+ if (Tok.isNot(tok::identifier))
+ goto Retry;
+ }
+
+ // Fall through
+ [[fallthrough]];
+ }
+
+ default: {
+ bool HaveAttrs = !CXX11Attrs.empty() || !GNUAttrs.empty();
+ auto IsStmtAttr = [](ParsedAttr &Attr) { return Attr.isStmtAttr(); };
+ bool AllAttrsAreStmtAttrs = llvm::all_of(CXX11Attrs, IsStmtAttr) &&
+ llvm::all_of(GNUAttrs, IsStmtAttr);
+ if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt ||
+ (StmtCtx & ParsedStmtContext::AllowDeclarationsInC) !=
+ ParsedStmtContext()) &&
+ ((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
+ isDeclarationStatement())) {
+ SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
+ DeclGroupPtrTy Decl;
+ if (GNUAttributeLoc.isValid()) {
+ DeclStart = GNUAttributeLoc;
+ Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, CXX11Attrs,
+ GNUAttrs, &GNUAttributeLoc);
+ } else {
+ Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, CXX11Attrs,
+ GNUAttrs);
+ }
+ if (CXX11Attrs.Range.getBegin().isValid()) {
+ // The caller must guarantee that the CXX11Attrs appear before the
+ // GNUAttrs, and we rely on that here.
+ assert(GNUAttrs.Range.getBegin().isInvalid() ||
+ GNUAttrs.Range.getBegin() > CXX11Attrs.Range.getBegin());
+ DeclStart = CXX11Attrs.Range.getBegin();
+ } else if (GNUAttrs.Range.getBegin().isValid())
+ DeclStart = GNUAttrs.Range.getBegin();
+ return Actions.ActOnDeclStmt(Decl, DeclStart, DeclEnd);
+ }
+
+ if (Tok.is(tok::r_brace)) {
+ Diag(Tok, diag::err_expected_statement);
+ return StmtError();
+ }
+
+ switch (Tok.getKind()) {
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
+ if (NextToken().is(tok::less)) {
+ Tok.setKind(tok::identifier);
+ Diag(Tok, diag::ext_keyword_as_ident)
+ << Tok.getIdentifierInfo()->getName() << 0;
+ goto ParseIdentifier;
+ }
+ [[fallthrough]];
+ default:
+ return ParseExprStatement(StmtCtx);
+ }
+ }
+
+ case tok::kw___attribute: {
+ GNUAttributeLoc = Tok.getLocation();
+ ParseGNUAttributes(GNUAttrs);
+ goto Retry;
+ }
+
+ case tok::kw_case: // C99 6.8.1: labeled-statement
+ return ParseCaseStatement(StmtCtx);
+ case tok::kw_default: // C99 6.8.1: labeled-statement
+ return ParseDefaultStatement(StmtCtx);
+
+ case tok::l_brace: // C99 6.8.2: compound-statement
+ return ParseCompoundStatement();
+ case tok::semi: { // C99 6.8.3p3: expression[opt] ';'
+ bool HasLeadingEmptyMacro = Tok.hasLeadingEmptyMacro();
+ return Actions.ActOnNullStmt(ConsumeToken(), HasLeadingEmptyMacro);
+ }
+
+ case tok::kw_if: // C99 6.8.4.1: if-statement
+ return ParseIfStatement(TrailingElseLoc);
+ case tok::kw_switch: // C99 6.8.4.2: switch-statement
+ return ParseSwitchStatement(TrailingElseLoc);
+
+ case tok::kw_while: // C99 6.8.5.1: while-statement
+ return ParseWhileStatement(TrailingElseLoc);
+ case tok::kw_do: // C99 6.8.5.2: do-statement
+ Res = ParseDoStatement();
+ SemiError = "do/while";
+ break;
+ case tok::kw_for: // C99 6.8.5.3: for-statement
+ return ParseForStatement(TrailingElseLoc);
+
+ case tok::kw_goto: // C99 6.8.6.1: goto-statement
+ Res = ParseGotoStatement();
+ SemiError = "goto";
+ break;
+ case tok::kw_continue: // C99 6.8.6.2: continue-statement
+ Res = ParseContinueStatement();
+ SemiError = "continue";
+ break;
+ case tok::kw_break: // C99 6.8.6.3: break-statement
+ Res = ParseBreakStatement();
+ SemiError = "break";
+ break;
+ case tok::kw_return: // C99 6.8.6.4: return-statement
+ Res = ParseReturnStatement();
+ SemiError = "return";
+ break;
+ case tok::kw_co_return: // C++ Coroutines: co_return statement
+ Res = ParseReturnStatement();
+ SemiError = "co_return";
+ break;
+
+ case tok::kw_asm: {
+ for (const ParsedAttr &AL : CXX11Attrs)
+ Diag(AL.getRange().getBegin(), diag::warn_attribute_ignored) << AL;
+ // Prevent these from being interpreted as statement attributes later on.
+ CXX11Attrs.clear();
+ ProhibitAttributes(GNUAttrs);
+ bool msAsm = false;
+ Res = ParseAsmStatement(msAsm);
+ if (msAsm) return Res;
+ SemiError = "asm";
+ break;
+ }
+
+ case tok::kw___if_exists:
+ case tok::kw___if_not_exists:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ ParseMicrosoftIfExistsStatement(Stmts);
+ // An __if_exists block is like a compound statement, but it doesn't create
+ // a new scope.
+ return StmtEmpty();
+
+ case tok::kw_try: // C++ 15: try-block
+ return ParseCXXTryBlock();
+
+ case tok::kw___try:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ return ParseSEHTryBlock();
+
+ case tok::kw___leave:
+ Res = ParseSEHLeaveStatement();
+ SemiError = "__leave";
+ break;
+
+ case tok::annot_pragma_vis:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaVisibility();
+ return StmtEmpty();
+
+ case tok::annot_pragma_pack:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaPack();
+ return StmtEmpty();
+
+ case tok::annot_pragma_msstruct:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaMSStruct();
+ return StmtEmpty();
+
+ case tok::annot_pragma_align:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaAlign();
+ return StmtEmpty();
+
+ case tok::annot_pragma_weak:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaWeak();
+ return StmtEmpty();
+
+ case tok::annot_pragma_weakalias:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaWeakAlias();
+ return StmtEmpty();
+
+ case tok::annot_pragma_redefine_extname:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaRedefineExtname();
+ return StmtEmpty();
+
+ case tok::annot_pragma_fp_contract:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ Diag(Tok, diag::err_pragma_file_or_compound_scope) << "fp_contract";
+ ConsumeAnnotationToken();
+ return StmtError();
+
+ case tok::annot_pragma_fp:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ Diag(Tok, diag::err_pragma_file_or_compound_scope) << "clang fp";
+ ConsumeAnnotationToken();
+ return StmtError();
+
+ case tok::annot_pragma_fenv_access:
+ case tok::annot_pragma_fenv_access_ms:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ Diag(Tok, diag::err_pragma_file_or_compound_scope)
+ << (Kind == tok::annot_pragma_fenv_access ? "STDC FENV_ACCESS"
+ : "fenv_access");
+ ConsumeAnnotationToken();
+ return StmtEmpty();
+
+ case tok::annot_pragma_fenv_round:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ Diag(Tok, diag::err_pragma_file_or_compound_scope) << "STDC FENV_ROUND";
+ ConsumeAnnotationToken();
+ return StmtError();
+
+ case tok::annot_pragma_float_control:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ Diag(Tok, diag::err_pragma_file_or_compound_scope) << "float_control";
+ ConsumeAnnotationToken();
+ return StmtError();
+
+ case tok::annot_pragma_opencl_extension:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaOpenCLExtension();
+ return StmtEmpty();
+
+ case tok::annot_pragma_captured:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ return HandlePragmaCaptured();
+
+ case tok::annot_pragma_openmp:
+ // Prohibit attributes that are not OpenMP attributes, but only before
+ // processing a #pragma omp clause.
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ [[fallthrough]];
+ case tok::annot_attr_openmp:
+ // Do not prohibit attributes if they were OpenMP attributes.
+ return ParseOpenMPDeclarativeOrExecutableDirective(StmtCtx);
+
+ case tok::annot_pragma_ms_pointers_to_members:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaMSPointersToMembers();
+ return StmtEmpty();
+
+ case tok::annot_pragma_ms_pragma:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaMSPragma();
+ return StmtEmpty();
+
+ case tok::annot_pragma_ms_vtordisp:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ HandlePragmaMSVtorDisp();
+ return StmtEmpty();
+
+ case tok::annot_pragma_loop_hint:
+ ProhibitAttributes(CXX11Attrs);
+ ProhibitAttributes(GNUAttrs);
+ return ParsePragmaLoopHint(Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs);
+
+ case tok::annot_pragma_dump:
+ HandlePragmaDump();
+ return StmtEmpty();
+
+ case tok::annot_pragma_attribute:
+ HandlePragmaAttribute();
+ return StmtEmpty();
+ }
+
+ // If we reached this code, the statement must end in a semicolon.
+ if (!TryConsumeToken(tok::semi) && !Res.isInvalid()) {
+ // If the result was valid, then we do want to diagnose this. Use
+ // ExpectAndConsume to emit the diagnostic, even though we know it won't
+ // succeed.
+ ExpectAndConsume(tok::semi, diag::err_expected_semi_after_stmt, SemiError);
+ // Skip until we see a } or ;, but don't eat it.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ }
+
+ return Res;
+}
+
+/// Parse an expression statement.
+StmtResult Parser::ParseExprStatement(ParsedStmtContext StmtCtx) {
+ // If a case keyword is missing, this is where it should be inserted.
+ Token OldToken = Tok;
+
+ ExprStatementTokLoc = Tok.getLocation();
+
+ // expression[opt] ';'
+ ExprResult Expr(ParseExpression());
+ if (Expr.isInvalid()) {
+ // If the expression is invalid, skip ahead to the next semicolon or '}'.
+ // Not doing this opens us up to the possibility of infinite loops if
+ // ParseExpression does not consume any tokens.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ return Actions.ActOnExprStmtError();
+ }
+
+ if (Tok.is(tok::colon) && getCurScope()->isSwitchScope() &&
+ Actions.CheckCaseExpression(Expr.get())) {
+ // If a constant expression is followed by a colon inside a switch block,
+ // suggest a missing case keyword.
+ Diag(OldToken, diag::err_expected_case_before_expression)
+ << FixItHint::CreateInsertion(OldToken.getLocation(), "case ");
+
+ // Recover parsing as a case statement.
+ return ParseCaseStatement(StmtCtx, /*MissingCase=*/true, Expr);
+ }
+
+ // Otherwise, eat the semicolon.
+ ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
+ return handleExprStmt(Expr, StmtCtx);
+}
+
+/// ParseSEHTryBlockCommon
+///
+/// seh-try-block:
+/// '__try' compound-statement seh-handler
+///
+/// seh-handler:
+/// seh-except-block
+/// seh-finally-block
+///
+StmtResult Parser::ParseSEHTryBlock() {
+ assert(Tok.is(tok::kw___try) && "Expected '__try'");
+ SourceLocation TryLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::l_brace))
+ return StmtError(Diag(Tok, diag::err_expected) << tok::l_brace);
+
+ StmtResult TryBlock(ParseCompoundStatement(
+ /*isStmtExpr=*/false,
+ Scope::DeclScope | Scope::CompoundStmtScope | Scope::SEHTryScope));
+ if (TryBlock.isInvalid())
+ return TryBlock;
+
+ StmtResult Handler;
+ if (Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo() == getSEHExceptKeyword()) {
+ SourceLocation Loc = ConsumeToken();
+ Handler = ParseSEHExceptBlock(Loc);
+ } else if (Tok.is(tok::kw___finally)) {
+ SourceLocation Loc = ConsumeToken();
+ Handler = ParseSEHFinallyBlock(Loc);
+ } else {
+ return StmtError(Diag(Tok, diag::err_seh_expected_handler));
+ }
+
+ if(Handler.isInvalid())
+ return Handler;
+
+ return Actions.ActOnSEHTryBlock(false /* IsCXXTry */,
+ TryLoc,
+ TryBlock.get(),
+ Handler.get());
+}
+
+/// ParseSEHExceptBlock - Handle __except
+///
+/// seh-except-block:
+/// '__except' '(' seh-filter-expression ')' compound-statement
+///
+StmtResult Parser::ParseSEHExceptBlock(SourceLocation ExceptLoc) {
+ PoisonIdentifierRAIIObject raii(Ident__exception_code, false),
+ raii2(Ident___exception_code, false),
+ raii3(Ident_GetExceptionCode, false);
+
+ if (ExpectAndConsume(tok::l_paren))
+ return StmtError();
+
+ ParseScope ExpectScope(this, Scope::DeclScope | Scope::ControlScope |
+ Scope::SEHExceptScope);
+
+ if (getLangOpts().Borland) {
+ Ident__exception_info->setIsPoisoned(false);
+ Ident___exception_info->setIsPoisoned(false);
+ Ident_GetExceptionInfo->setIsPoisoned(false);
+ }
+
+ ExprResult FilterExpr;
+ {
+ ParseScopeFlags FilterScope(this, getCurScope()->getFlags() |
+ Scope::SEHFilterScope);
+ FilterExpr = Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ }
+
+ if (getLangOpts().Borland) {
+ Ident__exception_info->setIsPoisoned(true);
+ Ident___exception_info->setIsPoisoned(true);
+ Ident_GetExceptionInfo->setIsPoisoned(true);
+ }
+
+ if(FilterExpr.isInvalid())
+ return StmtError();
+
+ if (ExpectAndConsume(tok::r_paren))
+ return StmtError();
+
+ if (Tok.isNot(tok::l_brace))
+ return StmtError(Diag(Tok, diag::err_expected) << tok::l_brace);
+
+ StmtResult Block(ParseCompoundStatement());
+
+ if(Block.isInvalid())
+ return Block;
+
+ return Actions.ActOnSEHExceptBlock(ExceptLoc, FilterExpr.get(), Block.get());
+}
+
+/// ParseSEHFinallyBlock - Handle __finally
+///
+/// seh-finally-block:
+/// '__finally' compound-statement
+///
+StmtResult Parser::ParseSEHFinallyBlock(SourceLocation FinallyLoc) {
+ PoisonIdentifierRAIIObject raii(Ident__abnormal_termination, false),
+ raii2(Ident___abnormal_termination, false),
+ raii3(Ident_AbnormalTermination, false);
+
+ if (Tok.isNot(tok::l_brace))
+ return StmtError(Diag(Tok, diag::err_expected) << tok::l_brace);
+
+ ParseScope FinallyScope(this, 0);
+ Actions.ActOnStartSEHFinallyBlock();
+
+ StmtResult Block(ParseCompoundStatement());
+ if(Block.isInvalid()) {
+ Actions.ActOnAbortSEHFinallyBlock();
+ return Block;
+ }
+
+ return Actions.ActOnFinishSEHFinallyBlock(FinallyLoc, Block.get());
+}
+
+/// Handle __leave
+///
+/// seh-leave-statement:
+/// '__leave' ';'
+///
+StmtResult Parser::ParseSEHLeaveStatement() {
+ SourceLocation LeaveLoc = ConsumeToken(); // eat the '__leave'.
+ return Actions.ActOnSEHLeaveStmt(LeaveLoc, getCurScope());
+}
+
+/// ParseLabeledStatement - We have an identifier and a ':' after it.
+///
+/// label:
+/// identifier ':'
+/// [GNU] identifier ':' attributes[opt]
+///
+/// labeled-statement:
+/// label statement
+///
+StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
+ ParsedStmtContext StmtCtx) {
+ assert(Tok.is(tok::identifier) && Tok.getIdentifierInfo() &&
+ "Not an identifier!");
+
+ // The substatement is always a 'statement', not a 'declaration', but is
+ // otherwise in the same context as the labeled-statement.
+ StmtCtx &= ~ParsedStmtContext::AllowDeclarationsInC;
+
+ Token IdentTok = Tok; // Save the whole token.
+ ConsumeToken(); // eat the identifier.
+
+ assert(Tok.is(tok::colon) && "Not a label!");
+
+ // identifier ':' statement
+ SourceLocation ColonLoc = ConsumeToken();
+
+ // Read label attributes, if present.
+ StmtResult SubStmt;
+ if (Tok.is(tok::kw___attribute)) {
+ ParsedAttributes TempAttrs(AttrFactory);
+ ParseGNUAttributes(TempAttrs);
+
+ // In C++, GNU attributes only apply to the label if they are followed by a
+ // semicolon, to disambiguate label attributes from attributes on a labeled
+ // declaration.
+ //
+ // This doesn't quite match what GCC does; if the attribute list is empty
+ // and followed by a semicolon, GCC will reject (it appears to parse the
+ // attributes as part of a statement in that case). That looks like a bug.
+ if (!getLangOpts().CPlusPlus || Tok.is(tok::semi))
+ Attrs.takeAllFrom(TempAttrs);
+ else {
+ StmtVector Stmts;
+ ParsedAttributes EmptyCXX11Attrs(AttrFactory);
+ SubStmt = ParseStatementOrDeclarationAfterAttributes(
+ Stmts, StmtCtx, nullptr, EmptyCXX11Attrs, TempAttrs);
+ if (!TempAttrs.empty() && !SubStmt.isInvalid())
+ SubStmt = Actions.ActOnAttributedStmt(TempAttrs, SubStmt.get());
+ }
+ }
+
+ // The label may have no statement following it
+ if (SubStmt.isUnset() && Tok.is(tok::r_brace)) {
+ DiagnoseLabelAtEndOfCompoundStatement();
+ SubStmt = Actions.ActOnNullStmt(ColonLoc);
+ }
+
+ // If we've not parsed a statement yet, parse one now.
+ if (!SubStmt.isInvalid() && !SubStmt.isUsable())
+ SubStmt = ParseStatement(nullptr, StmtCtx);
+
+ // Broken substmt shouldn't prevent the label from being added to the AST.
+ if (SubStmt.isInvalid())
+ SubStmt = Actions.ActOnNullStmt(ColonLoc);
+
+ LabelDecl *LD = Actions.LookupOrCreateLabel(IdentTok.getIdentifierInfo(),
+ IdentTok.getLocation());
+ Actions.ProcessDeclAttributeList(Actions.CurScope, LD, Attrs);
+ Attrs.clear();
+
+ return Actions.ActOnLabelStmt(IdentTok.getLocation(), LD, ColonLoc,
+ SubStmt.get());
+}
+
+/// ParseCaseStatement
+/// labeled-statement:
+/// 'case' constant-expression ':' statement
+/// [GNU] 'case' constant-expression '...' constant-expression ':' statement
+///
+StmtResult Parser::ParseCaseStatement(ParsedStmtContext StmtCtx,
+ bool MissingCase, ExprResult Expr) {
+ assert((MissingCase || Tok.is(tok::kw_case)) && "Not a case stmt!");
+
+ // The substatement is always a 'statement', not a 'declaration', but is
+ // otherwise in the same context as the labeled-statement.
+ StmtCtx &= ~ParsedStmtContext::AllowDeclarationsInC;
+
+ // It is very common for code to contain many case statements recursively
+ // nested, as in (but usually without indentation):
+ // case 1:
+ // case 2:
+ // case 3:
+ // case 4:
+ // case 5: etc.
+ //
+ // Parsing this naively works, but is both inefficient and can cause us to run
+ // out of stack space in our recursive descent parser. As a special case,
+ // flatten this recursion into an iterative loop. This is complex and gross,
+ // but all the grossness is constrained to ParseCaseStatement (and some
+ // weirdness in the actions), so this is just local grossness :).
+
+ // TopLevelCase - This is the highest level we have parsed. 'case 1' in the
+ // example above.
+ StmtResult TopLevelCase(true);
+
+ // DeepestParsedCaseStmt - This is the deepest statement we have parsed, which
+ // gets updated each time a new case is parsed, and whose body is unset so
+ // far. When parsing 'case 4', this is the 'case 3' node.
+ Stmt *DeepestParsedCaseStmt = nullptr;
+
+ // While we have case statements, eat and stack them.
+ SourceLocation ColonLoc;
+ do {
+ SourceLocation CaseLoc = MissingCase ? Expr.get()->getExprLoc() :
+ ConsumeToken(); // eat the 'case'.
+ ColonLoc = SourceLocation();
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteCase(getCurScope());
+ return StmtError();
+ }
+
+ /// We don't want to treat 'case x : y' as a potential typo for 'case x::y'.
+ /// Disable this form of error recovery while we're parsing the case
+ /// expression.
+ ColonProtectionRAIIObject ColonProtection(*this);
+
+ ExprResult LHS;
+ if (!MissingCase) {
+ LHS = ParseCaseExpression(CaseLoc);
+ if (LHS.isInvalid()) {
+ // If constant-expression is parsed unsuccessfully, recover by skipping
+ // current case statement (moving to the colon that ends it).
+ if (!SkipUntil(tok::colon, tok::r_brace, StopAtSemi | StopBeforeMatch))
+ return StmtError();
+ }
+ } else {
+ LHS = Expr;
+ MissingCase = false;
+ }
+
+ // GNU case range extension.
+ SourceLocation DotDotDotLoc;
+ ExprResult RHS;
+ if (TryConsumeToken(tok::ellipsis, DotDotDotLoc)) {
+ Diag(DotDotDotLoc, diag::ext_gnu_case_range);
+ RHS = ParseCaseExpression(CaseLoc);
+ if (RHS.isInvalid()) {
+ if (!SkipUntil(tok::colon, tok::r_brace, StopAtSemi | StopBeforeMatch))
+ return StmtError();
+ }
+ }
+
+ ColonProtection.restore();
+
+ if (TryConsumeToken(tok::colon, ColonLoc)) {
+ } else if (TryConsumeToken(tok::semi, ColonLoc) ||
+ TryConsumeToken(tok::coloncolon, ColonLoc)) {
+ // Treat "case blah;" or "case blah::" as a typo for "case blah:".
+ Diag(ColonLoc, diag::err_expected_after)
+ << "'case'" << tok::colon
+ << FixItHint::CreateReplacement(ColonLoc, ":");
+ } else {
+ SourceLocation ExpectedLoc = PP.getLocForEndOfToken(PrevTokLocation);
+ Diag(ExpectedLoc, diag::err_expected_after)
+ << "'case'" << tok::colon
+ << FixItHint::CreateInsertion(ExpectedLoc, ":");
+ ColonLoc = ExpectedLoc;
+ }
+
+ StmtResult Case =
+ Actions.ActOnCaseStmt(CaseLoc, LHS, DotDotDotLoc, RHS, ColonLoc);
+
+ // If we had a sema error parsing this case, then just ignore it and
+ // continue parsing the sub-stmt.
+ if (Case.isInvalid()) {
+ if (TopLevelCase.isInvalid()) // No parsed case stmts.
+ return ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
+ // Otherwise, just don't add it as a nested case.
+ } else {
+ // If this is the first case statement we parsed, it becomes TopLevelCase.
+ // Otherwise we link it into the current chain.
+ Stmt *NextDeepest = Case.get();
+ if (TopLevelCase.isInvalid())
+ TopLevelCase = Case;
+ else
+ Actions.ActOnCaseStmtBody(DeepestParsedCaseStmt, Case.get());
+ DeepestParsedCaseStmt = NextDeepest;
+ }
+
+ // Handle all case statements.
+ } while (Tok.is(tok::kw_case));
+
+ // If we found a non-case statement, start by parsing it.
+ StmtResult SubStmt;
+
+ if (Tok.is(tok::r_brace)) {
+ // "switch (X) { case 4: }", is valid and is treated as if label was
+ // followed by a null statement.
+ DiagnoseLabelAtEndOfCompoundStatement();
+ SubStmt = Actions.ActOnNullStmt(ColonLoc);
+ } else {
+ SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
+ }
+
+ // Install the body into the most deeply-nested case.
+ if (DeepestParsedCaseStmt) {
+ // Broken sub-stmt shouldn't prevent forming the case statement properly.
+ if (SubStmt.isInvalid())
+ SubStmt = Actions.ActOnNullStmt(SourceLocation());
+ Actions.ActOnCaseStmtBody(DeepestParsedCaseStmt, SubStmt.get());
+ }
+
+ // Return the top level parsed statement tree.
+ return TopLevelCase;
+}
+
+/// ParseDefaultStatement
+/// labeled-statement:
+/// 'default' ':' statement
+/// Note that this does not parse the 'statement' at the end.
+///
+StmtResult Parser::ParseDefaultStatement(ParsedStmtContext StmtCtx) {
+ assert(Tok.is(tok::kw_default) && "Not a default stmt!");
+
+ // The substatement is always a 'statement', not a 'declaration', but is
+ // otherwise in the same context as the labeled-statement.
+ StmtCtx &= ~ParsedStmtContext::AllowDeclarationsInC;
+
+ SourceLocation DefaultLoc = ConsumeToken(); // eat the 'default'.
+
+ SourceLocation ColonLoc;
+ if (TryConsumeToken(tok::colon, ColonLoc)) {
+ } else if (TryConsumeToken(tok::semi, ColonLoc)) {
+ // Treat "default;" as a typo for "default:".
+ Diag(ColonLoc, diag::err_expected_after)
+ << "'default'" << tok::colon
+ << FixItHint::CreateReplacement(ColonLoc, ":");
+ } else {
+ SourceLocation ExpectedLoc = PP.getLocForEndOfToken(PrevTokLocation);
+ Diag(ExpectedLoc, diag::err_expected_after)
+ << "'default'" << tok::colon
+ << FixItHint::CreateInsertion(ExpectedLoc, ":");
+ ColonLoc = ExpectedLoc;
+ }
+
+ StmtResult SubStmt;
+
+ if (Tok.is(tok::r_brace)) {
+ // "switch (X) {... default: }", is valid and is treated as if label was
+ // followed by a null statement.
+ DiagnoseLabelAtEndOfCompoundStatement();
+ SubStmt = Actions.ActOnNullStmt(ColonLoc);
+ } else {
+ SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
+ }
+
+ // Broken sub-stmt shouldn't prevent forming the case statement properly.
+ if (SubStmt.isInvalid())
+ SubStmt = Actions.ActOnNullStmt(ColonLoc);
+
+ return Actions.ActOnDefaultStmt(DefaultLoc, ColonLoc,
+ SubStmt.get(), getCurScope());
+}
+
+StmtResult Parser::ParseCompoundStatement(bool isStmtExpr) {
+ return ParseCompoundStatement(isStmtExpr,
+ Scope::DeclScope | Scope::CompoundStmtScope);
+}
+
+/// ParseCompoundStatement - Parse a "{}" block.
+///
+/// compound-statement: [C99 6.8.2]
+/// { block-item-list[opt] }
+/// [GNU] { label-declarations block-item-list } [TODO]
+///
+/// block-item-list:
+/// block-item
+/// block-item-list block-item
+///
+/// block-item:
+/// declaration
+/// [GNU] '__extension__' declaration
+/// statement
+///
+/// [GNU] label-declarations:
+/// [GNU] label-declaration
+/// [GNU] label-declarations label-declaration
+///
+/// [GNU] label-declaration:
+/// [GNU] '__label__' identifier-list ';'
+///
+StmtResult Parser::ParseCompoundStatement(bool isStmtExpr,
+ unsigned ScopeFlags) {
+ assert(Tok.is(tok::l_brace) && "Not a compound stmt!");
+
+ // Enter a scope to hold everything within the compound stmt. Compound
+ // statements can always hold declarations.
+ ParseScope CompoundScope(this, ScopeFlags);
+
+ // Parse the statements in the body.
+ return ParseCompoundStatementBody(isStmtExpr);
+}
+
+/// Parse any pragmas at the start of the compound expression. We handle these
+/// separately since some pragmas (FP_CONTRACT) must appear before any C
+/// statement in the compound, but may be intermingled with other pragmas.
+void Parser::ParseCompoundStatementLeadingPragmas() {
+ bool checkForPragmas = true;
+ while (checkForPragmas) {
+ switch (Tok.getKind()) {
+ case tok::annot_pragma_vis:
+ HandlePragmaVisibility();
+ break;
+ case tok::annot_pragma_pack:
+ HandlePragmaPack();
+ break;
+ case tok::annot_pragma_msstruct:
+ HandlePragmaMSStruct();
+ break;
+ case tok::annot_pragma_align:
+ HandlePragmaAlign();
+ break;
+ case tok::annot_pragma_weak:
+ HandlePragmaWeak();
+ break;
+ case tok::annot_pragma_weakalias:
+ HandlePragmaWeakAlias();
+ break;
+ case tok::annot_pragma_redefine_extname:
+ HandlePragmaRedefineExtname();
+ break;
+ case tok::annot_pragma_opencl_extension:
+ HandlePragmaOpenCLExtension();
+ break;
+ case tok::annot_pragma_fp_contract:
+ HandlePragmaFPContract();
+ break;
+ case tok::annot_pragma_fp:
+ HandlePragmaFP();
+ break;
+ case tok::annot_pragma_fenv_access:
+ case tok::annot_pragma_fenv_access_ms:
+ HandlePragmaFEnvAccess();
+ break;
+ case tok::annot_pragma_fenv_round:
+ HandlePragmaFEnvRound();
+ break;
+ case tok::annot_pragma_float_control:
+ HandlePragmaFloatControl();
+ break;
+ case tok::annot_pragma_ms_pointers_to_members:
+ HandlePragmaMSPointersToMembers();
+ break;
+ case tok::annot_pragma_ms_pragma:
+ HandlePragmaMSPragma();
+ break;
+ case tok::annot_pragma_ms_vtordisp:
+ HandlePragmaMSVtorDisp();
+ break;
+ case tok::annot_pragma_dump:
+ HandlePragmaDump();
+ break;
+ default:
+ checkForPragmas = false;
+ break;
+ }
+ }
+
+}
+
+void Parser::DiagnoseLabelAtEndOfCompoundStatement() {
+ if (getLangOpts().CPlusPlus) {
+ Diag(Tok, getLangOpts().CPlusPlus2b
+ ? diag::warn_cxx20_compat_label_end_of_compound_statement
+ : diag::ext_cxx_label_end_of_compound_statement);
+ } else {
+ Diag(Tok, getLangOpts().C2x
+ ? diag::warn_c2x_compat_label_end_of_compound_statement
+ : diag::ext_c_label_end_of_compound_statement);
+ }
+}
+
+/// Consume any extra semi-colons resulting in null statements,
+/// returning true if any tok::semi were consumed.
+bool Parser::ConsumeNullStmt(StmtVector &Stmts) {
+ if (!Tok.is(tok::semi))
+ return false;
+
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc;
+
+ while (Tok.is(tok::semi) && !Tok.hasLeadingEmptyMacro() &&
+ Tok.getLocation().isValid() && !Tok.getLocation().isMacroID()) {
+ EndLoc = Tok.getLocation();
+
+ // Don't just ConsumeToken() this tok::semi, do store it in AST.
+ StmtResult R =
+ ParseStatementOrDeclaration(Stmts, ParsedStmtContext::SubStmt);
+ if (R.isUsable())
+ Stmts.push_back(R.get());
+ }
+
+ // Did not consume any extra semi.
+ if (EndLoc.isInvalid())
+ return false;
+
+ Diag(StartLoc, diag::warn_null_statement)
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+ return true;
+}
+
+StmtResult Parser::handleExprStmt(ExprResult E, ParsedStmtContext StmtCtx) {
+ bool IsStmtExprResult = false;
+ if ((StmtCtx & ParsedStmtContext::InStmtExpr) != ParsedStmtContext()) {
+ // For GCC compatibility we skip past NullStmts.
+ unsigned LookAhead = 0;
+ while (GetLookAheadToken(LookAhead).is(tok::semi)) {
+ ++LookAhead;
+ }
+ // Then look to see if the next two tokens close the statement expression;
+ // if so, this expression statement is the last statement in a statment
+ // expression.
+ IsStmtExprResult = GetLookAheadToken(LookAhead).is(tok::r_brace) &&
+ GetLookAheadToken(LookAhead + 1).is(tok::r_paren);
+ }
+
+ if (IsStmtExprResult)
+ E = Actions.ActOnStmtExprResult(E);
+ return Actions.ActOnExprStmt(E, /*DiscardedValue=*/!IsStmtExprResult);
+}
+
+/// ParseCompoundStatementBody - Parse a sequence of statements optionally
+/// followed by a label and invoke the ActOnCompoundStmt action. This expects
+/// the '{' to be the current token, and consume the '}' at the end of the
+/// block. It does not manipulate the scope stack.
+StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
+ PrettyStackTraceLoc CrashInfo(PP.getSourceManager(),
+ Tok.getLocation(),
+ "in compound statement ('{}')");
+
+ // Record the current FPFeatures, restore on leaving the
+ // compound statement.
+ Sema::FPFeaturesStateRAII SaveFPFeatures(Actions);
+
+ InMessageExpressionRAIIObject InMessage(*this, false);
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ if (T.consumeOpen())
+ return StmtError();
+
+ Sema::CompoundScopeRAII CompoundScope(Actions, isStmtExpr);
+
+ // Parse any pragmas at the beginning of the compound statement.
+ ParseCompoundStatementLeadingPragmas();
+ Actions.ActOnAfterCompoundStatementLeadingPragmas();
+
+ StmtVector Stmts;
+
+ // "__label__ X, Y, Z;" is the GNU "Local Label" extension. These are
+ // only allowed at the start of a compound stmt regardless of the language.
+ while (Tok.is(tok::kw___label__)) {
+ SourceLocation LabelLoc = ConsumeToken();
+
+ SmallVector<Decl *, 8> DeclsInGroup;
+ while (true) {
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ break;
+ }
+
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ SourceLocation IdLoc = ConsumeToken();
+ DeclsInGroup.push_back(Actions.LookupOrCreateLabel(II, IdLoc, LabelLoc));
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ DeclSpec DS(AttrFactory);
+ DeclGroupPtrTy Res =
+ Actions.FinalizeDeclaratorGroup(getCurScope(), DS, DeclsInGroup);
+ StmtResult R = Actions.ActOnDeclStmt(Res, LabelLoc, Tok.getLocation());
+
+ ExpectAndConsumeSemi(diag::err_expected_semi_declaration);
+ if (R.isUsable())
+ Stmts.push_back(R.get());
+ }
+
+ ParsedStmtContext SubStmtCtx =
+ ParsedStmtContext::Compound |
+ (isStmtExpr ? ParsedStmtContext::InStmtExpr : ParsedStmtContext());
+
+ while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
+ Tok.isNot(tok::eof)) {
+ if (Tok.is(tok::annot_pragma_unused)) {
+ HandlePragmaUnused();
+ continue;
+ }
+
+ if (ConsumeNullStmt(Stmts))
+ continue;
+
+ StmtResult R;
+ if (Tok.isNot(tok::kw___extension__)) {
+ R = ParseStatementOrDeclaration(Stmts, SubStmtCtx);
+ } else {
+ // __extension__ can start declarations and it can also be a unary
+ // operator for expressions. Consume multiple __extension__ markers here
+ // until we can determine which is which.
+ // FIXME: This loses extension expressions in the AST!
+ SourceLocation ExtLoc = ConsumeToken();
+ while (Tok.is(tok::kw___extension__))
+ ConsumeToken();
+
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseCXX11Attributes(attrs, /*MightBeObjCMessageSend*/ true);
+
+ // If this is the start of a declaration, parse it as such.
+ if (isDeclarationStatement()) {
+ // __extension__ silences extension warnings in the subdeclaration.
+ // FIXME: Save the __extension__ on the decl as a node somehow?
+ ExtensionRAIIObject O(Diags);
+
+ SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
+ ParsedAttributes DeclSpecAttrs(AttrFactory);
+ DeclGroupPtrTy Res = ParseDeclaration(DeclaratorContext::Block, DeclEnd,
+ attrs, DeclSpecAttrs);
+ R = Actions.ActOnDeclStmt(Res, DeclStart, DeclEnd);
+ } else {
+ // Otherwise this was a unary __extension__ marker.
+ ExprResult Res(ParseExpressionWithLeadingExtension(ExtLoc));
+
+ if (Res.isInvalid()) {
+ SkipUntil(tok::semi);
+ continue;
+ }
+
+ // Eat the semicolon at the end of stmt and convert the expr into a
+ // statement.
+ ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
+ R = handleExprStmt(Res, SubStmtCtx);
+ if (R.isUsable())
+ R = Actions.ActOnAttributedStmt(attrs, R.get());
+ }
+ }
+
+ if (R.isUsable())
+ Stmts.push_back(R.get());
+ }
+ // Warn the user that using option `-ffp-eval-method=source` on a
+ // 32-bit target and feature `sse` disabled, or using
+ // `pragma clang fp eval_method=source` and feature `sse` disabled, is not
+ // supported.
+ if (!PP.getTargetInfo().supportSourceEvalMethod() &&
+ (PP.getLastFPEvalPragmaLocation().isValid() ||
+ PP.getCurrentFPEvalMethod() ==
+ LangOptions::FPEvalMethodKind::FEM_Source))
+ Diag(Tok.getLocation(),
+ diag::warn_no_support_for_eval_method_source_on_m32);
+
+ SourceLocation CloseLoc = Tok.getLocation();
+
+ // We broke out of the while loop because we found a '}' or EOF.
+ if (!T.consumeClose()) {
+ // If this is the '})' of a statement expression, check that it's written
+ // in a sensible way.
+ if (isStmtExpr && Tok.is(tok::r_paren))
+ checkCompoundToken(CloseLoc, tok::r_brace, CompoundToken::StmtExprEnd);
+ } else {
+ // Recover by creating a compound statement with what we parsed so far,
+ // instead of dropping everything and returning StmtError().
+ }
+
+ if (T.getCloseLocation().isValid())
+ CloseLoc = T.getCloseLocation();
+
+ return Actions.ActOnCompoundStmt(T.getOpenLocation(), CloseLoc,
+ Stmts, isStmtExpr);
+}
+
+/// ParseParenExprOrCondition:
+/// [C ] '(' expression ')'
+/// [C++] '(' condition ')'
+/// [C++1z] '(' init-statement[opt] condition ')'
+///
+/// This function parses and performs error recovery on the specified condition
+/// or expression (depending on whether we're in C++ or C mode). This function
+/// goes out of its way to recover well. It returns true if there was a parser
+/// error (the right paren couldn't be found), which indicates that the caller
+/// should try to recover harder. It returns false if the condition is
+/// successfully parsed. Note that a successful parse can still have semantic
+/// errors in the condition.
+/// Additionally, it will assign the location of the outer-most '(' and ')',
+/// to LParenLoc and RParenLoc, respectively.
+bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
+ Sema::ConditionResult &Cond,
+ SourceLocation Loc,
+ Sema::ConditionKind CK,
+ SourceLocation &LParenLoc,
+ SourceLocation &RParenLoc) {
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ SourceLocation Start = Tok.getLocation();
+
+ if (getLangOpts().CPlusPlus) {
+ Cond = ParseCXXCondition(InitStmt, Loc, CK, false);
+ } else {
+ ExprResult CondExpr = ParseExpression();
+
+ // If required, convert to a boolean value.
+ if (CondExpr.isInvalid())
+ Cond = Sema::ConditionError();
+ else
+ Cond = Actions.ActOnCondition(getCurScope(), Loc, CondExpr.get(), CK,
+ /*MissingOK=*/false);
+ }
+
+ // If the parser was confused by the condition and we don't have a ')', try to
+ // recover by skipping ahead to a semi and bailing out. If condexp is
+ // semantically invalid but we have well formed code, keep going.
+ if (Cond.isInvalid() && Tok.isNot(tok::r_paren)) {
+ SkipUntil(tok::semi);
+ // Skipping may have stopped if it found the containing ')'. If so, we can
+ // continue parsing the if statement.
+ if (Tok.isNot(tok::r_paren))
+ return true;
+ }
+
+ if (Cond.isInvalid()) {
+ ExprResult CondExpr = Actions.CreateRecoveryExpr(
+ Start, Tok.getLocation() == Start ? Start : PrevTokLocation, {},
+ Actions.PreferredConditionType(CK));
+ if (!CondExpr.isInvalid())
+ Cond = Actions.ActOnCondition(getCurScope(), Loc, CondExpr.get(), CK,
+ /*MissingOK=*/false);
+ }
+
+ // Either the condition is valid or the rparen is present.
+ T.consumeClose();
+ LParenLoc = T.getOpenLocation();
+ RParenLoc = T.getCloseLocation();
+
+ // Check for extraneous ')'s to catch things like "if (foo())) {". We know
+ // that all callers are looking for a statement after the condition, so ")"
+ // isn't valid.
+ while (Tok.is(tok::r_paren)) {
+ Diag(Tok, diag::err_extraneous_rparen_in_condition)
+ << FixItHint::CreateRemoval(Tok.getLocation());
+ ConsumeParen();
+ }
+
+ return false;
+}
+
+namespace {
+
+enum MisleadingStatementKind { MSK_if, MSK_else, MSK_for, MSK_while };
+
+struct MisleadingIndentationChecker {
+ Parser &P;
+ SourceLocation StmtLoc;
+ SourceLocation PrevLoc;
+ unsigned NumDirectives;
+ MisleadingStatementKind Kind;
+ bool ShouldSkip;
+ MisleadingIndentationChecker(Parser &P, MisleadingStatementKind K,
+ SourceLocation SL)
+ : P(P), StmtLoc(SL), PrevLoc(P.getCurToken().getLocation()),
+ NumDirectives(P.getPreprocessor().getNumDirectives()), Kind(K),
+ ShouldSkip(P.getCurToken().is(tok::l_brace)) {
+ if (!P.MisleadingIndentationElseLoc.isInvalid()) {
+ StmtLoc = P.MisleadingIndentationElseLoc;
+ P.MisleadingIndentationElseLoc = SourceLocation();
+ }
+ if (Kind == MSK_else && !ShouldSkip)
+ P.MisleadingIndentationElseLoc = SL;
+ }
+
+ /// Compute the column number will aligning tabs on TabStop (-ftabstop), this
+ /// gives the visual indentation of the SourceLocation.
+ static unsigned getVisualIndentation(SourceManager &SM, SourceLocation Loc) {
+ unsigned TabStop = SM.getDiagnostics().getDiagnosticOptions().TabStop;
+
+ unsigned ColNo = SM.getSpellingColumnNumber(Loc);
+ if (ColNo == 0 || TabStop == 1)
+ return ColNo;
+
+ std::pair<FileID, unsigned> FIDAndOffset = SM.getDecomposedLoc(Loc);
+
+ bool Invalid;
+ StringRef BufData = SM.getBufferData(FIDAndOffset.first, &Invalid);
+ if (Invalid)
+ return 0;
+
+ const char *EndPos = BufData.data() + FIDAndOffset.second;
+ // FileOffset are 0-based and Column numbers are 1-based
+ assert(FIDAndOffset.second + 1 >= ColNo &&
+ "Column number smaller than file offset?");
+
+ unsigned VisualColumn = 0; // Stored as 0-based column, here.
+ // Loop from beginning of line up to Loc's file position, counting columns,
+ // expanding tabs.
+ for (const char *CurPos = EndPos - (ColNo - 1); CurPos != EndPos;
+ ++CurPos) {
+ if (*CurPos == '\t')
+ // Advance visual column to next tabstop.
+ VisualColumn += (TabStop - VisualColumn % TabStop);
+ else
+ VisualColumn++;
+ }
+ return VisualColumn + 1;
+ }
+
+ void Check() {
+ Token Tok = P.getCurToken();
+ if (P.getActions().getDiagnostics().isIgnored(
+ diag::warn_misleading_indentation, Tok.getLocation()) ||
+ ShouldSkip || NumDirectives != P.getPreprocessor().getNumDirectives() ||
+ Tok.isOneOf(tok::semi, tok::r_brace) || Tok.isAnnotation() ||
+ Tok.getLocation().isMacroID() || PrevLoc.isMacroID() ||
+ StmtLoc.isMacroID() ||
+ (Kind == MSK_else && P.MisleadingIndentationElseLoc.isInvalid())) {
+ P.MisleadingIndentationElseLoc = SourceLocation();
+ return;
+ }
+ if (Kind == MSK_else)
+ P.MisleadingIndentationElseLoc = SourceLocation();
+
+ SourceManager &SM = P.getPreprocessor().getSourceManager();
+ unsigned PrevColNum = getVisualIndentation(SM, PrevLoc);
+ unsigned CurColNum = getVisualIndentation(SM, Tok.getLocation());
+ unsigned StmtColNum = getVisualIndentation(SM, StmtLoc);
+
+ if (PrevColNum != 0 && CurColNum != 0 && StmtColNum != 0 &&
+ ((PrevColNum > StmtColNum && PrevColNum == CurColNum) ||
+ !Tok.isAtStartOfLine()) &&
+ SM.getPresumedLineNumber(StmtLoc) !=
+ SM.getPresumedLineNumber(Tok.getLocation()) &&
+ (Tok.isNot(tok::identifier) ||
+ P.getPreprocessor().LookAhead(0).isNot(tok::colon))) {
+ P.Diag(Tok.getLocation(), diag::warn_misleading_indentation) << Kind;
+ P.Diag(StmtLoc, diag::note_previous_statement);
+ }
+ }
+};
+
+}
+
+/// ParseIfStatement
+/// if-statement: [C99 6.8.4.1]
+/// 'if' '(' expression ')' statement
+/// 'if' '(' expression ')' statement 'else' statement
+/// [C++] 'if' '(' condition ')' statement
+/// [C++] 'if' '(' condition ')' statement 'else' statement
+/// [C++23] 'if' '!' [opt] consteval compound-statement
+/// [C++23] 'if' '!' [opt] consteval compound-statement 'else' statement
+///
+StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
+ assert(Tok.is(tok::kw_if) && "Not an if stmt!");
+ SourceLocation IfLoc = ConsumeToken(); // eat the 'if'.
+
+ bool IsConstexpr = false;
+ bool IsConsteval = false;
+ SourceLocation NotLocation;
+ SourceLocation ConstevalLoc;
+
+ if (Tok.is(tok::kw_constexpr)) {
+ Diag(Tok, getLangOpts().CPlusPlus17 ? diag::warn_cxx14_compat_constexpr_if
+ : diag::ext_constexpr_if);
+ IsConstexpr = true;
+ ConsumeToken();
+ } else {
+ if (Tok.is(tok::exclaim)) {
+ NotLocation = ConsumeToken();
+ }
+
+ if (Tok.is(tok::kw_consteval)) {
+ Diag(Tok, getLangOpts().CPlusPlus2b ? diag::warn_cxx20_compat_consteval_if
+ : diag::ext_consteval_if);
+ IsConsteval = true;
+ ConstevalLoc = ConsumeToken();
+ }
+ }
+ if (!IsConsteval && (NotLocation.isValid() || Tok.isNot(tok::l_paren))) {
+ Diag(Tok, diag::err_expected_lparen_after) << "if";
+ SkipUntil(tok::semi);
+ return StmtError();
+ }
+
+ bool C99orCXX = getLangOpts().C99 || getLangOpts().CPlusPlus;
+
+ // C99 6.8.4p3 - In C99, the if statement is a block. This is not
+ // the case for C90.
+ //
+ // C++ 6.4p3:
+ // A name introduced by a declaration in a condition is in scope from its
+ // point of declaration until the end of the substatements controlled by the
+ // condition.
+ // C++ 3.3.2p4:
+ // Names declared in the for-init-statement, and in the condition of if,
+ // while, for, and switch statements are local to the if, while, for, or
+ // switch statement (including the controlled statement).
+ //
+ ParseScope IfScope(this, Scope::DeclScope | Scope::ControlScope, C99orCXX);
+
+ // Parse the condition.
+ StmtResult InitStmt;
+ Sema::ConditionResult Cond;
+ SourceLocation LParen;
+ SourceLocation RParen;
+ std::optional<bool> ConstexprCondition;
+ if (!IsConsteval) {
+
+ if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc,
+ IsConstexpr ? Sema::ConditionKind::ConstexprIf
+ : Sema::ConditionKind::Boolean,
+ LParen, RParen))
+ return StmtError();
+
+ if (IsConstexpr)
+ ConstexprCondition = Cond.getKnownValue();
+ }
+
+ bool IsBracedThen = Tok.is(tok::l_brace);
+
+ // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if
+ // there is no compound stmt. C90 does not have this clause. We only do this
+ // if the body isn't a compound statement to avoid push/pop in common cases.
+ //
+ // C++ 6.4p1:
+ // The substatement in a selection-statement (each substatement, in the else
+ // form of the if statement) implicitly defines a local scope.
+ //
+ // For C++ we create a scope for the condition and a new scope for
+ // substatements because:
+ // -When the 'then' scope exits, we want the condition declaration to still be
+ // active for the 'else' scope too.
+ // -Sema will detect name clashes by considering declarations of a
+ // 'ControlScope' as part of its direct subscope.
+ // -If we wanted the condition and substatement to be in the same scope, we
+ // would have to notify ParseStatement not to create a new scope. It's
+ // simpler to let it create a new scope.
+ //
+ ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, IsBracedThen);
+
+ MisleadingIndentationChecker MIChecker(*this, MSK_if, IfLoc);
+
+ // Read the 'then' stmt.
+ SourceLocation ThenStmtLoc = Tok.getLocation();
+
+ SourceLocation InnerStatementTrailingElseLoc;
+ StmtResult ThenStmt;
+ {
+ bool ShouldEnter = ConstexprCondition && !*ConstexprCondition;
+ Sema::ExpressionEvaluationContext Context =
+ Sema::ExpressionEvaluationContext::DiscardedStatement;
+ if (NotLocation.isInvalid() && IsConsteval) {
+ Context = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+ ShouldEnter = true;
+ }
+
+ EnterExpressionEvaluationContext PotentiallyDiscarded(
+ Actions, Context, nullptr,
+ Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
+ ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc);
+ }
+
+ if (Tok.isNot(tok::kw_else))
+ MIChecker.Check();
+
+ // Pop the 'if' scope if needed.
+ InnerScope.Exit();
+
+ // If it has an else, parse it.
+ SourceLocation ElseLoc;
+ SourceLocation ElseStmtLoc;
+ StmtResult ElseStmt;
+
+ if (Tok.is(tok::kw_else)) {
+ if (TrailingElseLoc)
+ *TrailingElseLoc = Tok.getLocation();
+
+ ElseLoc = ConsumeToken();
+ ElseStmtLoc = Tok.getLocation();
+
+ // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if
+ // there is no compound stmt. C90 does not have this clause. We only do
+ // this if the body isn't a compound statement to avoid push/pop in common
+ // cases.
+ //
+ // C++ 6.4p1:
+ // The substatement in a selection-statement (each substatement, in the else
+ // form of the if statement) implicitly defines a local scope.
+ //
+ ParseScope InnerScope(this, Scope::DeclScope, C99orCXX,
+ Tok.is(tok::l_brace));
+
+ MisleadingIndentationChecker MIChecker(*this, MSK_else, ElseLoc);
+ bool ShouldEnter = ConstexprCondition && *ConstexprCondition;
+ Sema::ExpressionEvaluationContext Context =
+ Sema::ExpressionEvaluationContext::DiscardedStatement;
+ if (NotLocation.isValid() && IsConsteval) {
+ Context = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+ ShouldEnter = true;
+ }
+
+ EnterExpressionEvaluationContext PotentiallyDiscarded(
+ Actions, Context, nullptr,
+ Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
+ ElseStmt = ParseStatement();
+
+ if (ElseStmt.isUsable())
+ MIChecker.Check();
+
+ // Pop the 'else' scope if needed.
+ InnerScope.Exit();
+ } else if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteAfterIf(getCurScope(), IsBracedThen);
+ return StmtError();
+ } else if (InnerStatementTrailingElseLoc.isValid()) {
+ Diag(InnerStatementTrailingElseLoc, diag::warn_dangling_else);
+ }
+
+ IfScope.Exit();
+
+ // If the then or else stmt is invalid and the other is valid (and present),
+ // make turn the invalid one into a null stmt to avoid dropping the other
+ // part. If both are invalid, return error.
+ if ((ThenStmt.isInvalid() && ElseStmt.isInvalid()) ||
+ (ThenStmt.isInvalid() && ElseStmt.get() == nullptr) ||
+ (ThenStmt.get() == nullptr && ElseStmt.isInvalid())) {
+ // Both invalid, or one is invalid and other is non-present: return error.
+ return StmtError();
+ }
+
+ if (IsConsteval) {
+ auto IsCompoundStatement = [](const Stmt *S) {
+ if (const auto *Outer = dyn_cast_or_null<AttributedStmt>(S))
+ S = Outer->getSubStmt();
+ return isa_and_nonnull<clang::CompoundStmt>(S);
+ };
+
+ if (!IsCompoundStatement(ThenStmt.get())) {
+ Diag(ConstevalLoc, diag::err_expected_after) << "consteval"
+ << "{";
+ return StmtError();
+ }
+ if (!ElseStmt.isUnset() && !IsCompoundStatement(ElseStmt.get())) {
+ Diag(ElseLoc, diag::err_expected_after) << "else"
+ << "{";
+ return StmtError();
+ }
+ }
+
+ // Now if either are invalid, replace with a ';'.
+ if (ThenStmt.isInvalid())
+ ThenStmt = Actions.ActOnNullStmt(ThenStmtLoc);
+ if (ElseStmt.isInvalid())
+ ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc);
+
+ IfStatementKind Kind = IfStatementKind::Ordinary;
+ if (IsConstexpr)
+ Kind = IfStatementKind::Constexpr;
+ else if (IsConsteval)
+ Kind = NotLocation.isValid() ? IfStatementKind::ConstevalNegated
+ : IfStatementKind::ConstevalNonNegated;
+
+ return Actions.ActOnIfStmt(IfLoc, Kind, LParen, InitStmt.get(), Cond, RParen,
+ ThenStmt.get(), ElseLoc, ElseStmt.get());
+}
+
+/// ParseSwitchStatement
+/// switch-statement:
+/// 'switch' '(' expression ')' statement
+/// [C++] 'switch' '(' condition ')' statement
+StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) {
+ assert(Tok.is(tok::kw_switch) && "Not a switch stmt!");
+ SourceLocation SwitchLoc = ConsumeToken(); // eat the 'switch'.
+
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_expected_lparen_after) << "switch";
+ SkipUntil(tok::semi);
+ return StmtError();
+ }
+
+ bool C99orCXX = getLangOpts().C99 || getLangOpts().CPlusPlus;
+
+ // C99 6.8.4p3 - In C99, the switch statement is a block. This is
+ // not the case for C90. Start the switch scope.
+ //
+ // C++ 6.4p3:
+ // A name introduced by a declaration in a condition is in scope from its
+ // point of declaration until the end of the substatements controlled by the
+ // condition.
+ // C++ 3.3.2p4:
+ // Names declared in the for-init-statement, and in the condition of if,
+ // while, for, and switch statements are local to the if, while, for, or
+ // switch statement (including the controlled statement).
+ //
+ unsigned ScopeFlags = Scope::SwitchScope;
+ if (C99orCXX)
+ ScopeFlags |= Scope::DeclScope | Scope::ControlScope;
+ ParseScope SwitchScope(this, ScopeFlags);
+
+ // Parse the condition.
+ StmtResult InitStmt;
+ Sema::ConditionResult Cond;
+ SourceLocation LParen;
+ SourceLocation RParen;
+ if (ParseParenExprOrCondition(&InitStmt, Cond, SwitchLoc,
+ Sema::ConditionKind::Switch, LParen, RParen))
+ return StmtError();
+
+ StmtResult Switch = Actions.ActOnStartOfSwitchStmt(
+ SwitchLoc, LParen, InitStmt.get(), Cond, RParen);
+
+ if (Switch.isInvalid()) {
+ // Skip the switch body.
+ // FIXME: This is not optimal recovery, but parsing the body is more
+ // dangerous due to the presence of case and default statements, which
+ // will have no place to connect back with the switch.
+ if (Tok.is(tok::l_brace)) {
+ ConsumeBrace();
+ SkipUntil(tok::r_brace);
+ } else
+ SkipUntil(tok::semi);
+ return Switch;
+ }
+
+ // C99 6.8.4p3 - In C99, the body of the switch statement is a scope, even if
+ // there is no compound stmt. C90 does not have this clause. We only do this
+ // if the body isn't a compound statement to avoid push/pop in common cases.
+ //
+ // C++ 6.4p1:
+ // The substatement in a selection-statement (each substatement, in the else
+ // form of the if statement) implicitly defines a local scope.
+ //
+ // See comments in ParseIfStatement for why we create a scope for the
+ // condition and a new scope for substatement in C++.
+ //
+ getCurScope()->AddFlags(Scope::BreakScope);
+ ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));
+
+ // We have incremented the mangling number for the SwitchScope and the
+ // InnerScope, which is one too many.
+ if (C99orCXX)
+ getCurScope()->decrementMSManglingNumber();
+
+ // Read the body statement.
+ StmtResult Body(ParseStatement(TrailingElseLoc));
+
+ // Pop the scopes.
+ InnerScope.Exit();
+ SwitchScope.Exit();
+
+ return Actions.ActOnFinishSwitchStmt(SwitchLoc, Switch.get(), Body.get());
+}
+
+/// ParseWhileStatement
+/// while-statement: [C99 6.8.5.1]
+/// 'while' '(' expression ')' statement
+/// [C++] 'while' '(' condition ')' statement
+StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) {
+ assert(Tok.is(tok::kw_while) && "Not a while stmt!");
+ SourceLocation WhileLoc = Tok.getLocation();
+ ConsumeToken(); // eat the 'while'.
+
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_expected_lparen_after) << "while";
+ SkipUntil(tok::semi);
+ return StmtError();
+ }
+
+ bool C99orCXX = getLangOpts().C99 || getLangOpts().CPlusPlus;
+
+ // C99 6.8.5p5 - In C99, the while statement is a block. This is not
+ // the case for C90. Start the loop scope.
+ //
+ // C++ 6.4p3:
+ // A name introduced by a declaration in a condition is in scope from its
+ // point of declaration until the end of the substatements controlled by the
+ // condition.
+ // C++ 3.3.2p4:
+ // Names declared in the for-init-statement, and in the condition of if,
+ // while, for, and switch statements are local to the if, while, for, or
+ // switch statement (including the controlled statement).
+ //
+ unsigned ScopeFlags;
+ if (C99orCXX)
+ ScopeFlags = Scope::BreakScope | Scope::ContinueScope |
+ Scope::DeclScope | Scope::ControlScope;
+ else
+ ScopeFlags = Scope::BreakScope | Scope::ContinueScope;
+ ParseScope WhileScope(this, ScopeFlags);
+
+ // Parse the condition.
+ Sema::ConditionResult Cond;
+ SourceLocation LParen;
+ SourceLocation RParen;
+ if (ParseParenExprOrCondition(nullptr, Cond, WhileLoc,
+ Sema::ConditionKind::Boolean, LParen, RParen))
+ return StmtError();
+
+ // C99 6.8.5p5 - In C99, the body of the while statement is a scope, even if
+ // there is no compound stmt. C90 does not have this clause. We only do this
+ // if the body isn't a compound statement to avoid push/pop in common cases.
+ //
+ // C++ 6.5p2:
+ // The substatement in an iteration-statement implicitly defines a local scope
+ // which is entered and exited each time through the loop.
+ //
+ // See comments in ParseIfStatement for why we create a scope for the
+ // condition and a new scope for substatement in C++.
+ //
+ ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));
+
+ MisleadingIndentationChecker MIChecker(*this, MSK_while, WhileLoc);
+
+ // Read the body statement.
+ StmtResult Body(ParseStatement(TrailingElseLoc));
+
+ if (Body.isUsable())
+ MIChecker.Check();
+ // Pop the body scope if needed.
+ InnerScope.Exit();
+ WhileScope.Exit();
+
+ if (Cond.isInvalid() || Body.isInvalid())
+ return StmtError();
+
+ return Actions.ActOnWhileStmt(WhileLoc, LParen, Cond, RParen, Body.get());
+}
+
+/// ParseDoStatement
+/// do-statement: [C99 6.8.5.2]
+/// 'do' statement 'while' '(' expression ')' ';'
+/// Note: this lets the caller parse the end ';'.
+StmtResult Parser::ParseDoStatement() {
+ assert(Tok.is(tok::kw_do) && "Not a do stmt!");
+ SourceLocation DoLoc = ConsumeToken(); // eat the 'do'.
+
+ // C99 6.8.5p5 - In C99, the do statement is a block. This is not
+ // the case for C90. Start the loop scope.
+ unsigned ScopeFlags;
+ if (getLangOpts().C99)
+ ScopeFlags = Scope::BreakScope | Scope::ContinueScope | Scope::DeclScope;
+ else
+ ScopeFlags = Scope::BreakScope | Scope::ContinueScope;
+
+ ParseScope DoScope(this, ScopeFlags);
+
+ // C99 6.8.5p5 - In C99, the body of the do statement is a scope, even if
+ // there is no compound stmt. C90 does not have this clause. We only do this
+ // if the body isn't a compound statement to avoid push/pop in common cases.
+ //
+ // C++ 6.5p2:
+ // The substatement in an iteration-statement implicitly defines a local scope
+ // which is entered and exited each time through the loop.
+ //
+ bool C99orCXX = getLangOpts().C99 || getLangOpts().CPlusPlus;
+ ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));
+
+ // Read the body statement.
+ StmtResult Body(ParseStatement());
+
+ // Pop the body scope if needed.
+ InnerScope.Exit();
+
+ if (Tok.isNot(tok::kw_while)) {
+ if (!Body.isInvalid()) {
+ Diag(Tok, diag::err_expected_while);
+ Diag(DoLoc, diag::note_matching) << "'do'";
+ SkipUntil(tok::semi, StopBeforeMatch);
+ }
+ return StmtError();
+ }
+ SourceLocation WhileLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_expected_lparen_after) << "do/while";
+ SkipUntil(tok::semi, StopBeforeMatch);
+ return StmtError();
+ }
+
+ // Parse the parenthesized expression.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ // A do-while expression is not a condition, so can't have attributes.
+ DiagnoseAndSkipCXX11Attributes();
+
+ SourceLocation Start = Tok.getLocation();
+ ExprResult Cond = ParseExpression();
+ // Correct the typos in condition before closing the scope.
+ if (Cond.isUsable())
+ Cond = Actions.CorrectDelayedTyposInExpr(Cond);
+ else {
+ if (!Tok.isOneOf(tok::r_paren, tok::r_square, tok::r_brace))
+ SkipUntil(tok::semi);
+ Cond = Actions.CreateRecoveryExpr(
+ Start, Start == Tok.getLocation() ? Start : PrevTokLocation, {},
+ Actions.getASTContext().BoolTy);
+ }
+ T.consumeClose();
+ DoScope.Exit();
+
+ if (Cond.isInvalid() || Body.isInvalid())
+ return StmtError();
+
+ return Actions.ActOnDoStmt(DoLoc, Body.get(), WhileLoc, T.getOpenLocation(),
+ Cond.get(), T.getCloseLocation());
+}
+
+bool Parser::isForRangeIdentifier() {
+ assert(Tok.is(tok::identifier));
+
+ const Token &Next = NextToken();
+ if (Next.is(tok::colon))
+ return true;
+
+ if (Next.isOneOf(tok::l_square, tok::kw_alignas)) {
+ TentativeParsingAction PA(*this);
+ ConsumeToken();
+ SkipCXX11Attributes();
+ bool Result = Tok.is(tok::colon);
+ PA.Revert();
+ return Result;
+ }
+
+ return false;
+}
+
+/// ParseForStatement
+/// for-statement: [C99 6.8.5.3]
+/// 'for' '(' expr[opt] ';' expr[opt] ';' expr[opt] ')' statement
+/// 'for' '(' declaration expr[opt] ';' expr[opt] ')' statement
+/// [C++] 'for' '(' for-init-statement condition[opt] ';' expression[opt] ')'
+/// [C++] statement
+/// [C++0x] 'for'
+/// 'co_await'[opt] [Coroutines]
+/// '(' for-range-declaration ':' for-range-initializer ')'
+/// statement
+/// [OBJC2] 'for' '(' declaration 'in' expr ')' statement
+/// [OBJC2] 'for' '(' expr 'in' expr ')' statement
+///
+/// [C++] for-init-statement:
+/// [C++] expression-statement
+/// [C++] simple-declaration
+/// [C++2b] alias-declaration
+///
+/// [C++0x] for-range-declaration:
+/// [C++0x] attribute-specifier-seq[opt] type-specifier-seq declarator
+/// [C++0x] for-range-initializer:
+/// [C++0x] expression
+/// [C++0x] braced-init-list [TODO]
+StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
+ assert(Tok.is(tok::kw_for) && "Not a for stmt!");
+ SourceLocation ForLoc = ConsumeToken(); // eat the 'for'.
+
+ SourceLocation CoawaitLoc;
+ if (Tok.is(tok::kw_co_await))
+ CoawaitLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_expected_lparen_after) << "for";
+ SkipUntil(tok::semi);
+ return StmtError();
+ }
+
+ bool C99orCXXorObjC = getLangOpts().C99 || getLangOpts().CPlusPlus ||
+ getLangOpts().ObjC;
+
+ // C99 6.8.5p5 - In C99, the for statement is a block. This is not
+ // the case for C90. Start the loop scope.
+ //
+ // C++ 6.4p3:
+ // A name introduced by a declaration in a condition is in scope from its
+ // point of declaration until the end of the substatements controlled by the
+ // condition.
+ // C++ 3.3.2p4:
+ // Names declared in the for-init-statement, and in the condition of if,
+ // while, for, and switch statements are local to the if, while, for, or
+ // switch statement (including the controlled statement).
+ // C++ 6.5.3p1:
+ // Names declared in the for-init-statement are in the same declarative-region
+ // as those declared in the condition.
+ //
+ unsigned ScopeFlags = 0;
+ if (C99orCXXorObjC)
+ ScopeFlags = Scope::DeclScope | Scope::ControlScope;
+
+ ParseScope ForScope(this, ScopeFlags);
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ ExprResult Value;
+
+ bool ForEach = false;
+ StmtResult FirstPart;
+ Sema::ConditionResult SecondPart;
+ ExprResult Collection;
+ ForRangeInfo ForRangeInfo;
+ FullExprArg ThirdPart(Actions);
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(),
+ C99orCXXorObjC? Sema::PCC_ForInit
+ : Sema::PCC_Expression);
+ return StmtError();
+ }
+
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseCXX11Attributes(attrs);
+
+ SourceLocation EmptyInitStmtSemiLoc;
+
+ // Parse the first part of the for specifier.
+ if (Tok.is(tok::semi)) { // for (;
+ ProhibitAttributes(attrs);
+ // no first part, eat the ';'.
+ SourceLocation SemiLoc = Tok.getLocation();
+ if (!Tok.hasLeadingEmptyMacro() && !SemiLoc.isMacroID())
+ EmptyInitStmtSemiLoc = SemiLoc;
+ ConsumeToken();
+ } else if (getLangOpts().CPlusPlus && Tok.is(tok::identifier) &&
+ isForRangeIdentifier()) {
+ ProhibitAttributes(attrs);
+ IdentifierInfo *Name = Tok.getIdentifierInfo();
+ SourceLocation Loc = ConsumeToken();
+ MaybeParseCXX11Attributes(attrs);
+
+ ForRangeInfo.ColonLoc = ConsumeToken();
+ if (Tok.is(tok::l_brace))
+ ForRangeInfo.RangeExpr = ParseBraceInitializer();
+ else
+ ForRangeInfo.RangeExpr = ParseExpression();
+
+ Diag(Loc, diag::err_for_range_identifier)
+ << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17)
+ ? FixItHint::CreateInsertion(Loc, "auto &&")
+ : FixItHint());
+
+ ForRangeInfo.LoopVar =
+ Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs);
+ } else if (isForInitDeclaration()) { // for (int X = 4;
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ // Parse declaration, which eats the ';'.
+ if (!C99orCXXorObjC) { // Use of C99-style for loops in C90 mode?
+ Diag(Tok, diag::ext_c99_variable_decl_in_for_loop);
+ Diag(Tok, diag::warn_gcc_variable_decl_in_for_loop);
+ }
+ DeclGroupPtrTy DG;
+ if (Tok.is(tok::kw_using)) {
+ DG = ParseAliasDeclarationInInitStatement(DeclaratorContext::ForInit,
+ attrs);
+ } else {
+ // In C++0x, "for (T NS:a" might not be a typo for ::
+ bool MightBeForRangeStmt = getLangOpts().CPlusPlus;
+ ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt);
+
+ SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
+ ParsedAttributes DeclSpecAttrs(AttrFactory);
+ DG = ParseSimpleDeclaration(
+ DeclaratorContext::ForInit, DeclEnd, attrs, DeclSpecAttrs, false,
+ MightBeForRangeStmt ? &ForRangeInfo : nullptr);
+ FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation());
+ if (ForRangeInfo.ParsedForRangeDecl()) {
+ Diag(ForRangeInfo.ColonLoc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_for_range
+ : diag::ext_for_range);
+ ForRangeInfo.LoopVar = FirstPart;
+ FirstPart = StmtResult();
+ } else if (Tok.is(tok::semi)) { // for (int x = 4;
+ ConsumeToken();
+ } else if ((ForEach = isTokIdentifier_in())) {
+ Actions.ActOnForEachDeclStmt(DG);
+ // ObjC: for (id x in expr)
+ ConsumeToken(); // consume 'in'
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCForCollection(getCurScope(), DG);
+ return StmtError();
+ }
+ Collection = ParseExpression();
+ } else {
+ Diag(Tok, diag::err_expected_semi_for);
+ }
+ }
+ } else {
+ ProhibitAttributes(attrs);
+ Value = Actions.CorrectDelayedTyposInExpr(ParseExpression());
+
+ ForEach = isTokIdentifier_in();
+
+ // Turn the expression into a stmt.
+ if (!Value.isInvalid()) {
+ if (ForEach)
+ FirstPart = Actions.ActOnForEachLValueExpr(Value.get());
+ else {
+ // We already know this is not an init-statement within a for loop, so
+ // if we are parsing a C++11 range-based for loop, we should treat this
+ // expression statement as being a discarded value expression because
+ // we will err below. This way we do not warn on an unused expression
+ // that was an error in the first place, like with: for (expr : expr);
+ bool IsRangeBasedFor =
+ getLangOpts().CPlusPlus11 && !ForEach && Tok.is(tok::colon);
+ FirstPart = Actions.ActOnExprStmt(Value, !IsRangeBasedFor);
+ }
+ }
+
+ if (Tok.is(tok::semi)) {
+ ConsumeToken();
+ } else if (ForEach) {
+ ConsumeToken(); // consume 'in'
+
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteObjCForCollection(getCurScope(), nullptr);
+ return StmtError();
+ }
+ Collection = ParseExpression();
+ } else if (getLangOpts().CPlusPlus11 && Tok.is(tok::colon) && FirstPart.get()) {
+ // User tried to write the reasonable, but ill-formed, for-range-statement
+ // for (expr : expr) { ... }
+ Diag(Tok, diag::err_for_range_expected_decl)
+ << FirstPart.get()->getSourceRange();
+ SkipUntil(tok::r_paren, StopBeforeMatch);
+ SecondPart = Sema::ConditionError();
+ } else {
+ if (!Value.isInvalid()) {
+ Diag(Tok, diag::err_expected_semi_for);
+ } else {
+ // Skip until semicolon or rparen, don't consume it.
+ SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ }
+ }
+ }
+
+ // Parse the second part of the for specifier.
+ if (!ForEach && !ForRangeInfo.ParsedForRangeDecl() &&
+ !SecondPart.isInvalid()) {
+ // Parse the second part of the for specifier.
+ if (Tok.is(tok::semi)) { // for (...;;
+ // no second part.
+ } else if (Tok.is(tok::r_paren)) {
+ // missing both semicolons.
+ } else {
+ if (getLangOpts().CPlusPlus) {
+ // C++2a: We've parsed an init-statement; we might have a
+ // for-range-declaration next.
+ bool MightBeForRangeStmt = !ForRangeInfo.ParsedForRangeDecl();
+ ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt);
+ SecondPart = ParseCXXCondition(
+ nullptr, ForLoc, Sema::ConditionKind::Boolean,
+ // FIXME: recovery if we don't see another semi!
+ /*MissingOK=*/true, MightBeForRangeStmt ? &ForRangeInfo : nullptr,
+ /*EnterForConditionScope*/ true);
+
+ if (ForRangeInfo.ParsedForRangeDecl()) {
+ Diag(FirstPart.get() ? FirstPart.get()->getBeginLoc()
+ : ForRangeInfo.ColonLoc,
+ getLangOpts().CPlusPlus20
+ ? diag::warn_cxx17_compat_for_range_init_stmt
+ : diag::ext_for_range_init_stmt)
+ << (FirstPart.get() ? FirstPart.get()->getSourceRange()
+ : SourceRange());
+ if (EmptyInitStmtSemiLoc.isValid()) {
+ Diag(EmptyInitStmtSemiLoc, diag::warn_empty_init_statement)
+ << /*for-loop*/ 2
+ << FixItHint::CreateRemoval(EmptyInitStmtSemiLoc);
+ }
+ }
+ } else {
+ // We permit 'continue' and 'break' in the condition of a for loop.
+ getCurScope()->AddFlags(Scope::BreakScope | Scope::ContinueScope);
+
+ ExprResult SecondExpr = ParseExpression();
+ if (SecondExpr.isInvalid())
+ SecondPart = Sema::ConditionError();
+ else
+ SecondPart = Actions.ActOnCondition(
+ getCurScope(), ForLoc, SecondExpr.get(),
+ Sema::ConditionKind::Boolean, /*MissingOK=*/true);
+ }
+ }
+ }
+
+ // Enter a break / continue scope, if we didn't already enter one while
+ // parsing the second part.
+ if (!getCurScope()->isContinueScope())
+ getCurScope()->AddFlags(Scope::BreakScope | Scope::ContinueScope);
+
+ // Parse the third part of the for statement.
+ if (!ForEach && !ForRangeInfo.ParsedForRangeDecl()) {
+ if (Tok.isNot(tok::semi)) {
+ if (!SecondPart.isInvalid())
+ Diag(Tok, diag::err_expected_semi_for);
+ else
+ // Skip until semicolon or rparen, don't consume it.
+ SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
+ }
+
+ if (Tok.is(tok::semi)) {
+ ConsumeToken();
+ }
+
+ if (Tok.isNot(tok::r_paren)) { // for (...;...;)
+ ExprResult Third = ParseExpression();
+ // FIXME: The C++11 standard doesn't actually say that this is a
+ // discarded-value expression, but it clearly should be.
+ ThirdPart = Actions.MakeFullDiscardedValueExpr(Third.get());
+ }
+ }
+ // Match the ')'.
+ T.consumeClose();
+
+ // C++ Coroutines [stmt.iter]:
+ // 'co_await' can only be used for a range-based for statement.
+ if (CoawaitLoc.isValid() && !ForRangeInfo.ParsedForRangeDecl()) {
+ Diag(CoawaitLoc, diag::err_for_co_await_not_range_for);
+ CoawaitLoc = SourceLocation();
+ }
+
+ if (CoawaitLoc.isValid() && getLangOpts().CPlusPlus20)
+ Diag(CoawaitLoc, diag::warn_deprecated_for_co_await);
+
+ // We need to perform most of the semantic analysis for a C++0x for-range
+ // statememt before parsing the body, in order to be able to deduce the type
+ // of an auto-typed loop variable.
+ StmtResult ForRangeStmt;
+ StmtResult ForEachStmt;
+
+ if (ForRangeInfo.ParsedForRangeDecl()) {
+ ExprResult CorrectedRange =
+ Actions.CorrectDelayedTyposInExpr(ForRangeInfo.RangeExpr.get());
+ ForRangeStmt = Actions.ActOnCXXForRangeStmt(
+ getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(),
+ ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc, CorrectedRange.get(),
+ T.getCloseLocation(), Sema::BFRK_Build);
+
+ // Similarly, we need to do the semantic analysis for a for-range
+ // statement immediately in order to close over temporaries correctly.
+ } else if (ForEach) {
+ ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc,
+ FirstPart.get(),
+ Collection.get(),
+ T.getCloseLocation());
+ } else {
+ // In OpenMP loop region loop control variable must be captured and be
+ // private. Perform analysis of first part (if any).
+ if (getLangOpts().OpenMP && FirstPart.isUsable()) {
+ Actions.ActOnOpenMPLoopInitialization(ForLoc, FirstPart.get());
+ }
+ }
+
+ // C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
+ // there is no compound stmt. C90 does not have this clause. We only do this
+ // if the body isn't a compound statement to avoid push/pop in common cases.
+ //
+ // C++ 6.5p2:
+ // The substatement in an iteration-statement implicitly defines a local scope
+ // which is entered and exited each time through the loop.
+ //
+ // See comments in ParseIfStatement for why we create a scope for
+ // for-init-statement/condition and a new scope for substatement in C++.
+ //
+ ParseScope InnerScope(this, Scope::DeclScope, C99orCXXorObjC,
+ Tok.is(tok::l_brace));
+
+ // The body of the for loop has the same local mangling number as the
+ // for-init-statement.
+ // It will only be incremented if the body contains other things that would
+ // normally increment the mangling number (like a compound statement).
+ if (C99orCXXorObjC)
+ getCurScope()->decrementMSManglingNumber();
+
+ MisleadingIndentationChecker MIChecker(*this, MSK_for, ForLoc);
+
+ // Read the body statement.
+ StmtResult Body(ParseStatement(TrailingElseLoc));
+
+ if (Body.isUsable())
+ MIChecker.Check();
+
+ // Pop the body scope if needed.
+ InnerScope.Exit();
+
+ // Leave the for-scope.
+ ForScope.Exit();
+
+ if (Body.isInvalid())
+ return StmtError();
+
+ if (ForEach)
+ return Actions.FinishObjCForCollectionStmt(ForEachStmt.get(),
+ Body.get());
+
+ if (ForRangeInfo.ParsedForRangeDecl())
+ return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get());
+
+ return Actions.ActOnForStmt(ForLoc, T.getOpenLocation(), FirstPart.get(),
+ SecondPart, ThirdPart, T.getCloseLocation(),
+ Body.get());
+}
+
+/// ParseGotoStatement
+/// jump-statement:
+/// 'goto' identifier ';'
+/// [GNU] 'goto' '*' expression ';'
+///
+/// Note: this lets the caller parse the end ';'.
+///
+StmtResult Parser::ParseGotoStatement() {
+ assert(Tok.is(tok::kw_goto) && "Not a goto stmt!");
+ SourceLocation GotoLoc = ConsumeToken(); // eat the 'goto'.
+
+ StmtResult Res;
+ if (Tok.is(tok::identifier)) {
+ LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(),
+ Tok.getLocation());
+ Res = Actions.ActOnGotoStmt(GotoLoc, Tok.getLocation(), LD);
+ ConsumeToken();
+ } else if (Tok.is(tok::star)) {
+ // GNU indirect goto extension.
+ Diag(Tok, diag::ext_gnu_indirect_goto);
+ SourceLocation StarLoc = ConsumeToken();
+ ExprResult R(ParseExpression());
+ if (R.isInvalid()) { // Skip to the semicolon, but don't consume it.
+ SkipUntil(tok::semi, StopBeforeMatch);
+ return StmtError();
+ }
+ Res = Actions.ActOnIndirectGotoStmt(GotoLoc, StarLoc, R.get());
+ } else {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ return StmtError();
+ }
+
+ return Res;
+}
+
+/// ParseContinueStatement
+/// jump-statement:
+/// 'continue' ';'
+///
+/// Note: this lets the caller parse the end ';'.
+///
+StmtResult Parser::ParseContinueStatement() {
+ SourceLocation ContinueLoc = ConsumeToken(); // eat the 'continue'.
+ return Actions.ActOnContinueStmt(ContinueLoc, getCurScope());
+}
+
+/// ParseBreakStatement
+/// jump-statement:
+/// 'break' ';'
+///
+/// Note: this lets the caller parse the end ';'.
+///
+StmtResult Parser::ParseBreakStatement() {
+ SourceLocation BreakLoc = ConsumeToken(); // eat the 'break'.
+ return Actions.ActOnBreakStmt(BreakLoc, getCurScope());
+}
+
+/// ParseReturnStatement
+/// jump-statement:
+/// 'return' expression[opt] ';'
+/// 'return' braced-init-list ';'
+/// 'co_return' expression[opt] ';'
+/// 'co_return' braced-init-list ';'
+StmtResult Parser::ParseReturnStatement() {
+ assert((Tok.is(tok::kw_return) || Tok.is(tok::kw_co_return)) &&
+ "Not a return stmt!");
+ bool IsCoreturn = Tok.is(tok::kw_co_return);
+ SourceLocation ReturnLoc = ConsumeToken(); // eat the 'return'.
+
+ ExprResult R;
+ if (Tok.isNot(tok::semi)) {
+ if (!IsCoreturn)
+ PreferredType.enterReturn(Actions, Tok.getLocation());
+ // FIXME: Code completion for co_return.
+ if (Tok.is(tok::code_completion) && !IsCoreturn) {
+ cutOffParsing();
+ Actions.CodeCompleteExpression(getCurScope(),
+ PreferredType.get(Tok.getLocation()));
+ return StmtError();
+ }
+
+ if (Tok.is(tok::l_brace) && getLangOpts().CPlusPlus) {
+ R = ParseInitializer();
+ if (R.isUsable())
+ Diag(R.get()->getBeginLoc(),
+ getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_generalized_initializer_lists
+ : diag::ext_generalized_initializer_lists)
+ << R.get()->getSourceRange();
+ } else
+ R = ParseExpression();
+ if (R.isInvalid()) {
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ return StmtError();
+ }
+ }
+ if (IsCoreturn)
+ return Actions.ActOnCoreturnStmt(getCurScope(), ReturnLoc, R.get());
+ return Actions.ActOnReturnStmt(ReturnLoc, R.get(), getCurScope());
+}
+
+StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts,
+ ParsedStmtContext StmtCtx,
+ SourceLocation *TrailingElseLoc,
+ ParsedAttributes &Attrs) {
+ // Create temporary attribute list.
+ ParsedAttributes TempAttrs(AttrFactory);
+
+ SourceLocation StartLoc = Tok.getLocation();
+
+ // Get loop hints and consume annotated token.
+ while (Tok.is(tok::annot_pragma_loop_hint)) {
+ LoopHint Hint;
+ if (!HandlePragmaLoopHint(Hint))
+ continue;
+
+ ArgsUnion ArgHints[] = {Hint.PragmaNameLoc, Hint.OptionLoc, Hint.StateLoc,
+ ArgsUnion(Hint.ValueExpr)};
+ TempAttrs.addNew(Hint.PragmaNameLoc->Ident, Hint.Range, nullptr,
+ Hint.PragmaNameLoc->Loc, ArgHints, 4,
+ ParsedAttr::AS_Pragma);
+ }
+
+ // Get the next statement.
+ MaybeParseCXX11Attributes(Attrs);
+
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+ StmtResult S = ParseStatementOrDeclarationAfterAttributes(
+ Stmts, StmtCtx, TrailingElseLoc, Attrs, EmptyDeclSpecAttrs);
+
+ Attrs.takeAllFrom(TempAttrs);
+
+ // Start of attribute range may already be set for some invalid input.
+ // See PR46336.
+ if (Attrs.Range.getBegin().isInvalid())
+ Attrs.Range.setBegin(StartLoc);
+
+ return S;
+}
+
+Decl *Parser::ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope) {
+ assert(Tok.is(tok::l_brace));
+ SourceLocation LBraceLoc = Tok.getLocation();
+
+ PrettyDeclStackTraceEntry CrashInfo(Actions.Context, Decl, LBraceLoc,
+ "parsing function body");
+
+ // Save and reset current vtordisp stack if we have entered a C++ method body.
+ bool IsCXXMethod =
+ getLangOpts().CPlusPlus && Decl && isa<CXXMethodDecl>(Decl);
+ Sema::PragmaStackSentinelRAII
+ PragmaStackSentinel(Actions, "InternalPragmaState", IsCXXMethod);
+
+ // Do not enter a scope for the brace, as the arguments are in the same scope
+ // (the function body) as the body itself. Instead, just read the statement
+ // list and put it into a CompoundStmt for safe keeping.
+ StmtResult FnBody(ParseCompoundStatementBody());
+
+ // If the function body could not be parsed, make a bogus compoundstmt.
+ if (FnBody.isInvalid()) {
+ Sema::CompoundScopeRAII CompoundScope(Actions);
+ FnBody =
+ Actions.ActOnCompoundStmt(LBraceLoc, LBraceLoc, std::nullopt, false);
+ }
+
+ BodyScope.Exit();
+ return Actions.ActOnFinishFunctionBody(Decl, FnBody.get());
+}
+
+/// ParseFunctionTryBlock - Parse a C++ function-try-block.
+///
+/// function-try-block:
+/// 'try' ctor-initializer[opt] compound-statement handler-seq
+///
+Decl *Parser::ParseFunctionTryBlock(Decl *Decl, ParseScope &BodyScope) {
+ assert(Tok.is(tok::kw_try) && "Expected 'try'");
+ SourceLocation TryLoc = ConsumeToken();
+
+ PrettyDeclStackTraceEntry CrashInfo(Actions.Context, Decl, TryLoc,
+ "parsing function try block");
+
+ // Constructor initializer list?
+ if (Tok.is(tok::colon))
+ ParseConstructorInitializer(Decl);
+ else
+ Actions.ActOnDefaultCtorInitializers(Decl);
+
+ // Save and reset current vtordisp stack if we have entered a C++ method body.
+ bool IsCXXMethod =
+ getLangOpts().CPlusPlus && Decl && isa<CXXMethodDecl>(Decl);
+ Sema::PragmaStackSentinelRAII
+ PragmaStackSentinel(Actions, "InternalPragmaState", IsCXXMethod);
+
+ SourceLocation LBraceLoc = Tok.getLocation();
+ StmtResult FnBody(ParseCXXTryBlockCommon(TryLoc, /*FnTry*/true));
+ // If we failed to parse the try-catch, we just give the function an empty
+ // compound statement as the body.
+ if (FnBody.isInvalid()) {
+ Sema::CompoundScopeRAII CompoundScope(Actions);
+ FnBody =
+ Actions.ActOnCompoundStmt(LBraceLoc, LBraceLoc, std::nullopt, false);
+ }
+
+ BodyScope.Exit();
+ return Actions.ActOnFinishFunctionBody(Decl, FnBody.get());
+}
+
+bool Parser::trySkippingFunctionBody() {
+ assert(SkipFunctionBodies &&
+ "Should only be called when SkipFunctionBodies is enabled");
+ if (!PP.isCodeCompletionEnabled()) {
+ SkipFunctionBody();
+ return true;
+ }
+
+ // We're in code-completion mode. Skip parsing for all function bodies unless
+ // the body contains the code-completion point.
+ TentativeParsingAction PA(*this);
+ bool IsTryCatch = Tok.is(tok::kw_try);
+ CachedTokens Toks;
+ bool ErrorInPrologue = ConsumeAndStoreFunctionPrologue(Toks);
+ if (llvm::any_of(Toks, [](const Token &Tok) {
+ return Tok.is(tok::code_completion);
+ })) {
+ PA.Revert();
+ return false;
+ }
+ if (ErrorInPrologue) {
+ PA.Commit();
+ SkipMalformedDecl();
+ return true;
+ }
+ if (!SkipUntil(tok::r_brace, StopAtCodeCompletion)) {
+ PA.Revert();
+ return false;
+ }
+ while (IsTryCatch && Tok.is(tok::kw_catch)) {
+ if (!SkipUntil(tok::l_brace, StopAtCodeCompletion) ||
+ !SkipUntil(tok::r_brace, StopAtCodeCompletion)) {
+ PA.Revert();
+ return false;
+ }
+ }
+ PA.Commit();
+ return true;
+}
+
+/// ParseCXXTryBlock - Parse a C++ try-block.
+///
+/// try-block:
+/// 'try' compound-statement handler-seq
+///
+StmtResult Parser::ParseCXXTryBlock() {
+ assert(Tok.is(tok::kw_try) && "Expected 'try'");
+
+ SourceLocation TryLoc = ConsumeToken();
+ return ParseCXXTryBlockCommon(TryLoc);
+}
+
+/// ParseCXXTryBlockCommon - Parse the common part of try-block and
+/// function-try-block.
+///
+/// try-block:
+/// 'try' compound-statement handler-seq
+///
+/// function-try-block:
+/// 'try' ctor-initializer[opt] compound-statement handler-seq
+///
+/// handler-seq:
+/// handler handler-seq[opt]
+///
+/// [Borland] try-block:
+/// 'try' compound-statement seh-except-block
+/// 'try' compound-statement seh-finally-block
+///
+StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc, bool FnTry) {
+ if (Tok.isNot(tok::l_brace))
+ return StmtError(Diag(Tok, diag::err_expected) << tok::l_brace);
+
+ StmtResult TryBlock(ParseCompoundStatement(
+ /*isStmtExpr=*/false, Scope::DeclScope | Scope::TryScope |
+ Scope::CompoundStmtScope |
+ (FnTry ? Scope::FnTryCatchScope : 0)));
+ if (TryBlock.isInvalid())
+ return TryBlock;
+
+ // Borland allows SEH-handlers with 'try'
+
+ if ((Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo() == getSEHExceptKeyword()) ||
+ Tok.is(tok::kw___finally)) {
+ // TODO: Factor into common return ParseSEHHandlerCommon(...)
+ StmtResult Handler;
+ if(Tok.getIdentifierInfo() == getSEHExceptKeyword()) {
+ SourceLocation Loc = ConsumeToken();
+ Handler = ParseSEHExceptBlock(Loc);
+ }
+ else {
+ SourceLocation Loc = ConsumeToken();
+ Handler = ParseSEHFinallyBlock(Loc);
+ }
+ if(Handler.isInvalid())
+ return Handler;
+
+ return Actions.ActOnSEHTryBlock(true /* IsCXXTry */,
+ TryLoc,
+ TryBlock.get(),
+ Handler.get());
+ }
+ else {
+ StmtVector Handlers;
+
+ // C++11 attributes can't appear here, despite this context seeming
+ // statement-like.
+ DiagnoseAndSkipCXX11Attributes();
+
+ if (Tok.isNot(tok::kw_catch))
+ return StmtError(Diag(Tok, diag::err_expected_catch));
+ while (Tok.is(tok::kw_catch)) {
+ StmtResult Handler(ParseCXXCatchBlock(FnTry));
+ if (!Handler.isInvalid())
+ Handlers.push_back(Handler.get());
+ }
+ // Don't bother creating the full statement if we don't have any usable
+ // handlers.
+ if (Handlers.empty())
+ return StmtError();
+
+ return Actions.ActOnCXXTryBlock(TryLoc, TryBlock.get(), Handlers);
+ }
+}
+
+/// ParseCXXCatchBlock - Parse a C++ catch block, called handler in the standard
+///
+/// handler:
+/// 'catch' '(' exception-declaration ')' compound-statement
+///
+/// exception-declaration:
+/// attribute-specifier-seq[opt] type-specifier-seq declarator
+/// attribute-specifier-seq[opt] type-specifier-seq abstract-declarator[opt]
+/// '...'
+///
+StmtResult Parser::ParseCXXCatchBlock(bool FnCatch) {
+ assert(Tok.is(tok::kw_catch) && "Expected 'catch'");
+
+ SourceLocation CatchLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume())
+ return StmtError();
+
+ // C++ 3.3.2p3:
+ // The name in a catch exception-declaration is local to the handler and
+ // shall not be redeclared in the outermost block of the handler.
+ ParseScope CatchScope(this, Scope::DeclScope | Scope::ControlScope |
+ Scope::CatchScope |
+ (FnCatch ? Scope::FnTryCatchScope : 0));
+
+ // exception-declaration is equivalent to '...' or a parameter-declaration
+ // without default arguments.
+ Decl *ExceptionDecl = nullptr;
+ if (Tok.isNot(tok::ellipsis)) {
+ ParsedAttributes Attributes(AttrFactory);
+ MaybeParseCXX11Attributes(Attributes);
+
+ DeclSpec DS(AttrFactory);
+
+ if (ParseCXXTypeSpecifierSeq(DS))
+ return StmtError();
+
+ Declarator ExDecl(DS, Attributes, DeclaratorContext::CXXCatch);
+ ParseDeclarator(ExDecl);
+ ExceptionDecl = Actions.ActOnExceptionDeclarator(getCurScope(), ExDecl);
+ } else
+ ConsumeToken();
+
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid())
+ return StmtError();
+
+ if (Tok.isNot(tok::l_brace))
+ return StmtError(Diag(Tok, diag::err_expected) << tok::l_brace);
+
+ // FIXME: Possible draft standard bug: attribute-specifier should be allowed?
+ StmtResult Block(ParseCompoundStatement());
+ if (Block.isInvalid())
+ return Block;
+
+ return Actions.ActOnCXXCatchBlock(CatchLoc, ExceptionDecl, Block.get());
+}
+
+void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) {
+ IfExistsCondition Result;
+ if (ParseMicrosoftIfExistsCondition(Result))
+ return;
+
+ // Handle dependent statements by parsing the braces as a compound statement.
+ // This is not the same behavior as Visual C++, which don't treat this as a
+ // compound statement, but for Clang's type checking we can't have anything
+ // inside these braces escaping to the surrounding code.
+ if (Result.Behavior == IEB_Dependent) {
+ if (!Tok.is(tok::l_brace)) {
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return;
+ }
+
+ StmtResult Compound = ParseCompoundStatement();
+ if (Compound.isInvalid())
+ return;
+
+ StmtResult DepResult = Actions.ActOnMSDependentExistsStmt(Result.KeywordLoc,
+ Result.IsIfExists,
+ Result.SS,
+ Result.Name,
+ Compound.get());
+ if (DepResult.isUsable())
+ Stmts.push_back(DepResult.get());
+ return;
+ }
+
+ BalancedDelimiterTracker Braces(*this, tok::l_brace);
+ if (Braces.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return;
+ }
+
+ switch (Result.Behavior) {
+ case IEB_Parse:
+ // Parse the statements below.
+ break;
+
+ case IEB_Dependent:
+ llvm_unreachable("Dependent case handled above");
+
+ case IEB_Skip:
+ Braces.skipToEnd();
+ return;
+ }
+
+ // Condition is true, parse the statements.
+ while (Tok.isNot(tok::r_brace)) {
+ StmtResult R =
+ ParseStatementOrDeclaration(Stmts, ParsedStmtContext::Compound);
+ if (R.isUsable())
+ Stmts.push_back(R.get());
+ }
+ Braces.consumeClose();
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseStmtAsm.cpp b/contrib/libs/clang16/lib/Parse/ParseStmtAsm.cpp
new file mode 100644
index 0000000000..04c3a8700c
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseStmtAsm.cpp
@@ -0,0 +1,963 @@
+//===---- ParseStmtAsm.cpp - Assembly Statement Parser --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements parsing for GCC and Microsoft inline assembly.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCParser/MCAsmParser.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+using namespace clang;
+
+namespace {
+class ClangAsmParserCallback : public llvm::MCAsmParserSemaCallback {
+ Parser &TheParser;
+ SourceLocation AsmLoc;
+ StringRef AsmString;
+
+ /// The tokens we streamed into AsmString and handed off to MC.
+ ArrayRef<Token> AsmToks;
+
+ /// The offset of each token in AsmToks within AsmString.
+ ArrayRef<unsigned> AsmTokOffsets;
+
+public:
+ ClangAsmParserCallback(Parser &P, SourceLocation Loc, StringRef AsmString,
+ ArrayRef<Token> Toks, ArrayRef<unsigned> Offsets)
+ : TheParser(P), AsmLoc(Loc), AsmString(AsmString), AsmToks(Toks),
+ AsmTokOffsets(Offsets) {
+ assert(AsmToks.size() == AsmTokOffsets.size());
+ }
+
+ void LookupInlineAsmIdentifier(StringRef &LineBuf,
+ llvm::InlineAsmIdentifierInfo &Info,
+ bool IsUnevaluatedContext) override;
+
+ StringRef LookupInlineAsmLabel(StringRef Identifier, llvm::SourceMgr &LSM,
+ llvm::SMLoc Location,
+ bool Create) override;
+
+ bool LookupInlineAsmField(StringRef Base, StringRef Member,
+ unsigned &Offset) override {
+ return TheParser.getActions().LookupInlineAsmField(Base, Member, Offset,
+ AsmLoc);
+ }
+
+ static void DiagHandlerCallback(const llvm::SMDiagnostic &D, void *Context) {
+ ((ClangAsmParserCallback *)Context)->handleDiagnostic(D);
+ }
+
+private:
+ /// Collect the appropriate tokens for the given string.
+ void findTokensForString(StringRef Str, SmallVectorImpl<Token> &TempToks,
+ const Token *&FirstOrigToken) const;
+
+ SourceLocation translateLocation(const llvm::SourceMgr &LSM,
+ llvm::SMLoc SMLoc);
+
+ void handleDiagnostic(const llvm::SMDiagnostic &D);
+};
+}
+
+void ClangAsmParserCallback::LookupInlineAsmIdentifier(
+ StringRef &LineBuf, llvm::InlineAsmIdentifierInfo &Info,
+ bool IsUnevaluatedContext) {
+ // Collect the desired tokens.
+ SmallVector<Token, 16> LineToks;
+ const Token *FirstOrigToken = nullptr;
+ findTokensForString(LineBuf, LineToks, FirstOrigToken);
+
+ unsigned NumConsumedToks;
+ ExprResult Result = TheParser.ParseMSAsmIdentifier(LineToks, NumConsumedToks,
+ IsUnevaluatedContext);
+
+ // If we consumed the entire line, tell MC that.
+ // Also do this if we consumed nothing as a way of reporting failure.
+ if (NumConsumedToks == 0 || NumConsumedToks == LineToks.size()) {
+ // By not modifying LineBuf, we're implicitly consuming it all.
+
+ // Otherwise, consume up to the original tokens.
+ } else {
+ assert(FirstOrigToken && "not using original tokens?");
+
+ // Since we're using original tokens, apply that offset.
+ assert(FirstOrigToken[NumConsumedToks].getLocation() ==
+ LineToks[NumConsumedToks].getLocation());
+ unsigned FirstIndex = FirstOrigToken - AsmToks.begin();
+ unsigned LastIndex = FirstIndex + NumConsumedToks - 1;
+
+ // The total length we've consumed is the relative offset
+ // of the last token we consumed plus its length.
+ unsigned TotalOffset =
+ (AsmTokOffsets[LastIndex] + AsmToks[LastIndex].getLength() -
+ AsmTokOffsets[FirstIndex]);
+ LineBuf = LineBuf.substr(0, TotalOffset);
+ }
+
+ // Initialize Info with the lookup result.
+ if (!Result.isUsable())
+ return;
+ TheParser.getActions().FillInlineAsmIdentifierInfo(Result.get(), Info);
+}
+
+StringRef ClangAsmParserCallback::LookupInlineAsmLabel(StringRef Identifier,
+ llvm::SourceMgr &LSM,
+ llvm::SMLoc Location,
+ bool Create) {
+ SourceLocation Loc = translateLocation(LSM, Location);
+ LabelDecl *Label =
+ TheParser.getActions().GetOrCreateMSAsmLabel(Identifier, Loc, Create);
+ return Label->getMSAsmLabel();
+}
+
+void ClangAsmParserCallback::findTokensForString(
+ StringRef Str, SmallVectorImpl<Token> &TempToks,
+ const Token *&FirstOrigToken) const {
+ // For now, assert that the string we're working with is a substring
+ // of what we gave to MC. This lets us use the original tokens.
+ assert(!std::less<const char *>()(Str.begin(), AsmString.begin()) &&
+ !std::less<const char *>()(AsmString.end(), Str.end()));
+
+ // Try to find a token whose offset matches the first token.
+ unsigned FirstCharOffset = Str.begin() - AsmString.begin();
+ const unsigned *FirstTokOffset =
+ llvm::lower_bound(AsmTokOffsets, FirstCharOffset);
+
+ // For now, assert that the start of the string exactly
+ // corresponds to the start of a token.
+ assert(*FirstTokOffset == FirstCharOffset);
+
+ // Use all the original tokens for this line. (We assume the
+ // end of the line corresponds cleanly to a token break.)
+ unsigned FirstTokIndex = FirstTokOffset - AsmTokOffsets.begin();
+ FirstOrigToken = &AsmToks[FirstTokIndex];
+ unsigned LastCharOffset = Str.end() - AsmString.begin();
+ for (unsigned i = FirstTokIndex, e = AsmTokOffsets.size(); i != e; ++i) {
+ if (AsmTokOffsets[i] >= LastCharOffset)
+ break;
+ TempToks.push_back(AsmToks[i]);
+ }
+}
+
+SourceLocation
+ClangAsmParserCallback::translateLocation(const llvm::SourceMgr &LSM,
+ llvm::SMLoc SMLoc) {
+ // Compute an offset into the inline asm buffer.
+ // FIXME: This isn't right if .macro is involved (but hopefully, no
+ // real-world code does that).
+ const llvm::MemoryBuffer *LBuf =
+ LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(SMLoc));
+ unsigned Offset = SMLoc.getPointer() - LBuf->getBufferStart();
+
+ // Figure out which token that offset points into.
+ const unsigned *TokOffsetPtr = llvm::lower_bound(AsmTokOffsets, Offset);
+ unsigned TokIndex = TokOffsetPtr - AsmTokOffsets.begin();
+ unsigned TokOffset = *TokOffsetPtr;
+
+ // If we come up with an answer which seems sane, use it; otherwise,
+ // just point at the __asm keyword.
+ // FIXME: Assert the answer is sane once we handle .macro correctly.
+ SourceLocation Loc = AsmLoc;
+ if (TokIndex < AsmToks.size()) {
+ const Token &Tok = AsmToks[TokIndex];
+ Loc = Tok.getLocation();
+ Loc = Loc.getLocWithOffset(Offset - TokOffset);
+ }
+ return Loc;
+}
+
+void ClangAsmParserCallback::handleDiagnostic(const llvm::SMDiagnostic &D) {
+ const llvm::SourceMgr &LSM = *D.getSourceMgr();
+ SourceLocation Loc = translateLocation(LSM, D.getLoc());
+ TheParser.Diag(Loc, diag::err_inline_ms_asm_parsing) << D.getMessage();
+}
+
+/// Parse an identifier in an MS-style inline assembly block.
+ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,
+ unsigned &NumLineToksConsumed,
+ bool IsUnevaluatedContext) {
+ // Push a fake token on the end so that we don't overrun the token
+ // stream. We use ';' because it expression-parsing should never
+ // overrun it.
+ const tok::TokenKind EndOfStream = tok::semi;
+ Token EndOfStreamTok;
+ EndOfStreamTok.startToken();
+ EndOfStreamTok.setKind(EndOfStream);
+ LineToks.push_back(EndOfStreamTok);
+
+ // Also copy the current token over.
+ LineToks.push_back(Tok);
+
+ PP.EnterTokenStream(LineToks, /*DisableMacroExpansions*/ true,
+ /*IsReinject*/ true);
+
+ // Clear the current token and advance to the first token in LineToks.
+ ConsumeAnyToken();
+
+ // Parse an optional scope-specifier if we're in C++.
+ CXXScopeSpec SS;
+ if (getLangOpts().CPlusPlus)
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false);
+
+ // Require an identifier here.
+ SourceLocation TemplateKWLoc;
+ UnqualifiedId Id;
+ bool Invalid = true;
+ ExprResult Result;
+ if (Tok.is(tok::kw_this)) {
+ Result = ParseCXXThis();
+ Invalid = false;
+ } else {
+ Invalid =
+ ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false,
+ /*AllowDestructorName=*/false,
+ /*AllowConstructorName=*/false,
+ /*AllowDeductionGuide=*/false, &TemplateKWLoc, Id);
+ // Perform the lookup.
+ Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id,
+ IsUnevaluatedContext);
+ }
+ // While the next two tokens are 'period' 'identifier', repeatedly parse it as
+ // a field access. We have to avoid consuming assembler directives that look
+ // like '.' 'else'.
+ while (Result.isUsable() && Tok.is(tok::period)) {
+ Token IdTok = PP.LookAhead(0);
+ if (IdTok.isNot(tok::identifier))
+ break;
+ ConsumeToken(); // Consume the period.
+ IdentifierInfo *Id = Tok.getIdentifierInfo();
+ ConsumeToken(); // Consume the identifier.
+ Result = Actions.LookupInlineAsmVarDeclField(Result.get(), Id->getName(),
+ Tok.getLocation());
+ }
+
+ // Figure out how many tokens we are into LineToks.
+ unsigned LineIndex = 0;
+ if (Tok.is(EndOfStream)) {
+ LineIndex = LineToks.size() - 2;
+ } else {
+ while (LineToks[LineIndex].getLocation() != Tok.getLocation()) {
+ LineIndex++;
+ assert(LineIndex < LineToks.size() - 2); // we added two extra tokens
+ }
+ }
+
+ // If we've run into the poison token we inserted before, or there
+ // was a parsing error, then claim the entire line.
+ if (Invalid || Tok.is(EndOfStream)) {
+ NumLineToksConsumed = LineToks.size() - 2;
+ } else {
+ // Otherwise, claim up to the start of the next token.
+ NumLineToksConsumed = LineIndex;
+ }
+
+ // Finally, restore the old parsing state by consuming all the tokens we
+ // staged before, implicitly killing off the token-lexer we pushed.
+ for (unsigned i = 0, e = LineToks.size() - LineIndex - 2; i != e; ++i) {
+ ConsumeAnyToken();
+ }
+ assert(Tok.is(EndOfStream));
+ ConsumeToken();
+
+ // Leave LineToks in its original state.
+ LineToks.pop_back();
+ LineToks.pop_back();
+
+ return Result;
+}
+
+/// Turn a sequence of our tokens back into a string that we can hand
+/// to the MC asm parser.
+static bool buildMSAsmString(Preprocessor &PP, SourceLocation AsmLoc,
+ ArrayRef<Token> AsmToks,
+ SmallVectorImpl<unsigned> &TokOffsets,
+ SmallString<512> &Asm) {
+ assert(!AsmToks.empty() && "Didn't expect an empty AsmToks!");
+
+ // Is this the start of a new assembly statement?
+ bool isNewStatement = true;
+
+ for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) {
+ const Token &Tok = AsmToks[i];
+
+ // Start each new statement with a newline and a tab.
+ if (!isNewStatement && (Tok.is(tok::kw_asm) || Tok.isAtStartOfLine())) {
+ Asm += "\n\t";
+ isNewStatement = true;
+ }
+
+ // Preserve the existence of leading whitespace except at the
+ // start of a statement.
+ if (!isNewStatement && Tok.hasLeadingSpace())
+ Asm += ' ';
+
+ // Remember the offset of this token.
+ TokOffsets.push_back(Asm.size());
+
+ // Don't actually write '__asm' into the assembly stream.
+ if (Tok.is(tok::kw_asm)) {
+ // Complain about __asm at the end of the stream.
+ if (i + 1 == e) {
+ PP.Diag(AsmLoc, diag::err_asm_empty);
+ return true;
+ }
+
+ continue;
+ }
+
+ // Append the spelling of the token.
+ SmallString<32> SpellingBuffer;
+ bool SpellingInvalid = false;
+ Asm += PP.getSpelling(Tok, SpellingBuffer, &SpellingInvalid);
+ assert(!SpellingInvalid && "spelling was invalid after correct parse?");
+
+ // We are no longer at the start of a statement.
+ isNewStatement = false;
+ }
+
+ // Ensure that the buffer is null-terminated.
+ Asm.push_back('\0');
+ Asm.pop_back();
+
+ assert(TokOffsets.size() == AsmToks.size());
+ return false;
+}
+
+// Determine if this is a GCC-style asm statement.
+bool Parser::isGCCAsmStatement(const Token &TokAfterAsm) const {
+ return TokAfterAsm.is(tok::l_paren) || isGNUAsmQualifier(TokAfterAsm);
+}
+
+bool Parser::isGNUAsmQualifier(const Token &TokAfterAsm) const {
+ return getGNUAsmQualifier(TokAfterAsm) != GNUAsmQualifiers::AQ_unspecified;
+}
+
+/// ParseMicrosoftAsmStatement. When -fms-extensions/-fasm-blocks is enabled,
+/// this routine is called to collect the tokens for an MS asm statement.
+///
+/// [MS] ms-asm-statement:
+/// ms-asm-block
+/// ms-asm-block ms-asm-statement
+///
+/// [MS] ms-asm-block:
+/// '__asm' ms-asm-line '\n'
+/// '__asm' '{' ms-asm-instruction-block[opt] '}' ';'[opt]
+///
+/// [MS] ms-asm-instruction-block
+/// ms-asm-line
+/// ms-asm-line '\n' ms-asm-instruction-block
+///
+StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {
+ SourceManager &SrcMgr = PP.getSourceManager();
+ SourceLocation EndLoc = AsmLoc;
+ SmallVector<Token, 4> AsmToks;
+
+ bool SingleLineMode = true;
+ unsigned BraceNesting = 0;
+ unsigned short savedBraceCount = BraceCount;
+ bool InAsmComment = false;
+ FileID FID;
+ unsigned LineNo = 0;
+ unsigned NumTokensRead = 0;
+ SmallVector<SourceLocation, 4> LBraceLocs;
+ bool SkippedStartOfLine = false;
+
+ if (Tok.is(tok::l_brace)) {
+ // Braced inline asm: consume the opening brace.
+ SingleLineMode = false;
+ BraceNesting = 1;
+ EndLoc = ConsumeBrace();
+ LBraceLocs.push_back(EndLoc);
+ ++NumTokensRead;
+ } else {
+ // Single-line inline asm; compute which line it is on.
+ std::pair<FileID, unsigned> ExpAsmLoc =
+ SrcMgr.getDecomposedExpansionLoc(EndLoc);
+ FID = ExpAsmLoc.first;
+ LineNo = SrcMgr.getLineNumber(FID, ExpAsmLoc.second);
+ LBraceLocs.push_back(SourceLocation());
+ }
+
+ SourceLocation TokLoc = Tok.getLocation();
+ do {
+ // If we hit EOF, we're done, period.
+ if (isEofOrEom())
+ break;
+
+ if (!InAsmComment && Tok.is(tok::l_brace)) {
+ // Consume the opening brace.
+ SkippedStartOfLine = Tok.isAtStartOfLine();
+ AsmToks.push_back(Tok);
+ EndLoc = ConsumeBrace();
+ BraceNesting++;
+ LBraceLocs.push_back(EndLoc);
+ TokLoc = Tok.getLocation();
+ ++NumTokensRead;
+ continue;
+ } else if (!InAsmComment && Tok.is(tok::semi)) {
+ // A semicolon in an asm is the start of a comment.
+ InAsmComment = true;
+ if (!SingleLineMode) {
+ // Compute which line the comment is on.
+ std::pair<FileID, unsigned> ExpSemiLoc =
+ SrcMgr.getDecomposedExpansionLoc(TokLoc);
+ FID = ExpSemiLoc.first;
+ LineNo = SrcMgr.getLineNumber(FID, ExpSemiLoc.second);
+ }
+ } else if (SingleLineMode || InAsmComment) {
+ // If end-of-line is significant, check whether this token is on a
+ // new line.
+ std::pair<FileID, unsigned> ExpLoc =
+ SrcMgr.getDecomposedExpansionLoc(TokLoc);
+ if (ExpLoc.first != FID ||
+ SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second) != LineNo) {
+ // If this is a single-line __asm, we're done, except if the next
+ // line is MS-style asm too, in which case we finish a comment
+ // if needed and then keep processing the next line as a single
+ // line __asm.
+ bool isAsm = Tok.is(tok::kw_asm);
+ if (SingleLineMode && (!isAsm || isGCCAsmStatement(NextToken())))
+ break;
+ // We're no longer in a comment.
+ InAsmComment = false;
+ if (isAsm) {
+ // If this is a new __asm {} block we want to process it separately
+ // from the single-line __asm statements
+ if (PP.LookAhead(0).is(tok::l_brace))
+ break;
+ LineNo = SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second);
+ SkippedStartOfLine = Tok.isAtStartOfLine();
+ } else if (Tok.is(tok::semi)) {
+ // A multi-line asm-statement, where next line is a comment
+ InAsmComment = true;
+ FID = ExpLoc.first;
+ LineNo = SrcMgr.getLineNumber(FID, ExpLoc.second);
+ }
+ } else if (!InAsmComment && Tok.is(tok::r_brace)) {
+ // In MSVC mode, braces only participate in brace matching and
+ // separating the asm statements. This is an intentional
+ // departure from the Apple gcc behavior.
+ if (!BraceNesting)
+ break;
+ }
+ }
+ if (!InAsmComment && BraceNesting && Tok.is(tok::r_brace) &&
+ BraceCount == (savedBraceCount + BraceNesting)) {
+ // Consume the closing brace.
+ SkippedStartOfLine = Tok.isAtStartOfLine();
+ // Don't want to add the closing brace of the whole asm block
+ if (SingleLineMode || BraceNesting > 1) {
+ Tok.clearFlag(Token::LeadingSpace);
+ AsmToks.push_back(Tok);
+ }
+ EndLoc = ConsumeBrace();
+ BraceNesting--;
+ // Finish if all of the opened braces in the inline asm section were
+ // consumed.
+ if (BraceNesting == 0 && !SingleLineMode)
+ break;
+ else {
+ LBraceLocs.pop_back();
+ TokLoc = Tok.getLocation();
+ ++NumTokensRead;
+ continue;
+ }
+ }
+
+ // Consume the next token; make sure we don't modify the brace count etc.
+ // if we are in a comment.
+ EndLoc = TokLoc;
+ if (InAsmComment)
+ PP.Lex(Tok);
+ else {
+ // Set the token as the start of line if we skipped the original start
+ // of line token in case it was a nested brace.
+ if (SkippedStartOfLine)
+ Tok.setFlag(Token::StartOfLine);
+ AsmToks.push_back(Tok);
+ ConsumeAnyToken();
+ }
+ TokLoc = Tok.getLocation();
+ ++NumTokensRead;
+ SkippedStartOfLine = false;
+ } while (true);
+
+ if (BraceNesting && BraceCount != savedBraceCount) {
+ // __asm without closing brace (this can happen at EOF).
+ for (unsigned i = 0; i < BraceNesting; ++i) {
+ Diag(Tok, diag::err_expected) << tok::r_brace;
+ Diag(LBraceLocs.back(), diag::note_matching) << tok::l_brace;
+ LBraceLocs.pop_back();
+ }
+ return StmtError();
+ } else if (NumTokensRead == 0) {
+ // Empty __asm.
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return StmtError();
+ }
+
+ // Okay, prepare to use MC to parse the assembly.
+ SmallVector<StringRef, 4> ConstraintRefs;
+ SmallVector<Expr *, 4> Exprs;
+ SmallVector<StringRef, 4> ClobberRefs;
+
+ // We need an actual supported target.
+ const llvm::Triple &TheTriple = Actions.Context.getTargetInfo().getTriple();
+ const std::string &TT = TheTriple.getTriple();
+ const llvm::Target *TheTarget = nullptr;
+ if (!TheTriple.isX86()) {
+ Diag(AsmLoc, diag::err_msasm_unsupported_arch) << TheTriple.getArchName();
+ } else {
+ std::string Error;
+ TheTarget = llvm::TargetRegistry::lookupTarget(TT, Error);
+ if (!TheTarget)
+ Diag(AsmLoc, diag::err_msasm_unable_to_create_target) << Error;
+ }
+
+ assert(!LBraceLocs.empty() && "Should have at least one location here");
+
+ SmallString<512> AsmString;
+ auto EmptyStmt = [&] {
+ return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmString,
+ /*NumOutputs*/ 0, /*NumInputs*/ 0,
+ ConstraintRefs, ClobberRefs, Exprs, EndLoc);
+ };
+ // If we don't support assembly, or the assembly is empty, we don't
+ // need to instantiate the AsmParser, etc.
+ if (!TheTarget || AsmToks.empty()) {
+ return EmptyStmt();
+ }
+
+ // Expand the tokens into a string buffer.
+ SmallVector<unsigned, 8> TokOffsets;
+ if (buildMSAsmString(PP, AsmLoc, AsmToks, TokOffsets, AsmString))
+ return StmtError();
+
+ const TargetOptions &TO = Actions.Context.getTargetInfo().getTargetOpts();
+ std::string FeaturesStr =
+ llvm::join(TO.Features.begin(), TO.Features.end(), ",");
+
+ std::unique_ptr<llvm::MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT));
+ if (!MRI) {
+ Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
+ << "target MC unavailable";
+ return EmptyStmt();
+ }
+ // FIXME: init MCOptions from sanitizer flags here.
+ llvm::MCTargetOptions MCOptions;
+ std::unique_ptr<llvm::MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TT, MCOptions));
+ // Get the instruction descriptor.
+ std::unique_ptr<llvm::MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ std::unique_ptr<llvm::MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TT, TO.CPU, FeaturesStr));
+ // Target MCTargetDesc may not be linked in clang-based tools.
+
+ if (!MAI || !MII || !STI) {
+ Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
+ << "target MC unavailable";
+ return EmptyStmt();
+ }
+
+ llvm::SourceMgr TempSrcMgr;
+ llvm::MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &TempSrcMgr);
+ std::unique_ptr<llvm::MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
+ Ctx.setObjectFileInfo(MOFI.get());
+
+ std::unique_ptr<llvm::MemoryBuffer> Buffer =
+ llvm::MemoryBuffer::getMemBuffer(AsmString, "<MS inline asm>");
+
+ // Tell SrcMgr about this buffer, which is what the parser will pick up.
+ TempSrcMgr.AddNewSourceBuffer(std::move(Buffer), llvm::SMLoc());
+
+ std::unique_ptr<llvm::MCStreamer> Str(createNullStreamer(Ctx));
+ std::unique_ptr<llvm::MCAsmParser> Parser(
+ createMCAsmParser(TempSrcMgr, Ctx, *Str.get(), *MAI));
+
+ std::unique_ptr<llvm::MCTargetAsmParser> TargetParser(
+ TheTarget->createMCAsmParser(*STI, *Parser, *MII, MCOptions));
+ // Target AsmParser may not be linked in clang-based tools.
+ if (!TargetParser) {
+ Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
+ << "target ASM parser unavailable";
+ return EmptyStmt();
+ }
+
+ std::unique_ptr<llvm::MCInstPrinter> IP(
+ TheTarget->createMCInstPrinter(llvm::Triple(TT), 1, *MAI, *MII, *MRI));
+
+ // Change to the Intel dialect.
+ Parser->setAssemblerDialect(1);
+ Parser->setTargetParser(*TargetParser.get());
+ Parser->setParsingMSInlineAsm(true);
+ TargetParser->setParsingMSInlineAsm(true);
+
+ ClangAsmParserCallback Callback(*this, AsmLoc, AsmString, AsmToks,
+ TokOffsets);
+ TargetParser->setSemaCallback(&Callback);
+ TempSrcMgr.setDiagHandler(ClangAsmParserCallback::DiagHandlerCallback,
+ &Callback);
+
+ unsigned NumOutputs;
+ unsigned NumInputs;
+ std::string AsmStringIR;
+ SmallVector<std::pair<void *, bool>, 4> OpExprs;
+ SmallVector<std::string, 4> Constraints;
+ SmallVector<std::string, 4> Clobbers;
+ if (Parser->parseMSInlineAsm(AsmStringIR, NumOutputs, NumInputs, OpExprs,
+ Constraints, Clobbers, MII.get(), IP.get(),
+ Callback))
+ return StmtError();
+
+ // Filter out "fpsw" and "mxcsr". They aren't valid GCC asm clobber
+ // constraints. Clang always adds fpsr to the clobber list anyway.
+ llvm::erase_if(Clobbers, [](const std::string &C) {
+ return C == "fpsr" || C == "mxcsr";
+ });
+
+ // Build the vector of clobber StringRefs.
+ ClobberRefs.insert(ClobberRefs.end(), Clobbers.begin(), Clobbers.end());
+
+ // Recast the void pointers and build the vector of constraint StringRefs.
+ unsigned NumExprs = NumOutputs + NumInputs;
+ ConstraintRefs.resize(NumExprs);
+ Exprs.resize(NumExprs);
+ for (unsigned i = 0, e = NumExprs; i != e; ++i) {
+ Expr *OpExpr = static_cast<Expr *>(OpExprs[i].first);
+ if (!OpExpr)
+ return StmtError();
+
+ // Need address of variable.
+ if (OpExprs[i].second)
+ OpExpr =
+ Actions.BuildUnaryOp(getCurScope(), AsmLoc, UO_AddrOf, OpExpr).get();
+
+ ConstraintRefs[i] = StringRef(Constraints[i]);
+ Exprs[i] = OpExpr;
+ }
+
+ // FIXME: We should be passing source locations for better diagnostics.
+ return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmStringIR,
+ NumOutputs, NumInputs, ConstraintRefs,
+ ClobberRefs, Exprs, EndLoc);
+}
+
+/// parseGNUAsmQualifierListOpt - Parse a GNU extended asm qualifier list.
+/// asm-qualifier:
+/// volatile
+/// inline
+/// goto
+///
+/// asm-qualifier-list:
+/// asm-qualifier
+/// asm-qualifier-list asm-qualifier
+bool Parser::parseGNUAsmQualifierListOpt(GNUAsmQualifiers &AQ) {
+ while (true) {
+ const GNUAsmQualifiers::AQ A = getGNUAsmQualifier(Tok);
+ if (A == GNUAsmQualifiers::AQ_unspecified) {
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok.getLocation(), diag::err_asm_qualifier_ignored);
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return true;
+ }
+ return false;
+ }
+ if (AQ.setAsmQualifier(A))
+ Diag(Tok.getLocation(), diag::err_asm_duplicate_qual)
+ << GNUAsmQualifiers::getQualifierName(A);
+ ConsumeToken();
+ }
+ return false;
+}
+
+/// ParseAsmStatement - Parse a GNU extended asm statement.
+/// asm-statement:
+/// gnu-asm-statement
+/// ms-asm-statement
+///
+/// [GNU] gnu-asm-statement:
+/// 'asm' asm-qualifier-list[opt] '(' asm-argument ')' ';'
+///
+/// [GNU] asm-argument:
+/// asm-string-literal
+/// asm-string-literal ':' asm-operands[opt]
+/// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt]
+/// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt]
+/// ':' asm-clobbers
+///
+/// [GNU] asm-clobbers:
+/// asm-string-literal
+/// asm-clobbers ',' asm-string-literal
+///
+StmtResult Parser::ParseAsmStatement(bool &msAsm) {
+ assert(Tok.is(tok::kw_asm) && "Not an asm stmt");
+ SourceLocation AsmLoc = ConsumeToken();
+
+ if (getLangOpts().AsmBlocks && !isGCCAsmStatement(Tok)) {
+ msAsm = true;
+ return ParseMicrosoftAsmStatement(AsmLoc);
+ }
+
+ SourceLocation Loc = Tok.getLocation();
+ GNUAsmQualifiers GAQ;
+ if (parseGNUAsmQualifierListOpt(GAQ))
+ return StmtError();
+
+ if (GAQ.isGoto() && getLangOpts().SpeculativeLoadHardening)
+ Diag(Loc, diag::warn_slh_does_not_support_asm_goto);
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ ExprResult AsmString(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
+
+ // Check if GNU-style InlineAsm is disabled.
+ // Error on anything other than empty string.
+ if (!(getLangOpts().GNUAsm || AsmString.isInvalid())) {
+ const auto *SL = cast<StringLiteral>(AsmString.get());
+ if (!SL->getString().trim().empty())
+ Diag(Loc, diag::err_gnu_inline_asm_disabled);
+ }
+
+ if (AsmString.isInvalid()) {
+ // Consume up to and including the closing paren.
+ T.skipToEnd();
+ return StmtError();
+ }
+
+ SmallVector<IdentifierInfo *, 4> Names;
+ ExprVector Constraints;
+ ExprVector Exprs;
+ ExprVector Clobbers;
+
+ if (Tok.is(tok::r_paren)) {
+ // We have a simple asm expression like 'asm("foo")'.
+ T.consumeClose();
+ return Actions.ActOnGCCAsmStmt(
+ AsmLoc, /*isSimple*/ true, GAQ.isVolatile(),
+ /*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr, Constraints, Exprs,
+ AsmString.get(), Clobbers, /*NumLabels*/ 0, T.getCloseLocation());
+ }
+
+ // Parse Outputs, if present.
+ bool AteExtraColon = false;
+ if (Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
+ // In C++ mode, parse "::" like ": :".
+ AteExtraColon = Tok.is(tok::coloncolon);
+ ConsumeToken();
+
+ if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
+ return StmtError();
+ }
+
+ unsigned NumOutputs = Names.size();
+
+ // Parse Inputs, if present.
+ if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
+ // In C++ mode, parse "::" like ": :".
+ if (AteExtraColon)
+ AteExtraColon = false;
+ else {
+ AteExtraColon = Tok.is(tok::coloncolon);
+ ConsumeToken();
+ }
+
+ if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
+ return StmtError();
+ }
+
+ assert(Names.size() == Constraints.size() &&
+ Constraints.size() == Exprs.size() && "Input operand size mismatch!");
+
+ unsigned NumInputs = Names.size() - NumOutputs;
+
+ // Parse the clobbers, if present.
+ if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
+ if (AteExtraColon)
+ AteExtraColon = false;
+ else {
+ AteExtraColon = Tok.is(tok::coloncolon);
+ ConsumeToken();
+ }
+ // Parse the asm-string list for clobbers if present.
+ if (!AteExtraColon && isTokenStringLiteral()) {
+ while (true) {
+ ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
+
+ if (Clobber.isInvalid())
+ break;
+
+ Clobbers.push_back(Clobber.get());
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+ }
+ }
+ if (!GAQ.isGoto() && (Tok.isNot(tok::r_paren) || AteExtraColon)) {
+ Diag(Tok, diag::err_expected) << tok::r_paren;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return StmtError();
+ }
+
+ // Parse the goto label, if present.
+ unsigned NumLabels = 0;
+ if (AteExtraColon || Tok.is(tok::colon)) {
+ if (!AteExtraColon)
+ ConsumeToken();
+
+ while (true) {
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return StmtError();
+ }
+ LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(),
+ Tok.getLocation());
+ Names.push_back(Tok.getIdentifierInfo());
+ if (!LD) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return StmtError();
+ }
+ ExprResult Res =
+ Actions.ActOnAddrLabel(Tok.getLocation(), Tok.getLocation(), LD);
+ Exprs.push_back(Res.get());
+ NumLabels++;
+ ConsumeToken();
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+ } else if (GAQ.isGoto()) {
+ Diag(Tok, diag::err_expected) << tok::colon;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return StmtError();
+ }
+ T.consumeClose();
+ return Actions.ActOnGCCAsmStmt(AsmLoc, false, GAQ.isVolatile(), NumOutputs,
+ NumInputs, Names.data(), Constraints, Exprs,
+ AsmString.get(), Clobbers, NumLabels,
+ T.getCloseLocation());
+}
+
+/// ParseAsmOperands - Parse the asm-operands production as used by
+/// asm-statement, assuming the leading ':' token was eaten.
+///
+/// [GNU] asm-operands:
+/// asm-operand
+/// asm-operands ',' asm-operand
+///
+/// [GNU] asm-operand:
+/// asm-string-literal '(' expression ')'
+/// '[' identifier ']' asm-string-literal '(' expression ')'
+///
+//
+// FIXME: Avoid unnecessary std::string trashing.
+bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names,
+ SmallVectorImpl<Expr *> &Constraints,
+ SmallVectorImpl<Expr *> &Exprs) {
+ // 'asm-operands' isn't present?
+ if (!isTokenStringLiteral() && Tok.isNot(tok::l_square))
+ return false;
+
+ while (true) {
+ // Read the [id] if present.
+ if (Tok.is(tok::l_square)) {
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ T.consumeOpen();
+
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return true;
+ }
+
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ ConsumeToken();
+
+ Names.push_back(II);
+ T.consumeClose();
+ } else
+ Names.push_back(nullptr);
+
+ ExprResult Constraint(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
+ if (Constraint.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return true;
+ }
+ Constraints.push_back(Constraint.get());
+
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_expected_lparen_after) << "asm operand";
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return true;
+ }
+
+ // Read the parenthesized expression.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ ExprResult Res = Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ T.consumeClose();
+ if (Res.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return true;
+ }
+ Exprs.push_back(Res.get());
+ // Eat the comma and continue parsing if it exists.
+ if (!TryConsumeToken(tok::comma))
+ return false;
+ }
+}
+
+const char *Parser::GNUAsmQualifiers::getQualifierName(AQ Qualifier) {
+ switch (Qualifier) {
+ case AQ_volatile: return "volatile";
+ case AQ_inline: return "inline";
+ case AQ_goto: return "goto";
+ case AQ_unspecified: return "unspecified";
+ }
+ llvm_unreachable("Unknown GNUAsmQualifier");
+}
+
+Parser::GNUAsmQualifiers::AQ
+Parser::getGNUAsmQualifier(const Token &Tok) const {
+ switch (Tok.getKind()) {
+ case tok::kw_volatile: return GNUAsmQualifiers::AQ_volatile;
+ case tok::kw_inline: return GNUAsmQualifiers::AQ_inline;
+ case tok::kw_goto: return GNUAsmQualifiers::AQ_goto;
+ default: return GNUAsmQualifiers::AQ_unspecified;
+ }
+}
+bool Parser::GNUAsmQualifiers::setAsmQualifier(AQ Qualifier) {
+ bool IsDuplicate = Qualifiers & Qualifier;
+ Qualifiers |= Qualifier;
+ return IsDuplicate;
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseTemplate.cpp b/contrib/libs/clang16/lib/Parse/ParseTemplate.cpp
new file mode 100644
index 0000000000..6fc67b6965
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseTemplate.cpp
@@ -0,0 +1,1894 @@
+//===--- ParseTemplate.cpp - Template Parsing -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements parsing of C++ templates.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/ParsedTemplate.h"
+#include "clang/Sema/Scope.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/Support/TimeProfiler.h"
+using namespace clang;
+
+/// Re-enter a possible template scope, creating as many template parameter
+/// scopes as necessary.
+/// \return The number of template parameter scopes entered.
+unsigned Parser::ReenterTemplateScopes(MultiParseScope &S, Decl *D) {
+ return Actions.ActOnReenterTemplateScope(D, [&] {
+ S.Enter(Scope::TemplateParamScope);
+ return Actions.getCurScope();
+ });
+}
+
+/// Parse a template declaration, explicit instantiation, or
+/// explicit specialization.
+Decl *Parser::ParseDeclarationStartingWithTemplate(
+ DeclaratorContext Context, SourceLocation &DeclEnd,
+ ParsedAttributes &AccessAttrs, AccessSpecifier AS) {
+ ObjCDeclContextSwitch ObjCDC(*this);
+
+ if (Tok.is(tok::kw_template) && NextToken().isNot(tok::less)) {
+ return ParseExplicitInstantiation(Context, SourceLocation(), ConsumeToken(),
+ DeclEnd, AccessAttrs, AS);
+ }
+ return ParseTemplateDeclarationOrSpecialization(Context, DeclEnd, AccessAttrs,
+ AS);
+}
+
+/// Parse a template declaration or an explicit specialization.
+///
+/// Template declarations include one or more template parameter lists
+/// and either the function or class template declaration. Explicit
+/// specializations contain one or more 'template < >' prefixes
+/// followed by a (possibly templated) declaration. Since the
+/// syntactic form of both features is nearly identical, we parse all
+/// of the template headers together and let semantic analysis sort
+/// the declarations from the explicit specializations.
+///
+/// template-declaration: [C++ temp]
+/// 'export'[opt] 'template' '<' template-parameter-list '>' declaration
+///
+/// template-declaration: [C++2a]
+/// template-head declaration
+/// template-head concept-definition
+///
+/// TODO: requires-clause
+/// template-head: [C++2a]
+/// 'template' '<' template-parameter-list '>'
+/// requires-clause[opt]
+///
+/// explicit-specialization: [ C++ temp.expl.spec]
+/// 'template' '<' '>' declaration
+Decl *Parser::ParseTemplateDeclarationOrSpecialization(
+ DeclaratorContext Context, SourceLocation &DeclEnd,
+ ParsedAttributes &AccessAttrs, AccessSpecifier AS) {
+ assert(Tok.isOneOf(tok::kw_export, tok::kw_template) &&
+ "Token does not start a template declaration.");
+
+ MultiParseScope TemplateParamScopes(*this);
+
+ // Tell the action that names should be checked in the context of
+ // the declaration to come.
+ ParsingDeclRAIIObject
+ ParsingTemplateParams(*this, ParsingDeclRAIIObject::NoParent);
+
+ // Parse multiple levels of template headers within this template
+ // parameter scope, e.g.,
+ //
+ // template<typename T>
+ // template<typename U>
+ // class A<T>::B { ... };
+ //
+ // We parse multiple levels non-recursively so that we can build a
+ // single data structure containing all of the template parameter
+ // lists to easily differentiate between the case above and:
+ //
+ // template<typename T>
+ // class A {
+ // template<typename U> class B;
+ // };
+ //
+ // In the first case, the action for declaring A<T>::B receives
+ // both template parameter lists. In the second case, the action for
+ // defining A<T>::B receives just the inner template parameter list
+ // (and retrieves the outer template parameter list from its
+ // context).
+ bool isSpecialization = true;
+ bool LastParamListWasEmpty = false;
+ TemplateParameterLists ParamLists;
+ TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
+
+ do {
+ // Consume the 'export', if any.
+ SourceLocation ExportLoc;
+ TryConsumeToken(tok::kw_export, ExportLoc);
+
+ // Consume the 'template', which should be here.
+ SourceLocation TemplateLoc;
+ if (!TryConsumeToken(tok::kw_template, TemplateLoc)) {
+ Diag(Tok.getLocation(), diag::err_expected_template);
+ return nullptr;
+ }
+
+ // Parse the '<' template-parameter-list '>'
+ SourceLocation LAngleLoc, RAngleLoc;
+ SmallVector<NamedDecl*, 4> TemplateParams;
+ if (ParseTemplateParameters(TemplateParamScopes,
+ CurTemplateDepthTracker.getDepth(),
+ TemplateParams, LAngleLoc, RAngleLoc)) {
+ // Skip until the semi-colon or a '}'.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ TryConsumeToken(tok::semi);
+ return nullptr;
+ }
+
+ ExprResult OptionalRequiresClauseConstraintER;
+ if (!TemplateParams.empty()) {
+ isSpecialization = false;
+ ++CurTemplateDepthTracker;
+
+ if (TryConsumeToken(tok::kw_requires)) {
+ OptionalRequiresClauseConstraintER =
+ Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression(
+ /*IsTrailingRequiresClause=*/false));
+ if (!OptionalRequiresClauseConstraintER.isUsable()) {
+ // Skip until the semi-colon or a '}'.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ TryConsumeToken(tok::semi);
+ return nullptr;
+ }
+ }
+ } else {
+ LastParamListWasEmpty = true;
+ }
+
+ ParamLists.push_back(Actions.ActOnTemplateParameterList(
+ CurTemplateDepthTracker.getDepth(), ExportLoc, TemplateLoc, LAngleLoc,
+ TemplateParams, RAngleLoc, OptionalRequiresClauseConstraintER.get()));
+ } while (Tok.isOneOf(tok::kw_export, tok::kw_template));
+
+ // Parse the actual template declaration.
+ if (Tok.is(tok::kw_concept))
+ return ParseConceptDefinition(
+ ParsedTemplateInfo(&ParamLists, isSpecialization,
+ LastParamListWasEmpty),
+ DeclEnd);
+
+ return ParseSingleDeclarationAfterTemplate(
+ Context,
+ ParsedTemplateInfo(&ParamLists, isSpecialization, LastParamListWasEmpty),
+ ParsingTemplateParams, DeclEnd, AccessAttrs, AS);
+}
+
+/// Parse a single declaration that declares a template,
+/// template specialization, or explicit instantiation of a template.
+///
+/// \param DeclEnd will receive the source location of the last token
+/// within this declaration.
+///
+/// \param AS the access specifier associated with this
+/// declaration. Will be AS_none for namespace-scope declarations.
+///
+/// \returns the new declaration.
+Decl *Parser::ParseSingleDeclarationAfterTemplate(
+ DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
+ ParsingDeclRAIIObject &DiagsFromTParams, SourceLocation &DeclEnd,
+ ParsedAttributes &AccessAttrs, AccessSpecifier AS) {
+ assert(TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
+ "Template information required");
+
+ if (Tok.is(tok::kw_static_assert)) {
+ // A static_assert declaration may not be templated.
+ Diag(Tok.getLocation(), diag::err_templated_invalid_declaration)
+ << TemplateInfo.getSourceRange();
+ // Parse the static_assert declaration to improve error recovery.
+ return ParseStaticAssertDeclaration(DeclEnd);
+ }
+
+ if (Context == DeclaratorContext::Member) {
+ // We are parsing a member template.
+ DeclGroupPtrTy D = ParseCXXClassMemberDeclaration(
+ AS, AccessAttrs, TemplateInfo, &DiagsFromTParams);
+
+ if (!D || !D.get().isSingleDecl())
+ return nullptr;
+ return D.get().getSingleDecl();
+ }
+
+ ParsedAttributes prefixAttrs(AttrFactory);
+ MaybeParseCXX11Attributes(prefixAttrs);
+
+ if (Tok.is(tok::kw_using)) {
+ auto usingDeclPtr = ParseUsingDirectiveOrDeclaration(Context, TemplateInfo, DeclEnd,
+ prefixAttrs);
+ if (!usingDeclPtr || !usingDeclPtr.get().isSingleDecl())
+ return nullptr;
+ return usingDeclPtr.get().getSingleDecl();
+ }
+
+ // Parse the declaration specifiers, stealing any diagnostics from
+ // the template parameters.
+ ParsingDeclSpec DS(*this, &DiagsFromTParams);
+
+ ParseDeclarationSpecifiers(DS, TemplateInfo, AS,
+ getDeclSpecContextFromDeclaratorContext(Context));
+
+ if (Tok.is(tok::semi)) {
+ ProhibitAttributes(prefixAttrs);
+ DeclEnd = ConsumeToken();
+ RecordDecl *AnonRecord = nullptr;
+ Decl *Decl = Actions.ParsedFreeStandingDeclSpec(
+ getCurScope(), AS, DS, ParsedAttributesView::none(),
+ TemplateInfo.TemplateParams ? *TemplateInfo.TemplateParams
+ : MultiTemplateParamsArg(),
+ TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation,
+ AnonRecord);
+ assert(!AnonRecord &&
+ "Anonymous unions/structs should not be valid with template");
+ DS.complete(Decl);
+ return Decl;
+ }
+
+ // Move the attributes from the prefix into the DS.
+ if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation)
+ ProhibitAttributes(prefixAttrs);
+
+ // Parse the declarator.
+ ParsingDeclarator DeclaratorInfo(*this, DS, prefixAttrs,
+ (DeclaratorContext)Context);
+ if (TemplateInfo.TemplateParams)
+ DeclaratorInfo.setTemplateParameterLists(*TemplateInfo.TemplateParams);
+
+ // Turn off usual access checking for template specializations and
+ // instantiations.
+ // C++20 [temp.spec] 13.9/6.
+ // This disables the access checking rules for function template explicit
+ // instantiation and explicit specialization:
+ // - parameter-list;
+ // - template-argument-list;
+ // - noexcept-specifier;
+ // - dynamic-exception-specifications (deprecated in C++11, removed since
+ // C++17).
+ bool IsTemplateSpecOrInst =
+ (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
+ TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+ SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
+
+ ParseDeclarator(DeclaratorInfo);
+
+ if (IsTemplateSpecOrInst)
+ SAC.done();
+
+ // Error parsing the declarator?
+ if (!DeclaratorInfo.hasName()) {
+ // If so, skip until the semi-colon or a }.
+ SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ return nullptr;
+ }
+
+ llvm::TimeTraceScope TimeScope("ParseTemplate", [&]() {
+ return std::string(DeclaratorInfo.getIdentifier() != nullptr
+ ? DeclaratorInfo.getIdentifier()->getName()
+ : "<unknown>");
+ });
+
+ LateParsedAttrList LateParsedAttrs(true);
+ if (DeclaratorInfo.isFunctionDeclarator()) {
+ if (Tok.is(tok::kw_requires)) {
+ CXXScopeSpec &ScopeSpec = DeclaratorInfo.getCXXScopeSpec();
+ DeclaratorScopeObj DeclScopeObj(*this, ScopeSpec);
+ if (ScopeSpec.isValid() &&
+ Actions.ShouldEnterDeclaratorScope(getCurScope(), ScopeSpec))
+ DeclScopeObj.EnterDeclaratorScope();
+ ParseTrailingRequiresClause(DeclaratorInfo);
+ }
+
+ MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs);
+ }
+
+ if (DeclaratorInfo.isFunctionDeclarator() &&
+ isStartOfFunctionDefinition(DeclaratorInfo)) {
+
+ // Function definitions are only allowed at file scope and in C++ classes.
+ // The C++ inline method definition case is handled elsewhere, so we only
+ // need to handle the file scope definition case.
+ if (Context != DeclaratorContext::File) {
+ Diag(Tok, diag::err_function_definition_not_allowed);
+ SkipMalformedDecl();
+ return nullptr;
+ }
+
+ if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
+ // Recover by ignoring the 'typedef'. This was probably supposed to be
+ // the 'typename' keyword, which we should have already suggested adding
+ // if it's appropriate.
+ Diag(DS.getStorageClassSpecLoc(), diag::err_function_declared_typedef)
+ << FixItHint::CreateRemoval(DS.getStorageClassSpecLoc());
+ DS.ClearStorageClassSpecs();
+ }
+
+ if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) {
+ if (DeclaratorInfo.getName().getKind() !=
+ UnqualifiedIdKind::IK_TemplateId) {
+ // If the declarator-id is not a template-id, issue a diagnostic and
+ // recover by ignoring the 'template' keyword.
+ Diag(Tok, diag::err_template_defn_explicit_instantiation) << 0;
+ return ParseFunctionDefinition(DeclaratorInfo, ParsedTemplateInfo(),
+ &LateParsedAttrs);
+ } else {
+ SourceLocation LAngleLoc
+ = PP.getLocForEndOfToken(TemplateInfo.TemplateLoc);
+ Diag(DeclaratorInfo.getIdentifierLoc(),
+ diag::err_explicit_instantiation_with_definition)
+ << SourceRange(TemplateInfo.TemplateLoc)
+ << FixItHint::CreateInsertion(LAngleLoc, "<>");
+
+ // Recover as if it were an explicit specialization.
+ TemplateParameterLists FakedParamLists;
+ FakedParamLists.push_back(Actions.ActOnTemplateParameterList(
+ 0, SourceLocation(), TemplateInfo.TemplateLoc, LAngleLoc,
+ std::nullopt, LAngleLoc, nullptr));
+
+ return ParseFunctionDefinition(
+ DeclaratorInfo, ParsedTemplateInfo(&FakedParamLists,
+ /*isSpecialization=*/true,
+ /*lastParameterListWasEmpty=*/true),
+ &LateParsedAttrs);
+ }
+ }
+ return ParseFunctionDefinition(DeclaratorInfo, TemplateInfo,
+ &LateParsedAttrs);
+ }
+
+ // Parse this declaration.
+ Decl *ThisDecl = ParseDeclarationAfterDeclarator(DeclaratorInfo,
+ TemplateInfo);
+
+ if (Tok.is(tok::comma)) {
+ Diag(Tok, diag::err_multiple_template_declarators)
+ << (int)TemplateInfo.Kind;
+ SkipUntil(tok::semi);
+ return ThisDecl;
+ }
+
+ // Eat the semi colon after the declaration.
+ ExpectAndConsumeSemi(diag::err_expected_semi_declaration);
+ if (LateParsedAttrs.size() > 0)
+ ParseLexedAttributeList(LateParsedAttrs, ThisDecl, true, false);
+ DeclaratorInfo.complete(ThisDecl);
+ return ThisDecl;
+}
+
+/// \brief Parse a single declaration that declares a concept.
+///
+/// \param DeclEnd will receive the source location of the last token
+/// within this declaration.
+///
+/// \returns the new declaration.
+Decl *
+Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
+ SourceLocation &DeclEnd) {
+ assert(TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
+ "Template information required");
+ assert(Tok.is(tok::kw_concept) &&
+ "ParseConceptDefinition must be called when at a 'concept' keyword");
+
+ ConsumeToken(); // Consume 'concept'
+
+ SourceLocation BoolKWLoc;
+ if (TryConsumeToken(tok::kw_bool, BoolKWLoc))
+ Diag(Tok.getLocation(), diag::err_concept_legacy_bool_keyword) <<
+ FixItHint::CreateRemoval(SourceLocation(BoolKWLoc));
+
+ DiagnoseAndSkipCXX11Attributes();
+
+ CXXScopeSpec SS;
+ if (ParseOptionalCXXScopeSpecifier(
+ SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false, /*EnteringContext=*/false,
+ /*MayBePseudoDestructor=*/nullptr,
+ /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) ||
+ SS.isInvalid()) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ if (SS.isNotEmpty())
+ Diag(SS.getBeginLoc(),
+ diag::err_concept_definition_not_identifier);
+
+ UnqualifiedId Result;
+ if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext=*/false,
+ /*AllowDestructorName=*/false,
+ /*AllowConstructorName=*/false,
+ /*AllowDeductionGuide=*/false,
+ /*TemplateKWLoc=*/nullptr, Result)) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ if (Result.getKind() != UnqualifiedIdKind::IK_Identifier) {
+ Diag(Result.getBeginLoc(), diag::err_concept_definition_not_identifier);
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ IdentifierInfo *Id = Result.Identifier;
+ SourceLocation IdLoc = Result.getBeginLoc();
+
+ DiagnoseAndSkipCXX11Attributes();
+
+ if (!TryConsumeToken(tok::equal)) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::equal;
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ ExprResult ConstraintExprResult =
+ Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression());
+ if (ConstraintExprResult.isInvalid()) {
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ DeclEnd = Tok.getLocation();
+ ExpectAndConsumeSemi(diag::err_expected_semi_declaration);
+ Expr *ConstraintExpr = ConstraintExprResult.get();
+ return Actions.ActOnConceptDefinition(getCurScope(),
+ *TemplateInfo.TemplateParams,
+ Id, IdLoc, ConstraintExpr);
+}
+
+/// ParseTemplateParameters - Parses a template-parameter-list enclosed in
+/// angle brackets. Depth is the depth of this template-parameter-list, which
+/// is the number of template headers directly enclosing this template header.
+/// TemplateParams is the current list of template parameters we're building.
+/// The template parameter we parse will be added to this list. LAngleLoc and
+/// RAngleLoc will receive the positions of the '<' and '>', respectively,
+/// that enclose this template parameter list.
+///
+/// \returns true if an error occurred, false otherwise.
+bool Parser::ParseTemplateParameters(
+ MultiParseScope &TemplateScopes, unsigned Depth,
+ SmallVectorImpl<NamedDecl *> &TemplateParams, SourceLocation &LAngleLoc,
+ SourceLocation &RAngleLoc) {
+ // Get the template parameter list.
+ if (!TryConsumeToken(tok::less, LAngleLoc)) {
+ Diag(Tok.getLocation(), diag::err_expected_less_after) << "template";
+ return true;
+ }
+
+ // Try to parse the template parameter list.
+ bool Failed = false;
+ // FIXME: Missing greatergreatergreater support.
+ if (!Tok.is(tok::greater) && !Tok.is(tok::greatergreater)) {
+ TemplateScopes.Enter(Scope::TemplateParamScope);
+ Failed = ParseTemplateParameterList(Depth, TemplateParams);
+ }
+
+ if (Tok.is(tok::greatergreater)) {
+ // No diagnostic required here: a template-parameter-list can only be
+ // followed by a declaration or, for a template template parameter, the
+ // 'class' keyword. Therefore, the second '>' will be diagnosed later.
+ // This matters for elegant diagnosis of:
+ // template<template<typename>> struct S;
+ Tok.setKind(tok::greater);
+ RAngleLoc = Tok.getLocation();
+ Tok.setLocation(Tok.getLocation().getLocWithOffset(1));
+ } else if (!TryConsumeToken(tok::greater, RAngleLoc) && Failed) {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::greater;
+ return true;
+ }
+ return false;
+}
+
+/// ParseTemplateParameterList - Parse a template parameter list. If
+/// the parsing fails badly (i.e., closing bracket was left out), this
+/// will try to put the token stream in a reasonable position (closing
+/// a statement, etc.) and return false.
+///
+/// template-parameter-list: [C++ temp]
+/// template-parameter
+/// template-parameter-list ',' template-parameter
+bool
+Parser::ParseTemplateParameterList(const unsigned Depth,
+ SmallVectorImpl<NamedDecl*> &TemplateParams) {
+ while (true) {
+
+ if (NamedDecl *TmpParam
+ = ParseTemplateParameter(Depth, TemplateParams.size())) {
+ TemplateParams.push_back(TmpParam);
+ } else {
+ // If we failed to parse a template parameter, skip until we find
+ // a comma or closing brace.
+ SkipUntil(tok::comma, tok::greater, tok::greatergreater,
+ StopAtSemi | StopBeforeMatch);
+ }
+
+ // Did we find a comma or the end of the template parameter list?
+ if (Tok.is(tok::comma)) {
+ ConsumeToken();
+ } else if (Tok.isOneOf(tok::greater, tok::greatergreater)) {
+ // Don't consume this... that's done by template parser.
+ break;
+ } else {
+ // Somebody probably forgot to close the template. Skip ahead and
+ // try to get out of the expression. This error is currently
+ // subsumed by whatever goes on in ParseTemplateParameter.
+ Diag(Tok.getLocation(), diag::err_expected_comma_greater);
+ SkipUntil(tok::comma, tok::greater, tok::greatergreater,
+ StopAtSemi | StopBeforeMatch);
+ return false;
+ }
+ }
+ return true;
+}
+
+/// Determine whether the parser is at the start of a template
+/// type parameter.
+Parser::TPResult Parser::isStartOfTemplateTypeParameter() {
+ if (Tok.is(tok::kw_class)) {
+ // "class" may be the start of an elaborated-type-specifier or a
+ // type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter.
+ switch (NextToken().getKind()) {
+ case tok::equal:
+ case tok::comma:
+ case tok::greater:
+ case tok::greatergreater:
+ case tok::ellipsis:
+ return TPResult::True;
+
+ case tok::identifier:
+ // This may be either a type-parameter or an elaborated-type-specifier.
+ // We have to look further.
+ break;
+
+ default:
+ return TPResult::False;
+ }
+
+ switch (GetLookAheadToken(2).getKind()) {
+ case tok::equal:
+ case tok::comma:
+ case tok::greater:
+ case tok::greatergreater:
+ return TPResult::True;
+
+ default:
+ return TPResult::False;
+ }
+ }
+
+ if (TryAnnotateTypeConstraint())
+ return TPResult::Error;
+
+ if (isTypeConstraintAnnotation() &&
+ // Next token might be 'auto' or 'decltype', indicating that this
+ // type-constraint is in fact part of a placeholder-type-specifier of a
+ // non-type template parameter.
+ !GetLookAheadToken(Tok.is(tok::annot_cxxscope) ? 2 : 1)
+ .isOneOf(tok::kw_auto, tok::kw_decltype))
+ return TPResult::True;
+
+ // 'typedef' is a reasonably-common typo/thinko for 'typename', and is
+ // ill-formed otherwise.
+ if (Tok.isNot(tok::kw_typename) && Tok.isNot(tok::kw_typedef))
+ return TPResult::False;
+
+ // C++ [temp.param]p2:
+ // There is no semantic difference between class and typename in a
+ // template-parameter. typename followed by an unqualified-id
+ // names a template type parameter. typename followed by a
+ // qualified-id denotes the type in a non-type
+ // parameter-declaration.
+ Token Next = NextToken();
+
+ // If we have an identifier, skip over it.
+ if (Next.getKind() == tok::identifier)
+ Next = GetLookAheadToken(2);
+
+ switch (Next.getKind()) {
+ case tok::equal:
+ case tok::comma:
+ case tok::greater:
+ case tok::greatergreater:
+ case tok::ellipsis:
+ return TPResult::True;
+
+ case tok::kw_typename:
+ case tok::kw_typedef:
+ case tok::kw_class:
+ // These indicate that a comma was missed after a type parameter, not that
+ // we have found a non-type parameter.
+ return TPResult::True;
+
+ default:
+ return TPResult::False;
+ }
+}
+
+/// ParseTemplateParameter - Parse a template-parameter (C++ [temp.param]).
+///
+/// template-parameter: [C++ temp.param]
+/// type-parameter
+/// parameter-declaration
+///
+/// type-parameter: (See below)
+/// type-parameter-key ...[opt] identifier[opt]
+/// type-parameter-key identifier[opt] = type-id
+/// (C++2a) type-constraint ...[opt] identifier[opt]
+/// (C++2a) type-constraint identifier[opt] = type-id
+/// 'template' '<' template-parameter-list '>' type-parameter-key
+/// ...[opt] identifier[opt]
+/// 'template' '<' template-parameter-list '>' type-parameter-key
+/// identifier[opt] '=' id-expression
+///
+/// type-parameter-key:
+/// class
+/// typename
+///
+NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
+
+ switch (isStartOfTemplateTypeParameter()) {
+ case TPResult::True:
+ // Is there just a typo in the input code? ('typedef' instead of
+ // 'typename')
+ if (Tok.is(tok::kw_typedef)) {
+ Diag(Tok.getLocation(), diag::err_expected_template_parameter);
+
+ Diag(Tok.getLocation(), diag::note_meant_to_use_typename)
+ << FixItHint::CreateReplacement(CharSourceRange::getCharRange(
+ Tok.getLocation(),
+ Tok.getEndLoc()),
+ "typename");
+
+ Tok.setKind(tok::kw_typename);
+ }
+
+ return ParseTypeParameter(Depth, Position);
+ case TPResult::False:
+ break;
+
+ case TPResult::Error: {
+ // We return an invalid parameter as opposed to null to avoid having bogus
+ // diagnostics about an empty template parameter list.
+ // FIXME: Fix ParseTemplateParameterList to better handle nullptr results
+ // from here.
+ // Return a NTTP as if there was an error in a scope specifier, the user
+ // probably meant to write the type of a NTTP.
+ DeclSpec DS(getAttrFactory());
+ DS.SetTypeSpecError();
+ Declarator D(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TemplateParam);
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ D.setInvalidType(true);
+ NamedDecl *ErrorParam = Actions.ActOnNonTypeTemplateParameter(
+ getCurScope(), D, Depth, Position, /*EqualLoc=*/SourceLocation(),
+ /*DefaultArg=*/nullptr);
+ ErrorParam->setInvalidDecl(true);
+ SkipUntil(tok::comma, tok::greater, tok::greatergreater,
+ StopAtSemi | StopBeforeMatch);
+ return ErrorParam;
+ }
+
+ case TPResult::Ambiguous:
+ llvm_unreachable("template param classification can't be ambiguous");
+ }
+
+ if (Tok.is(tok::kw_template))
+ return ParseTemplateTemplateParameter(Depth, Position);
+
+ // If it's none of the above, then it must be a parameter declaration.
+ // NOTE: This will pick up errors in the closure of the template parameter
+ // list (e.g., template < ; Check here to implement >> style closures.
+ return ParseNonTypeTemplateParameter(Depth, Position);
+}
+
+/// Check whether the current token is a template-id annotation denoting a
+/// type-constraint.
+bool Parser::isTypeConstraintAnnotation() {
+ const Token &T = Tok.is(tok::annot_cxxscope) ? NextToken() : Tok;
+ if (T.isNot(tok::annot_template_id))
+ return false;
+ const auto *ExistingAnnot =
+ static_cast<TemplateIdAnnotation *>(T.getAnnotationValue());
+ return ExistingAnnot->Kind == TNK_Concept_template;
+}
+
+/// Try parsing a type-constraint at the current location.
+///
+/// type-constraint:
+/// nested-name-specifier[opt] concept-name
+/// nested-name-specifier[opt] concept-name
+/// '<' template-argument-list[opt] '>'[opt]
+///
+/// \returns true if an error occurred, and false otherwise.
+bool Parser::TryAnnotateTypeConstraint() {
+ if (!getLangOpts().CPlusPlus20)
+ return false;
+ CXXScopeSpec SS;
+ bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope);
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false,
+ /*MayBePseudoDestructor=*/nullptr,
+ // If this is not a type-constraint, then
+ // this scope-spec is part of the typename
+ // of a non-type template parameter
+ /*IsTypename=*/true, /*LastII=*/nullptr,
+ // We won't find concepts in
+ // non-namespaces anyway, so might as well
+ // parse this correctly for possible type
+ // names.
+ /*OnlyNamespace=*/false))
+ return true;
+
+ if (Tok.is(tok::identifier)) {
+ UnqualifiedId PossibleConceptName;
+ PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(),
+ Tok.getLocation());
+
+ TemplateTy PossibleConcept;
+ bool MemberOfUnknownSpecialization = false;
+ auto TNK = Actions.isTemplateName(getCurScope(), SS,
+ /*hasTemplateKeyword=*/false,
+ PossibleConceptName,
+ /*ObjectType=*/ParsedType(),
+ /*EnteringContext=*/false,
+ PossibleConcept,
+ MemberOfUnknownSpecialization,
+ /*Disambiguation=*/true);
+ if (MemberOfUnknownSpecialization || !PossibleConcept ||
+ TNK != TNK_Concept_template) {
+ if (SS.isNotEmpty())
+ AnnotateScopeToken(SS, !WasScopeAnnotation);
+ return false;
+ }
+
+ // At this point we're sure we're dealing with a constrained parameter. It
+ // may or may not have a template parameter list following the concept
+ // name.
+ if (AnnotateTemplateIdToken(PossibleConcept, TNK, SS,
+ /*TemplateKWLoc=*/SourceLocation(),
+ PossibleConceptName,
+ /*AllowTypeAnnotation=*/false,
+ /*TypeConstraint=*/true))
+ return true;
+ }
+
+ if (SS.isNotEmpty())
+ AnnotateScopeToken(SS, !WasScopeAnnotation);
+ return false;
+}
+
+/// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]).
+/// Other kinds of template parameters are parsed in
+/// ParseTemplateTemplateParameter and ParseNonTypeTemplateParameter.
+///
+/// type-parameter: [C++ temp.param]
+/// 'class' ...[opt][C++0x] identifier[opt]
+/// 'class' identifier[opt] '=' type-id
+/// 'typename' ...[opt][C++0x] identifier[opt]
+/// 'typename' identifier[opt] '=' type-id
+NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
+ assert((Tok.isOneOf(tok::kw_class, tok::kw_typename) ||
+ isTypeConstraintAnnotation()) &&
+ "A type-parameter starts with 'class', 'typename' or a "
+ "type-constraint");
+
+ CXXScopeSpec TypeConstraintSS;
+ TemplateIdAnnotation *TypeConstraint = nullptr;
+ bool TypenameKeyword = false;
+ SourceLocation KeyLoc;
+ ParseOptionalCXXScopeSpecifier(TypeConstraintSS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext*/ false);
+ if (Tok.is(tok::annot_template_id)) {
+ // Consume the 'type-constraint'.
+ TypeConstraint =
+ static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+ assert(TypeConstraint->Kind == TNK_Concept_template &&
+ "stray non-concept template-id annotation");
+ KeyLoc = ConsumeAnnotationToken();
+ } else {
+ assert(TypeConstraintSS.isEmpty() &&
+ "expected type constraint after scope specifier");
+
+ // Consume the 'class' or 'typename' keyword.
+ TypenameKeyword = Tok.is(tok::kw_typename);
+ KeyLoc = ConsumeToken();
+ }
+
+ // Grab the ellipsis (if given).
+ SourceLocation EllipsisLoc;
+ if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) {
+ Diag(EllipsisLoc,
+ getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_variadic_templates
+ : diag::ext_variadic_templates);
+ }
+
+ // Grab the template parameter name (if given)
+ SourceLocation NameLoc = Tok.getLocation();
+ IdentifierInfo *ParamName = nullptr;
+ if (Tok.is(tok::identifier)) {
+ ParamName = Tok.getIdentifierInfo();
+ ConsumeToken();
+ } else if (Tok.isOneOf(tok::equal, tok::comma, tok::greater,
+ tok::greatergreater)) {
+ // Unnamed template parameter. Don't have to do anything here, just
+ // don't consume this token.
+ } else {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
+ return nullptr;
+ }
+
+ // Recover from misplaced ellipsis.
+ bool AlreadyHasEllipsis = EllipsisLoc.isValid();
+ if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
+ DiagnoseMisplacedEllipsis(EllipsisLoc, NameLoc, AlreadyHasEllipsis, true);
+
+ // Grab a default argument (if available).
+ // Per C++0x [basic.scope.pdecl]p9, we parse the default argument before
+ // we introduce the type parameter into the local scope.
+ SourceLocation EqualLoc;
+ ParsedType DefaultArg;
+ if (TryConsumeToken(tok::equal, EqualLoc))
+ DefaultArg =
+ ParseTypeName(/*Range=*/nullptr, DeclaratorContext::TemplateTypeArg)
+ .get();
+
+ NamedDecl *NewDecl = Actions.ActOnTypeParameter(getCurScope(),
+ TypenameKeyword, EllipsisLoc,
+ KeyLoc, ParamName, NameLoc,
+ Depth, Position, EqualLoc,
+ DefaultArg,
+ TypeConstraint != nullptr);
+
+ if (TypeConstraint) {
+ Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint,
+ cast<TemplateTypeParmDecl>(NewDecl),
+ EllipsisLoc);
+ }
+
+ return NewDecl;
+}
+
+/// ParseTemplateTemplateParameter - Handle the parsing of template
+/// template parameters.
+///
+/// type-parameter: [C++ temp.param]
+/// template-head type-parameter-key ...[opt] identifier[opt]
+/// template-head type-parameter-key identifier[opt] = id-expression
+/// type-parameter-key:
+/// 'class'
+/// 'typename' [C++1z]
+/// template-head: [C++2a]
+/// 'template' '<' template-parameter-list '>'
+/// requires-clause[opt]
+NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
+ unsigned Position) {
+ assert(Tok.is(tok::kw_template) && "Expected 'template' keyword");
+
+ // Handle the template <...> part.
+ SourceLocation TemplateLoc = ConsumeToken();
+ SmallVector<NamedDecl*,8> TemplateParams;
+ SourceLocation LAngleLoc, RAngleLoc;
+ ExprResult OptionalRequiresClauseConstraintER;
+ {
+ MultiParseScope TemplateParmScope(*this);
+ if (ParseTemplateParameters(TemplateParmScope, Depth + 1, TemplateParams,
+ LAngleLoc, RAngleLoc)) {
+ return nullptr;
+ }
+ if (TryConsumeToken(tok::kw_requires)) {
+ OptionalRequiresClauseConstraintER =
+ Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression(
+ /*IsTrailingRequiresClause=*/false));
+ if (!OptionalRequiresClauseConstraintER.isUsable()) {
+ SkipUntil(tok::comma, tok::greater, tok::greatergreater,
+ StopAtSemi | StopBeforeMatch);
+ return nullptr;
+ }
+ }
+ }
+
+ // Provide an ExtWarn if the C++1z feature of using 'typename' here is used.
+ // Generate a meaningful error if the user forgot to put class before the
+ // identifier, comma, or greater. Provide a fixit if the identifier, comma,
+ // or greater appear immediately or after 'struct'. In the latter case,
+ // replace the keyword with 'class'.
+ if (!TryConsumeToken(tok::kw_class)) {
+ bool Replace = Tok.isOneOf(tok::kw_typename, tok::kw_struct);
+ const Token &Next = Tok.is(tok::kw_struct) ? NextToken() : Tok;
+ if (Tok.is(tok::kw_typename)) {
+ Diag(Tok.getLocation(),
+ getLangOpts().CPlusPlus17
+ ? diag::warn_cxx14_compat_template_template_param_typename
+ : diag::ext_template_template_param_typename)
+ << (!getLangOpts().CPlusPlus17
+ ? FixItHint::CreateReplacement(Tok.getLocation(), "class")
+ : FixItHint());
+ } else if (Next.isOneOf(tok::identifier, tok::comma, tok::greater,
+ tok::greatergreater, tok::ellipsis)) {
+ Diag(Tok.getLocation(), diag::err_class_on_template_template_param)
+ << getLangOpts().CPlusPlus17
+ << (Replace
+ ? FixItHint::CreateReplacement(Tok.getLocation(), "class")
+ : FixItHint::CreateInsertion(Tok.getLocation(), "class "));
+ } else
+ Diag(Tok.getLocation(), diag::err_class_on_template_template_param)
+ << getLangOpts().CPlusPlus17;
+
+ if (Replace)
+ ConsumeToken();
+ }
+
+ // Parse the ellipsis, if given.
+ SourceLocation EllipsisLoc;
+ if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
+ Diag(EllipsisLoc,
+ getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_variadic_templates
+ : diag::ext_variadic_templates);
+
+ // Get the identifier, if given.
+ SourceLocation NameLoc = Tok.getLocation();
+ IdentifierInfo *ParamName = nullptr;
+ if (Tok.is(tok::identifier)) {
+ ParamName = Tok.getIdentifierInfo();
+ ConsumeToken();
+ } else if (Tok.isOneOf(tok::equal, tok::comma, tok::greater,
+ tok::greatergreater)) {
+ // Unnamed template parameter. Don't have to do anything here, just
+ // don't consume this token.
+ } else {
+ Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
+ return nullptr;
+ }
+
+ // Recover from misplaced ellipsis.
+ bool AlreadyHasEllipsis = EllipsisLoc.isValid();
+ if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
+ DiagnoseMisplacedEllipsis(EllipsisLoc, NameLoc, AlreadyHasEllipsis, true);
+
+ TemplateParameterList *ParamList = Actions.ActOnTemplateParameterList(
+ Depth, SourceLocation(), TemplateLoc, LAngleLoc, TemplateParams,
+ RAngleLoc, OptionalRequiresClauseConstraintER.get());
+
+ // Grab a default argument (if available).
+ // Per C++0x [basic.scope.pdecl]p9, we parse the default argument before
+ // we introduce the template parameter into the local scope.
+ SourceLocation EqualLoc;
+ ParsedTemplateArgument DefaultArg;
+ if (TryConsumeToken(tok::equal, EqualLoc)) {
+ DefaultArg = ParseTemplateTemplateArgument();
+ if (DefaultArg.isInvalid()) {
+ Diag(Tok.getLocation(),
+ diag::err_default_template_template_parameter_not_template);
+ SkipUntil(tok::comma, tok::greater, tok::greatergreater,
+ StopAtSemi | StopBeforeMatch);
+ }
+ }
+
+ return Actions.ActOnTemplateTemplateParameter(getCurScope(), TemplateLoc,
+ ParamList, EllipsisLoc,
+ ParamName, NameLoc, Depth,
+ Position, EqualLoc, DefaultArg);
+}
+
+/// ParseNonTypeTemplateParameter - Handle the parsing of non-type
+/// template parameters (e.g., in "template<int Size> class array;").
+///
+/// template-parameter:
+/// ...
+/// parameter-declaration
+NamedDecl *
+Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) {
+ // Parse the declaration-specifiers (i.e., the type).
+ // FIXME: The type should probably be restricted in some way... Not all
+ // declarators (parts of declarators?) are accepted for parameters.
+ DeclSpec DS(AttrFactory);
+ ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none,
+ DeclSpecContext::DSC_template_param);
+
+ // Parse this as a typename.
+ Declarator ParamDecl(DS, ParsedAttributesView::none(),
+ DeclaratorContext::TemplateParam);
+ ParseDeclarator(ParamDecl);
+ if (DS.getTypeSpecType() == DeclSpec::TST_unspecified) {
+ Diag(Tok.getLocation(), diag::err_expected_template_parameter);
+ return nullptr;
+ }
+
+ // Recover from misplaced ellipsis.
+ SourceLocation EllipsisLoc;
+ if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
+ DiagnoseMisplacedEllipsisInDeclarator(EllipsisLoc, ParamDecl);
+
+ // If there is a default value, parse it.
+ // Per C++0x [basic.scope.pdecl]p9, we parse the default argument before
+ // we introduce the template parameter into the local scope.
+ SourceLocation EqualLoc;
+ ExprResult DefaultArg;
+ if (TryConsumeToken(tok::equal, EqualLoc)) {
+ if (Tok.is(tok::l_paren) && NextToken().is(tok::l_brace)) {
+ Diag(Tok.getLocation(), diag::err_stmt_expr_in_default_arg) << 1;
+ SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch);
+ } else {
+ // C++ [temp.param]p15:
+ // When parsing a default template-argument for a non-type
+ // template-parameter, the first non-nested > is taken as the
+ // end of the template-parameter-list rather than a greater-than
+ // operator.
+ GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ DefaultArg =
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+ if (DefaultArg.isInvalid())
+ SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch);
+ }
+ }
+
+ // Create the parameter.
+ return Actions.ActOnNonTypeTemplateParameter(getCurScope(), ParamDecl,
+ Depth, Position, EqualLoc,
+ DefaultArg.get());
+}
+
+void Parser::DiagnoseMisplacedEllipsis(SourceLocation EllipsisLoc,
+ SourceLocation CorrectLoc,
+ bool AlreadyHasEllipsis,
+ bool IdentifierHasName) {
+ FixItHint Insertion;
+ if (!AlreadyHasEllipsis)
+ Insertion = FixItHint::CreateInsertion(CorrectLoc, "...");
+ Diag(EllipsisLoc, diag::err_misplaced_ellipsis_in_declaration)
+ << FixItHint::CreateRemoval(EllipsisLoc) << Insertion
+ << !IdentifierHasName;
+}
+
+void Parser::DiagnoseMisplacedEllipsisInDeclarator(SourceLocation EllipsisLoc,
+ Declarator &D) {
+ assert(EllipsisLoc.isValid());
+ bool AlreadyHasEllipsis = D.getEllipsisLoc().isValid();
+ if (!AlreadyHasEllipsis)
+ D.setEllipsisLoc(EllipsisLoc);
+ DiagnoseMisplacedEllipsis(EllipsisLoc, D.getIdentifierLoc(),
+ AlreadyHasEllipsis, D.hasName());
+}
+
+/// Parses a '>' at the end of a template list.
+///
+/// If this function encounters '>>', '>>>', '>=', or '>>=', it tries
+/// to determine if these tokens were supposed to be a '>' followed by
+/// '>', '>>', '>=', or '>='. It emits an appropriate diagnostic if necessary.
+///
+/// \param RAngleLoc the location of the consumed '>'.
+///
+/// \param ConsumeLastToken if true, the '>' is consumed.
+///
+/// \param ObjCGenericList if true, this is the '>' closing an Objective-C
+/// type parameter or type argument list, rather than a C++ template parameter
+/// or argument list.
+///
+/// \returns true, if current token does not start with '>', false otherwise.
+bool Parser::ParseGreaterThanInTemplateList(SourceLocation LAngleLoc,
+ SourceLocation &RAngleLoc,
+ bool ConsumeLastToken,
+ bool ObjCGenericList) {
+ // What will be left once we've consumed the '>'.
+ tok::TokenKind RemainingToken;
+ const char *ReplacementStr = "> >";
+ bool MergeWithNextToken = false;
+
+ switch (Tok.getKind()) {
+ default:
+ Diag(getEndOfPreviousToken(), diag::err_expected) << tok::greater;
+ Diag(LAngleLoc, diag::note_matching) << tok::less;
+ return true;
+
+ case tok::greater:
+ // Determine the location of the '>' token. Only consume this token
+ // if the caller asked us to.
+ RAngleLoc = Tok.getLocation();
+ if (ConsumeLastToken)
+ ConsumeToken();
+ return false;
+
+ case tok::greatergreater:
+ RemainingToken = tok::greater;
+ break;
+
+ case tok::greatergreatergreater:
+ RemainingToken = tok::greatergreater;
+ break;
+
+ case tok::greaterequal:
+ RemainingToken = tok::equal;
+ ReplacementStr = "> =";
+
+ // Join two adjacent '=' tokens into one, for cases like:
+ // void (*p)() = f<int>;
+ // return f<int>==p;
+ if (NextToken().is(tok::equal) &&
+ areTokensAdjacent(Tok, NextToken())) {
+ RemainingToken = tok::equalequal;
+ MergeWithNextToken = true;
+ }
+ break;
+
+ case tok::greatergreaterequal:
+ RemainingToken = tok::greaterequal;
+ break;
+ }
+
+ // This template-id is terminated by a token that starts with a '>'.
+ // Outside C++11 and Objective-C, this is now error recovery.
+ //
+ // C++11 allows this when the token is '>>', and in CUDA + C++11 mode, we
+ // extend that treatment to also apply to the '>>>' token.
+ //
+ // Objective-C allows this in its type parameter / argument lists.
+
+ SourceLocation TokBeforeGreaterLoc = PrevTokLocation;
+ SourceLocation TokLoc = Tok.getLocation();
+ Token Next = NextToken();
+
+ // Whether splitting the current token after the '>' would undesirably result
+ // in the remaining token pasting with the token after it. This excludes the
+ // MergeWithNextToken cases, which we've already handled.
+ bool PreventMergeWithNextToken =
+ (RemainingToken == tok::greater ||
+ RemainingToken == tok::greatergreater) &&
+ (Next.isOneOf(tok::greater, tok::greatergreater,
+ tok::greatergreatergreater, tok::equal, tok::greaterequal,
+ tok::greatergreaterequal, tok::equalequal)) &&
+ areTokensAdjacent(Tok, Next);
+
+ // Diagnose this situation as appropriate.
+ if (!ObjCGenericList) {
+ // The source range of the replaced token(s).
+ CharSourceRange ReplacementRange = CharSourceRange::getCharRange(
+ TokLoc, Lexer::AdvanceToTokenCharacter(TokLoc, 2, PP.getSourceManager(),
+ getLangOpts()));
+
+ // A hint to put a space between the '>>'s. In order to make the hint as
+ // clear as possible, we include the characters either side of the space in
+ // the replacement, rather than just inserting a space at SecondCharLoc.
+ FixItHint Hint1 = FixItHint::CreateReplacement(ReplacementRange,
+ ReplacementStr);
+
+ // A hint to put another space after the token, if it would otherwise be
+ // lexed differently.
+ FixItHint Hint2;
+ if (PreventMergeWithNextToken)
+ Hint2 = FixItHint::CreateInsertion(Next.getLocation(), " ");
+
+ unsigned DiagId = diag::err_two_right_angle_brackets_need_space;
+ if (getLangOpts().CPlusPlus11 &&
+ (Tok.is(tok::greatergreater) || Tok.is(tok::greatergreatergreater)))
+ DiagId = diag::warn_cxx98_compat_two_right_angle_brackets;
+ else if (Tok.is(tok::greaterequal))
+ DiagId = diag::err_right_angle_bracket_equal_needs_space;
+ Diag(TokLoc, DiagId) << Hint1 << Hint2;
+ }
+
+ // Find the "length" of the resulting '>' token. This is not always 1, as it
+ // can contain escaped newlines.
+ unsigned GreaterLength = Lexer::getTokenPrefixLength(
+ TokLoc, 1, PP.getSourceManager(), getLangOpts());
+
+ // Annotate the source buffer to indicate that we split the token after the
+ // '>'. This allows us to properly find the end of, and extract the spelling
+ // of, the '>' token later.
+ RAngleLoc = PP.SplitToken(TokLoc, GreaterLength);
+
+ // Strip the initial '>' from the token.
+ bool CachingTokens = PP.IsPreviousCachedToken(Tok);
+
+ Token Greater = Tok;
+ Greater.setLocation(RAngleLoc);
+ Greater.setKind(tok::greater);
+ Greater.setLength(GreaterLength);
+
+ unsigned OldLength = Tok.getLength();
+ if (MergeWithNextToken) {
+ ConsumeToken();
+ OldLength += Tok.getLength();
+ }
+
+ Tok.setKind(RemainingToken);
+ Tok.setLength(OldLength - GreaterLength);
+
+ // Split the second token if lexing it normally would lex a different token
+ // (eg, the fifth token in 'A<B>>>' should re-lex as '>', not '>>').
+ SourceLocation AfterGreaterLoc = TokLoc.getLocWithOffset(GreaterLength);
+ if (PreventMergeWithNextToken)
+ AfterGreaterLoc = PP.SplitToken(AfterGreaterLoc, Tok.getLength());
+ Tok.setLocation(AfterGreaterLoc);
+
+ // Update the token cache to match what we just did if necessary.
+ if (CachingTokens) {
+ // If the previous cached token is being merged, delete it.
+ if (MergeWithNextToken)
+ PP.ReplacePreviousCachedToken({});
+
+ if (ConsumeLastToken)
+ PP.ReplacePreviousCachedToken({Greater, Tok});
+ else
+ PP.ReplacePreviousCachedToken({Greater});
+ }
+
+ if (ConsumeLastToken) {
+ PrevTokLocation = RAngleLoc;
+ } else {
+ PrevTokLocation = TokBeforeGreaterLoc;
+ PP.EnterToken(Tok, /*IsReinject=*/true);
+ Tok = Greater;
+ }
+
+ return false;
+}
+
+/// Parses a template-id that after the template name has
+/// already been parsed.
+///
+/// This routine takes care of parsing the enclosed template argument
+/// list ('<' template-parameter-list [opt] '>') and placing the
+/// results into a form that can be transferred to semantic analysis.
+///
+/// \param ConsumeLastToken if true, then we will consume the last
+/// token that forms the template-id. Otherwise, we will leave the
+/// last token in the stream (e.g., so that it can be replaced with an
+/// annotation token).
+bool Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
+ SourceLocation &LAngleLoc,
+ TemplateArgList &TemplateArgs,
+ SourceLocation &RAngleLoc,
+ TemplateTy Template) {
+ assert(Tok.is(tok::less) && "Must have already parsed the template-name");
+
+ // Consume the '<'.
+ LAngleLoc = ConsumeToken();
+
+ // Parse the optional template-argument-list.
+ bool Invalid = false;
+ {
+ GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
+ if (!Tok.isOneOf(tok::greater, tok::greatergreater,
+ tok::greatergreatergreater, tok::greaterequal,
+ tok::greatergreaterequal))
+ Invalid = ParseTemplateArgumentList(TemplateArgs, Template, LAngleLoc);
+
+ if (Invalid) {
+ // Try to find the closing '>'.
+ if (getLangOpts().CPlusPlus11)
+ SkipUntil(tok::greater, tok::greatergreater,
+ tok::greatergreatergreater, StopAtSemi | StopBeforeMatch);
+ else
+ SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
+ }
+ }
+
+ return ParseGreaterThanInTemplateList(LAngleLoc, RAngleLoc, ConsumeLastToken,
+ /*ObjCGenericList=*/false) ||
+ Invalid;
+}
+
+/// Replace the tokens that form a simple-template-id with an
+/// annotation token containing the complete template-id.
+///
+/// The first token in the stream must be the name of a template that
+/// is followed by a '<'. This routine will parse the complete
+/// simple-template-id and replace the tokens with a single annotation
+/// token with one of two different kinds: if the template-id names a
+/// type (and \p AllowTypeAnnotation is true), the annotation token is
+/// a type annotation that includes the optional nested-name-specifier
+/// (\p SS). Otherwise, the annotation token is a template-id
+/// annotation that does not include the optional
+/// nested-name-specifier.
+///
+/// \param Template the declaration of the template named by the first
+/// token (an identifier), as returned from \c Action::isTemplateName().
+///
+/// \param TNK the kind of template that \p Template
+/// refers to, as returned from \c Action::isTemplateName().
+///
+/// \param SS if non-NULL, the nested-name-specifier that precedes
+/// this template name.
+///
+/// \param TemplateKWLoc if valid, specifies that this template-id
+/// annotation was preceded by the 'template' keyword and gives the
+/// location of that keyword. If invalid (the default), then this
+/// template-id was not preceded by a 'template' keyword.
+///
+/// \param AllowTypeAnnotation if true (the default), then a
+/// simple-template-id that refers to a class template, template
+/// template parameter, or other template that produces a type will be
+/// replaced with a type annotation token. Otherwise, the
+/// simple-template-id is always replaced with a template-id
+/// annotation token.
+///
+/// \param TypeConstraint if true, then this is actually a type-constraint,
+/// meaning that the template argument list can be omitted (and the template in
+/// question must be a concept).
+///
+/// If an unrecoverable parse error occurs and no annotation token can be
+/// formed, this function returns true.
+///
+bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
+ CXXScopeSpec &SS,
+ SourceLocation TemplateKWLoc,
+ UnqualifiedId &TemplateName,
+ bool AllowTypeAnnotation,
+ bool TypeConstraint) {
+ assert(getLangOpts().CPlusPlus && "Can only annotate template-ids in C++");
+ assert((Tok.is(tok::less) || TypeConstraint) &&
+ "Parser isn't at the beginning of a template-id");
+ assert(!(TypeConstraint && AllowTypeAnnotation) && "type-constraint can't be "
+ "a type annotation");
+ assert((!TypeConstraint || TNK == TNK_Concept_template) && "type-constraint "
+ "must accompany a concept name");
+ assert((Template || TNK == TNK_Non_template) && "missing template name");
+
+ // Consume the template-name.
+ SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin();
+
+ // Parse the enclosed template argument list.
+ SourceLocation LAngleLoc, RAngleLoc;
+ TemplateArgList TemplateArgs;
+ bool ArgsInvalid = false;
+ if (!TypeConstraint || Tok.is(tok::less)) {
+ ArgsInvalid = ParseTemplateIdAfterTemplateName(
+ false, LAngleLoc, TemplateArgs, RAngleLoc, Template);
+ // If we couldn't recover from invalid arguments, don't form an annotation
+ // token -- we don't know how much to annotate.
+ // FIXME: This can lead to duplicate diagnostics if we retry parsing this
+ // template-id in another context. Try to annotate anyway?
+ if (RAngleLoc.isInvalid())
+ return true;
+ }
+
+ ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs);
+
+ // Build the annotation token.
+ if (TNK == TNK_Type_template && AllowTypeAnnotation) {
+ TypeResult Type = ArgsInvalid
+ ? TypeError()
+ : Actions.ActOnTemplateIdType(
+ getCurScope(), SS, TemplateKWLoc, Template,
+ TemplateName.Identifier, TemplateNameLoc,
+ LAngleLoc, TemplateArgsPtr, RAngleLoc);
+
+ Tok.setKind(tok::annot_typename);
+ setTypeAnnotation(Tok, Type);
+ if (SS.isNotEmpty())
+ Tok.setLocation(SS.getBeginLoc());
+ else if (TemplateKWLoc.isValid())
+ Tok.setLocation(TemplateKWLoc);
+ else
+ Tok.setLocation(TemplateNameLoc);
+ } else {
+ // Build a template-id annotation token that can be processed
+ // later.
+ Tok.setKind(tok::annot_template_id);
+
+ IdentifierInfo *TemplateII =
+ TemplateName.getKind() == UnqualifiedIdKind::IK_Identifier
+ ? TemplateName.Identifier
+ : nullptr;
+
+ OverloadedOperatorKind OpKind =
+ TemplateName.getKind() == UnqualifiedIdKind::IK_Identifier
+ ? OO_None
+ : TemplateName.OperatorFunctionId.Operator;
+
+ TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create(
+ TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK,
+ LAngleLoc, RAngleLoc, TemplateArgs, ArgsInvalid, TemplateIds);
+
+ Tok.setAnnotationValue(TemplateId);
+ if (TemplateKWLoc.isValid())
+ Tok.setLocation(TemplateKWLoc);
+ else
+ Tok.setLocation(TemplateNameLoc);
+ }
+
+ // Common fields for the annotation token
+ Tok.setAnnotationEndLoc(RAngleLoc);
+
+ // In case the tokens were cached, have Preprocessor replace them with the
+ // annotation token.
+ PP.AnnotateCachedTokens(Tok);
+ return false;
+}
+
+/// Replaces a template-id annotation token with a type
+/// annotation token.
+///
+/// If there was a failure when forming the type from the template-id,
+/// a type annotation token will still be created, but will have a
+/// NULL type pointer to signify an error.
+///
+/// \param SS The scope specifier appearing before the template-id, if any.
+///
+/// \param AllowImplicitTypename whether this is a context where T::type
+/// denotes a dependent type.
+/// \param IsClassName Is this template-id appearing in a context where we
+/// know it names a class, such as in an elaborated-type-specifier or
+/// base-specifier? ('typename' and 'template' are unneeded and disallowed
+/// in those contexts.)
+void Parser::AnnotateTemplateIdTokenAsType(
+ CXXScopeSpec &SS, ImplicitTypenameContext AllowImplicitTypename,
+ bool IsClassName) {
+ assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens");
+
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ assert(TemplateId->mightBeType() &&
+ "Only works for type and dependent templates");
+
+ ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
+ TemplateId->NumArgs);
+
+ TypeResult Type =
+ TemplateId->isInvalid()
+ ? TypeError()
+ : Actions.ActOnTemplateIdType(
+ getCurScope(), SS, TemplateId->TemplateKWLoc,
+ TemplateId->Template, TemplateId->Name,
+ TemplateId->TemplateNameLoc, TemplateId->LAngleLoc,
+ TemplateArgsPtr, TemplateId->RAngleLoc,
+ /*IsCtorOrDtorName=*/false, IsClassName, AllowImplicitTypename);
+ // Create the new "type" annotation token.
+ Tok.setKind(tok::annot_typename);
+ setTypeAnnotation(Tok, Type);
+ if (SS.isNotEmpty()) // it was a C++ qualified type name.
+ Tok.setLocation(SS.getBeginLoc());
+ // End location stays the same
+
+ // Replace the template-id annotation token, and possible the scope-specifier
+ // that precedes it, with the typename annotation token.
+ PP.AnnotateCachedTokens(Tok);
+}
+
+/// Determine whether the given token can end a template argument.
+static bool isEndOfTemplateArgument(Token Tok) {
+ // FIXME: Handle '>>>'.
+ return Tok.isOneOf(tok::comma, tok::greater, tok::greatergreater,
+ tok::greatergreatergreater);
+}
+
+/// Parse a C++ template template argument.
+ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
+ if (!Tok.is(tok::identifier) && !Tok.is(tok::coloncolon) &&
+ !Tok.is(tok::annot_cxxscope))
+ return ParsedTemplateArgument();
+
+ // C++0x [temp.arg.template]p1:
+ // A template-argument for a template template-parameter shall be the name
+ // of a class template or an alias template, expressed as id-expression.
+ //
+ // We parse an id-expression that refers to a class template or alias
+ // template. The grammar we parse is:
+ //
+ // nested-name-specifier[opt] template[opt] identifier ...[opt]
+ //
+ // followed by a token that terminates a template argument, such as ',',
+ // '>', or (in some cases) '>>'.
+ CXXScopeSpec SS; // nested-name-specifier, if present
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false);
+
+ ParsedTemplateArgument Result;
+ SourceLocation EllipsisLoc;
+ if (SS.isSet() && Tok.is(tok::kw_template)) {
+ // Parse the optional 'template' keyword following the
+ // nested-name-specifier.
+ SourceLocation TemplateKWLoc = ConsumeToken();
+
+ if (Tok.is(tok::identifier)) {
+ // We appear to have a dependent template name.
+ UnqualifiedId Name;
+ Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ ConsumeToken(); // the identifier
+
+ TryConsumeToken(tok::ellipsis, EllipsisLoc);
+
+ // If the next token signals the end of a template argument, then we have
+ // a (possibly-dependent) template name that could be a template template
+ // argument.
+ TemplateTy Template;
+ if (isEndOfTemplateArgument(Tok) &&
+ Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Name,
+ /*ObjectType=*/nullptr,
+ /*EnteringContext=*/false, Template))
+ Result = ParsedTemplateArgument(SS, Template, Name.StartLocation);
+ }
+ } else if (Tok.is(tok::identifier)) {
+ // We may have a (non-dependent) template name.
+ TemplateTy Template;
+ UnqualifiedId Name;
+ Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ ConsumeToken(); // the identifier
+
+ TryConsumeToken(tok::ellipsis, EllipsisLoc);
+
+ if (isEndOfTemplateArgument(Tok)) {
+ bool MemberOfUnknownSpecialization;
+ TemplateNameKind TNK = Actions.isTemplateName(
+ getCurScope(), SS,
+ /*hasTemplateKeyword=*/false, Name,
+ /*ObjectType=*/nullptr,
+ /*EnteringContext=*/false, Template, MemberOfUnknownSpecialization);
+ if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template) {
+ // We have an id-expression that refers to a class template or
+ // (C++0x) alias template.
+ Result = ParsedTemplateArgument(SS, Template, Name.StartLocation);
+ }
+ }
+ }
+
+ // If this is a pack expansion, build it as such.
+ if (EllipsisLoc.isValid() && !Result.isInvalid())
+ Result = Actions.ActOnPackExpansion(Result, EllipsisLoc);
+
+ return Result;
+}
+
+/// ParseTemplateArgument - Parse a C++ template argument (C++ [temp.names]).
+///
+/// template-argument: [C++ 14.2]
+/// constant-expression
+/// type-id
+/// id-expression
+ParsedTemplateArgument Parser::ParseTemplateArgument() {
+ // C++ [temp.arg]p2:
+ // In a template-argument, an ambiguity between a type-id and an
+ // expression is resolved to a type-id, regardless of the form of
+ // the corresponding template-parameter.
+ //
+ // Therefore, we initially try to parse a type-id - and isCXXTypeId might look
+ // up and annotate an identifier as an id-expression during disambiguation,
+ // so enter the appropriate context for a constant expression template
+ // argument before trying to disambiguate.
+
+ EnterExpressionEvaluationContext EnterConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+ /*LambdaContextDecl=*/nullptr,
+ /*ExprContext=*/Sema::ExpressionEvaluationContextRecord::EK_TemplateArgument);
+ if (isCXXTypeId(TypeIdAsTemplateArgument)) {
+ TypeResult TypeArg = ParseTypeName(
+ /*Range=*/nullptr, DeclaratorContext::TemplateArg);
+ return Actions.ActOnTemplateTypeArgument(TypeArg);
+ }
+
+ // Try to parse a template template argument.
+ {
+ TentativeParsingAction TPA(*this);
+
+ ParsedTemplateArgument TemplateTemplateArgument
+ = ParseTemplateTemplateArgument();
+ if (!TemplateTemplateArgument.isInvalid()) {
+ TPA.Commit();
+ return TemplateTemplateArgument;
+ }
+
+ // Revert this tentative parse to parse a non-type template argument.
+ TPA.Revert();
+ }
+
+ // Parse a non-type template argument.
+ SourceLocation Loc = Tok.getLocation();
+ ExprResult ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast);
+ if (ExprArg.isInvalid() || !ExprArg.get()) {
+ return ParsedTemplateArgument();
+ }
+
+ return ParsedTemplateArgument(ParsedTemplateArgument::NonType,
+ ExprArg.get(), Loc);
+}
+
+/// ParseTemplateArgumentList - Parse a C++ template-argument-list
+/// (C++ [temp.names]). Returns true if there was an error.
+///
+/// template-argument-list: [C++ 14.2]
+/// template-argument
+/// template-argument-list ',' template-argument
+///
+/// \param Template is only used for code completion, and may be null.
+bool Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
+ TemplateTy Template,
+ SourceLocation OpenLoc) {
+
+ ColonProtectionRAIIObject ColonProtection(*this, false);
+
+ auto RunSignatureHelp = [&] {
+ if (!Template)
+ return QualType();
+ CalledSignatureHelp = true;
+ return Actions.ProduceTemplateArgumentSignatureHelp(Template, TemplateArgs,
+ OpenLoc);
+ };
+
+ do {
+ PreferredType.enterFunctionArgument(Tok.getLocation(), RunSignatureHelp);
+ ParsedTemplateArgument Arg = ParseTemplateArgument();
+ SourceLocation EllipsisLoc;
+ if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
+ Arg = Actions.ActOnPackExpansion(Arg, EllipsisLoc);
+
+ if (Arg.isInvalid()) {
+ if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
+ RunSignatureHelp();
+ return true;
+ }
+
+ // Save this template argument.
+ TemplateArgs.push_back(Arg);
+
+ // If the next token is a comma, consume it and keep reading
+ // arguments.
+ } while (TryConsumeToken(tok::comma));
+
+ return false;
+}
+
+/// Parse a C++ explicit template instantiation
+/// (C++ [temp.explicit]).
+///
+/// explicit-instantiation:
+/// 'extern' [opt] 'template' declaration
+///
+/// Note that the 'extern' is a GNU extension and C++11 feature.
+Decl *Parser::ParseExplicitInstantiation(DeclaratorContext Context,
+ SourceLocation ExternLoc,
+ SourceLocation TemplateLoc,
+ SourceLocation &DeclEnd,
+ ParsedAttributes &AccessAttrs,
+ AccessSpecifier AS) {
+ // This isn't really required here.
+ ParsingDeclRAIIObject
+ ParsingTemplateParams(*this, ParsingDeclRAIIObject::NoParent);
+
+ return ParseSingleDeclarationAfterTemplate(
+ Context, ParsedTemplateInfo(ExternLoc, TemplateLoc),
+ ParsingTemplateParams, DeclEnd, AccessAttrs, AS);
+}
+
+SourceRange Parser::ParsedTemplateInfo::getSourceRange() const {
+ if (TemplateParams)
+ return getTemplateParamsRange(TemplateParams->data(),
+ TemplateParams->size());
+
+ SourceRange R(TemplateLoc);
+ if (ExternLoc.isValid())
+ R.setBegin(ExternLoc);
+ return R;
+}
+
+void Parser::LateTemplateParserCallback(void *P, LateParsedTemplate &LPT) {
+ ((Parser *)P)->ParseLateTemplatedFuncDef(LPT);
+}
+
+/// Late parse a C++ function template in Microsoft mode.
+void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) {
+ if (!LPT.D)
+ return;
+
+ // Destroy TemplateIdAnnotations when we're done, if possible.
+ DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this);
+
+ // Get the FunctionDecl.
+ FunctionDecl *FunD = LPT.D->getAsFunction();
+ // Track template parameter depth.
+ TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
+
+ // To restore the context after late parsing.
+ Sema::ContextRAII GlobalSavedContext(
+ Actions, Actions.Context.getTranslationUnitDecl());
+
+ MultiParseScope Scopes(*this);
+
+ // Get the list of DeclContexts to reenter.
+ SmallVector<DeclContext*, 4> DeclContextsToReenter;
+ for (DeclContext *DC = FunD; DC && !DC->isTranslationUnit();
+ DC = DC->getLexicalParent())
+ DeclContextsToReenter.push_back(DC);
+
+ // Reenter scopes from outermost to innermost.
+ for (DeclContext *DC : reverse(DeclContextsToReenter)) {
+ CurTemplateDepthTracker.addDepth(
+ ReenterTemplateScopes(Scopes, cast<Decl>(DC)));
+ Scopes.Enter(Scope::DeclScope);
+ // We'll reenter the function context itself below.
+ if (DC != FunD)
+ Actions.PushDeclContext(Actions.getCurScope(), DC);
+ }
+
+ assert(!LPT.Toks.empty() && "Empty body!");
+
+ // Append the current token at the end of the new token stream so that it
+ // doesn't get lost.
+ LPT.Toks.push_back(Tok);
+ PP.EnterTokenStream(LPT.Toks, true, /*IsReinject*/true);
+
+ // Consume the previously pushed token.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+ assert(Tok.isOneOf(tok::l_brace, tok::colon, tok::kw_try) &&
+ "Inline method not starting with '{', ':' or 'try'");
+
+ // Parse the method body. Function body parsing code is similar enough
+ // to be re-used for method bodies as well.
+ ParseScope FnScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+
+ // Recreate the containing function DeclContext.
+ Sema::ContextRAII FunctionSavedContext(Actions, FunD->getLexicalParent());
+
+ Actions.ActOnStartOfFunctionDef(getCurScope(), FunD);
+
+ if (Tok.is(tok::kw_try)) {
+ ParseFunctionTryBlock(LPT.D, FnScope);
+ } else {
+ if (Tok.is(tok::colon))
+ ParseConstructorInitializer(LPT.D);
+ else
+ Actions.ActOnDefaultCtorInitializers(LPT.D);
+
+ if (Tok.is(tok::l_brace)) {
+ assert((!isa<FunctionTemplateDecl>(LPT.D) ||
+ cast<FunctionTemplateDecl>(LPT.D)
+ ->getTemplateParameters()
+ ->getDepth() == TemplateParameterDepth - 1) &&
+ "TemplateParameterDepth should be greater than the depth of "
+ "current template being instantiated!");
+ ParseFunctionStatementBody(LPT.D, FnScope);
+ Actions.UnmarkAsLateParsedTemplate(FunD);
+ } else
+ Actions.ActOnFinishFunctionBody(LPT.D, nullptr);
+ }
+}
+
+/// Lex a delayed template function for late parsing.
+void Parser::LexTemplateFunctionForLateParsing(CachedTokens &Toks) {
+ tok::TokenKind kind = Tok.getKind();
+ if (!ConsumeAndStoreFunctionPrologue(Toks)) {
+ // Consume everything up to (and including) the matching right brace.
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ }
+
+ // If we're in a function-try-block, we need to store all the catch blocks.
+ if (kind == tok::kw_try) {
+ while (Tok.is(tok::kw_catch)) {
+ ConsumeAndStoreUntil(tok::l_brace, Toks, /*StopAtSemi=*/false);
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ }
+ }
+}
+
+/// We've parsed something that could plausibly be intended to be a template
+/// name (\p LHS) followed by a '<' token, and the following code can't possibly
+/// be an expression. Determine if this is likely to be a template-id and if so,
+/// diagnose it.
+bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) {
+ TentativeParsingAction TPA(*this);
+ // FIXME: We could look at the token sequence in a lot more detail here.
+ if (SkipUntil(tok::greater, tok::greatergreater, tok::greatergreatergreater,
+ StopAtSemi | StopBeforeMatch)) {
+ TPA.Commit();
+
+ SourceLocation Greater;
+ ParseGreaterThanInTemplateList(Less, Greater, true, false);
+ Actions.diagnoseExprIntendedAsTemplateName(getCurScope(), LHS,
+ Less, Greater);
+ return true;
+ }
+
+ // There's no matching '>' token, this probably isn't supposed to be
+ // interpreted as a template-id. Parse it as an (ill-formed) comparison.
+ TPA.Revert();
+ return false;
+}
+
+void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) {
+ assert(Tok.is(tok::less) && "not at a potential angle bracket");
+
+ bool DependentTemplateName = false;
+ if (!Actions.mightBeIntendedToBeTemplateName(PotentialTemplateName,
+ DependentTemplateName))
+ return;
+
+ // OK, this might be a name that the user intended to be parsed as a
+ // template-name, followed by a '<' token. Check for some easy cases.
+
+ // If we have potential_template<>, then it's supposed to be a template-name.
+ if (NextToken().is(tok::greater) ||
+ (getLangOpts().CPlusPlus11 &&
+ NextToken().isOneOf(tok::greatergreater, tok::greatergreatergreater))) {
+ SourceLocation Less = ConsumeToken();
+ SourceLocation Greater;
+ ParseGreaterThanInTemplateList(Less, Greater, true, false);
+ Actions.diagnoseExprIntendedAsTemplateName(
+ getCurScope(), PotentialTemplateName, Less, Greater);
+ // FIXME: Perform error recovery.
+ PotentialTemplateName = ExprError();
+ return;
+ }
+
+ // If we have 'potential_template<type-id', assume it's supposed to be a
+ // template-name if there's a matching '>' later on.
+ {
+ // FIXME: Avoid the tentative parse when NextToken() can't begin a type.
+ TentativeParsingAction TPA(*this);
+ SourceLocation Less = ConsumeToken();
+ if (isTypeIdUnambiguously() &&
+ diagnoseUnknownTemplateId(PotentialTemplateName, Less)) {
+ TPA.Commit();
+ // FIXME: Perform error recovery.
+ PotentialTemplateName = ExprError();
+ return;
+ }
+ TPA.Revert();
+ }
+
+ // Otherwise, remember that we saw this in case we see a potentially-matching
+ // '>' token later on.
+ AngleBracketTracker::Priority Priority =
+ (DependentTemplateName ? AngleBracketTracker::DependentName
+ : AngleBracketTracker::PotentialTypo) |
+ (Tok.hasLeadingSpace() ? AngleBracketTracker::SpaceBeforeLess
+ : AngleBracketTracker::NoSpaceBeforeLess);
+ AngleBrackets.add(*this, PotentialTemplateName.get(), Tok.getLocation(),
+ Priority);
+}
+
+bool Parser::checkPotentialAngleBracketDelimiter(
+ const AngleBracketTracker::Loc &LAngle, const Token &OpToken) {
+ // If a comma in an expression context is followed by a type that can be a
+ // template argument and cannot be an expression, then this is ill-formed,
+ // but might be intended to be part of a template-id.
+ if (OpToken.is(tok::comma) && isTypeIdUnambiguously() &&
+ diagnoseUnknownTemplateId(LAngle.TemplateName, LAngle.LessLoc)) {
+ AngleBrackets.clear(*this);
+ return true;
+ }
+
+ // If a context that looks like a template-id is followed by '()', then
+ // this is ill-formed, but might be intended to be a template-id
+ // followed by '()'.
+ if (OpToken.is(tok::greater) && Tok.is(tok::l_paren) &&
+ NextToken().is(tok::r_paren)) {
+ Actions.diagnoseExprIntendedAsTemplateName(
+ getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
+ OpToken.getLocation());
+ AngleBrackets.clear(*this);
+ return true;
+ }
+
+ // After a '>' (etc), we're no longer potentially in a construct that's
+ // intended to be treated as a template-id.
+ if (OpToken.is(tok::greater) ||
+ (getLangOpts().CPlusPlus11 &&
+ OpToken.isOneOf(tok::greatergreater, tok::greatergreatergreater)))
+ AngleBrackets.clear(*this);
+ return false;
+}
diff --git a/contrib/libs/clang16/lib/Parse/ParseTentative.cpp b/contrib/libs/clang16/lib/Parse/ParseTentative.cpp
new file mode 100644
index 0000000000..785749bff6
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ParseTentative.cpp
@@ -0,0 +1,2253 @@
+//===--- ParseTentative.cpp - Ambiguity Resolution Parsing ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the tentative parsing portions of the Parser
+// interfaces, for ambiguity resolution.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Sema/ParsedTemplate.h"
+using namespace clang;
+
+/// isCXXDeclarationStatement - C++-specialized function that disambiguates
+/// between a declaration or an expression statement, when parsing function
+/// bodies. Returns true for declaration, false for expression.
+///
+/// declaration-statement:
+/// block-declaration
+///
+/// block-declaration:
+/// simple-declaration
+/// asm-definition
+/// namespace-alias-definition
+/// using-declaration
+/// using-directive
+/// [C++0x] static_assert-declaration
+///
+/// asm-definition:
+/// 'asm' '(' string-literal ')' ';'
+///
+/// namespace-alias-definition:
+/// 'namespace' identifier = qualified-namespace-specifier ';'
+///
+/// using-declaration:
+/// 'using' typename[opt] '::'[opt] nested-name-specifier
+/// unqualified-id ';'
+/// 'using' '::' unqualified-id ;
+///
+/// using-directive:
+/// 'using' 'namespace' '::'[opt] nested-name-specifier[opt]
+/// namespace-name ';'
+///
+bool Parser::isCXXDeclarationStatement(
+ bool DisambiguatingWithExpression /*=false*/) {
+ assert(getLangOpts().CPlusPlus && "Must be called for C++ only.");
+
+ switch (Tok.getKind()) {
+ // asm-definition
+ case tok::kw_asm:
+ // namespace-alias-definition
+ case tok::kw_namespace:
+ // using-declaration
+ // using-directive
+ case tok::kw_using:
+ // static_assert-declaration
+ case tok::kw_static_assert:
+ case tok::kw__Static_assert:
+ return true;
+ case tok::identifier: {
+ if (DisambiguatingWithExpression) {
+ RevertingTentativeParsingAction TPA(*this);
+ // Parse the C++ scope specifier.
+ CXXScopeSpec SS;
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/true);
+
+ switch (Tok.getKind()) {
+ case tok::identifier: {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ bool isDeductionGuide =
+ Actions.isDeductionGuideName(getCurScope(), *II, Tok.getLocation(),
+ /*Template=*/nullptr);
+ if (Actions.isCurrentClassName(*II, getCurScope(), &SS) ||
+ isDeductionGuide) {
+ if (isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(),
+ isDeductionGuide,
+ DeclSpec::FriendSpecified::No))
+ return true;
+ }
+ break;
+ }
+ case tok::kw_operator:
+ return true;
+ case tok::annot_cxxscope: // Check if this is a dtor.
+ if (NextToken().is(tok::tilde))
+ return true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ [[fallthrough]];
+ // simple-declaration
+ default:
+ return isCXXSimpleDeclaration(/*AllowForRangeDecl=*/false);
+ }
+}
+
+/// isCXXSimpleDeclaration - C++-specialized function that disambiguates
+/// between a simple-declaration or an expression-statement.
+/// If during the disambiguation process a parsing error is encountered,
+/// the function returns true to let the declaration parsing code handle it.
+/// Returns false if the statement is disambiguated as expression.
+///
+/// simple-declaration:
+/// decl-specifier-seq init-declarator-list[opt] ';'
+/// decl-specifier-seq ref-qualifier[opt] '[' identifier-list ']'
+/// brace-or-equal-initializer ';' [C++17]
+///
+/// (if AllowForRangeDecl specified)
+/// for ( for-range-declaration : for-range-initializer ) statement
+///
+/// for-range-declaration:
+/// decl-specifier-seq declarator
+/// decl-specifier-seq ref-qualifier[opt] '[' identifier-list ']'
+///
+/// In any of the above cases there can be a preceding attribute-specifier-seq,
+/// but the caller is expected to handle that.
+bool Parser::isCXXSimpleDeclaration(bool AllowForRangeDecl) {
+ // C++ 6.8p1:
+ // There is an ambiguity in the grammar involving expression-statements and
+ // declarations: An expression-statement with a function-style explicit type
+ // conversion (5.2.3) as its leftmost subexpression can be indistinguishable
+ // from a declaration where the first declarator starts with a '('. In those
+ // cases the statement is a declaration. [Note: To disambiguate, the whole
+ // statement might have to be examined to determine if it is an
+ // expression-statement or a declaration].
+
+ // C++ 6.8p3:
+ // The disambiguation is purely syntactic; that is, the meaning of the names
+ // occurring in such a statement, beyond whether they are type-names or not,
+ // is not generally used in or changed by the disambiguation. Class
+ // templates are instantiated as necessary to determine if a qualified name
+ // is a type-name. Disambiguation precedes parsing, and a statement
+ // disambiguated as a declaration may be an ill-formed declaration.
+
+ // We don't have to parse all of the decl-specifier-seq part. There's only
+ // an ambiguity if the first decl-specifier is
+ // simple-type-specifier/typename-specifier followed by a '(', which may
+ // indicate a function-style cast expression.
+ // isCXXDeclarationSpecifier will return TPResult::Ambiguous only in such
+ // a case.
+
+ bool InvalidAsDeclaration = false;
+ TPResult TPR = isCXXDeclarationSpecifier(
+ ImplicitTypenameContext::No, TPResult::False, &InvalidAsDeclaration);
+ if (TPR != TPResult::Ambiguous)
+ return TPR != TPResult::False; // Returns true for TPResult::True or
+ // TPResult::Error.
+
+ // FIXME: TryParseSimpleDeclaration doesn't look past the first initializer,
+ // and so gets some cases wrong. We can't carry on if we've already seen
+ // something which makes this statement invalid as a declaration in this case,
+ // since it can cause us to misparse valid code. Revisit this once
+ // TryParseInitDeclaratorList is fixed.
+ if (InvalidAsDeclaration)
+ return false;
+
+ // FIXME: Add statistics about the number of ambiguous statements encountered
+ // and how they were resolved (number of declarations+number of expressions).
+
+ // Ok, we have a simple-type-specifier/typename-specifier followed by a '(',
+ // or an identifier which doesn't resolve as anything. We need tentative
+ // parsing...
+
+ {
+ RevertingTentativeParsingAction PA(*this);
+ TPR = TryParseSimpleDeclaration(AllowForRangeDecl);
+ }
+
+ // In case of an error, let the declaration parsing code handle it.
+ if (TPR == TPResult::Error)
+ return true;
+
+ // Declarations take precedence over expressions.
+ if (TPR == TPResult::Ambiguous)
+ TPR = TPResult::True;
+
+ assert(TPR == TPResult::True || TPR == TPResult::False);
+ return TPR == TPResult::True;
+}
+
+/// Try to consume a token sequence that we've already identified as
+/// (potentially) starting a decl-specifier.
+Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
+ switch (Tok.getKind()) {
+ case tok::kw__Atomic:
+ if (NextToken().isNot(tok::l_paren)) {
+ ConsumeToken();
+ break;
+ }
+ [[fallthrough]];
+ case tok::kw_typeof:
+ case tok::kw___attribute:
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
+ {
+ ConsumeToken();
+ if (Tok.isNot(tok::l_paren))
+ return TPResult::Error;
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren))
+ return TPResult::Error;
+ break;
+ }
+
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw_union:
+ case tok::kw___interface:
+ case tok::kw_enum:
+ // elaborated-type-specifier:
+ // class-key attribute-specifier-seq[opt]
+ // nested-name-specifier[opt] identifier
+ // class-key nested-name-specifier[opt] template[opt] simple-template-id
+ // enum nested-name-specifier[opt] identifier
+ //
+ // FIXME: We don't support class-specifiers nor enum-specifiers here.
+ ConsumeToken();
+
+ // Skip attributes.
+ if (!TrySkipAttributes())
+ return TPResult::Error;
+
+ if (TryAnnotateOptionalCXXScopeToken())
+ return TPResult::Error;
+ if (Tok.is(tok::annot_cxxscope))
+ ConsumeAnnotationToken();
+ if (Tok.is(tok::identifier))
+ ConsumeToken();
+ else if (Tok.is(tok::annot_template_id))
+ ConsumeAnnotationToken();
+ else
+ return TPResult::Error;
+ break;
+
+ case tok::annot_cxxscope:
+ ConsumeAnnotationToken();
+ [[fallthrough]];
+ default:
+ ConsumeAnyToken();
+
+ if (getLangOpts().ObjC && Tok.is(tok::less))
+ return TryParseProtocolQualifiers();
+ break;
+ }
+
+ return TPResult::Ambiguous;
+}
+
+/// simple-declaration:
+/// decl-specifier-seq init-declarator-list[opt] ';'
+///
+/// (if AllowForRangeDecl specified)
+/// for ( for-range-declaration : for-range-initializer ) statement
+/// for-range-declaration:
+/// attribute-specifier-seqopt type-specifier-seq declarator
+///
+Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error)
+ return TPResult::Error;
+
+ // Two decl-specifiers in a row conclusively disambiguate this as being a
+ // simple-declaration. Don't bother calling isCXXDeclarationSpecifier in the
+ // overwhelmingly common case that the next token is a '('.
+ if (Tok.isNot(tok::l_paren)) {
+ TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
+ if (TPR == TPResult::Ambiguous)
+ return TPResult::True;
+ if (TPR == TPResult::True || TPR == TPResult::Error)
+ return TPR;
+ assert(TPR == TPResult::False);
+ }
+
+ TPResult TPR = TryParseInitDeclaratorList();
+ if (TPR != TPResult::Ambiguous)
+ return TPR;
+
+ if (Tok.isNot(tok::semi) && (!AllowForRangeDecl || Tok.isNot(tok::colon)))
+ return TPResult::False;
+
+ return TPResult::Ambiguous;
+}
+
+/// Tentatively parse an init-declarator-list in order to disambiguate it from
+/// an expression.
+///
+/// init-declarator-list:
+/// init-declarator
+/// init-declarator-list ',' init-declarator
+///
+/// init-declarator:
+/// declarator initializer[opt]
+/// [GNU] declarator simple-asm-expr[opt] attributes[opt] initializer[opt]
+///
+/// initializer:
+/// brace-or-equal-initializer
+/// '(' expression-list ')'
+///
+/// brace-or-equal-initializer:
+/// '=' initializer-clause
+/// [C++11] braced-init-list
+///
+/// initializer-clause:
+/// assignment-expression
+/// braced-init-list
+///
+/// braced-init-list:
+/// '{' initializer-list ','[opt] '}'
+/// '{' '}'
+///
+Parser::TPResult Parser::TryParseInitDeclaratorList() {
+ while (true) {
+ // declarator
+ TPResult TPR = TryParseDeclarator(false/*mayBeAbstract*/);
+ if (TPR != TPResult::Ambiguous)
+ return TPR;
+
+ // [GNU] simple-asm-expr[opt] attributes[opt]
+ if (Tok.isOneOf(tok::kw_asm, tok::kw___attribute))
+ return TPResult::True;
+
+ // initializer[opt]
+ if (Tok.is(tok::l_paren)) {
+ // Parse through the parens.
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren, StopAtSemi))
+ return TPResult::Error;
+ } else if (Tok.is(tok::l_brace)) {
+ // A left-brace here is sufficient to disambiguate the parse; an
+ // expression can never be followed directly by a braced-init-list.
+ return TPResult::True;
+ } else if (Tok.is(tok::equal) || isTokIdentifier_in()) {
+ // MSVC and g++ won't examine the rest of declarators if '=' is
+ // encountered; they just conclude that we have a declaration.
+ // EDG parses the initializer completely, which is the proper behavior
+ // for this case.
+ //
+ // At present, Clang follows MSVC and g++, since the parser does not have
+ // the ability to parse an expression fully without recording the
+ // results of that parse.
+ // FIXME: Handle this case correctly.
+ //
+ // Also allow 'in' after an Objective-C declaration as in:
+ // for (int (^b)(void) in array). Ideally this should be done in the
+ // context of parsing for-init-statement of a foreach statement only. But,
+ // in any other context 'in' is invalid after a declaration and parser
+ // issues the error regardless of outcome of this decision.
+ // FIXME: Change if above assumption does not hold.
+ return TPResult::True;
+ }
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ return TPResult::Ambiguous;
+}
+
+struct Parser::ConditionDeclarationOrInitStatementState {
+ Parser &P;
+ bool CanBeExpression = true;
+ bool CanBeCondition = true;
+ bool CanBeInitStatement;
+ bool CanBeForRangeDecl;
+
+ ConditionDeclarationOrInitStatementState(Parser &P, bool CanBeInitStatement,
+ bool CanBeForRangeDecl)
+ : P(P), CanBeInitStatement(CanBeInitStatement),
+ CanBeForRangeDecl(CanBeForRangeDecl) {}
+
+ bool resolved() {
+ return CanBeExpression + CanBeCondition + CanBeInitStatement +
+ CanBeForRangeDecl < 2;
+ }
+
+ void markNotExpression() {
+ CanBeExpression = false;
+
+ if (!resolved()) {
+ // FIXME: Unify the parsing codepaths for condition variables and
+ // simple-declarations so that we don't need to eagerly figure out which
+ // kind we have here. (Just parse init-declarators until we reach a
+ // semicolon or right paren.)
+ RevertingTentativeParsingAction PA(P);
+ if (CanBeForRangeDecl) {
+ // Skip until we hit a ')', ';', or a ':' with no matching '?'.
+ // The final case is a for range declaration, the rest are not.
+ unsigned QuestionColonDepth = 0;
+ while (true) {
+ P.SkipUntil({tok::r_paren, tok::semi, tok::question, tok::colon},
+ StopBeforeMatch);
+ if (P.Tok.is(tok::question))
+ ++QuestionColonDepth;
+ else if (P.Tok.is(tok::colon)) {
+ if (QuestionColonDepth)
+ --QuestionColonDepth;
+ else {
+ CanBeCondition = CanBeInitStatement = false;
+ return;
+ }
+ } else {
+ CanBeForRangeDecl = false;
+ break;
+ }
+ P.ConsumeToken();
+ }
+ } else {
+ // Just skip until we hit a ')' or ';'.
+ P.SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch);
+ }
+ if (P.Tok.isNot(tok::r_paren))
+ CanBeCondition = CanBeForRangeDecl = false;
+ if (P.Tok.isNot(tok::semi))
+ CanBeInitStatement = false;
+ }
+ }
+
+ bool markNotCondition() {
+ CanBeCondition = false;
+ return resolved();
+ }
+
+ bool markNotForRangeDecl() {
+ CanBeForRangeDecl = false;
+ return resolved();
+ }
+
+ bool update(TPResult IsDecl) {
+ switch (IsDecl) {
+ case TPResult::True:
+ markNotExpression();
+ assert(resolved() && "can't continue after tentative parsing bails out");
+ break;
+ case TPResult::False:
+ CanBeCondition = CanBeInitStatement = CanBeForRangeDecl = false;
+ break;
+ case TPResult::Ambiguous:
+ break;
+ case TPResult::Error:
+ CanBeExpression = CanBeCondition = CanBeInitStatement =
+ CanBeForRangeDecl = false;
+ break;
+ }
+ return resolved();
+ }
+
+ ConditionOrInitStatement result() const {
+ assert(CanBeExpression + CanBeCondition + CanBeInitStatement +
+ CanBeForRangeDecl < 2 &&
+ "result called but not yet resolved");
+ if (CanBeExpression)
+ return ConditionOrInitStatement::Expression;
+ if (CanBeCondition)
+ return ConditionOrInitStatement::ConditionDecl;
+ if (CanBeInitStatement)
+ return ConditionOrInitStatement::InitStmtDecl;
+ if (CanBeForRangeDecl)
+ return ConditionOrInitStatement::ForRangeDecl;
+ return ConditionOrInitStatement::Error;
+ }
+};
+
+bool Parser::isEnumBase(bool AllowSemi) {
+ assert(Tok.is(tok::colon) && "should be looking at the ':'");
+
+ RevertingTentativeParsingAction PA(*this);
+ // ':'
+ ConsumeToken();
+
+ // type-specifier-seq
+ bool InvalidAsDeclSpec = false;
+ // FIXME: We could disallow non-type decl-specifiers here, but it makes no
+ // difference: those specifiers are ill-formed regardless of the
+ // interpretation.
+ TPResult R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No,
+ /*BracedCastResult=*/TPResult::True,
+ &InvalidAsDeclSpec);
+ if (R == TPResult::Ambiguous) {
+ // We either have a decl-specifier followed by '(' or an undeclared
+ // identifier.
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error)
+ return true;
+
+ // If we get to the end of the enum-base, we hit either a '{' or a ';'.
+ // Don't bother checking the enumerator-list.
+ if (Tok.is(tok::l_brace) || (AllowSemi && Tok.is(tok::semi)))
+ return true;
+
+ // A second decl-specifier unambiguously indicatges an enum-base.
+ R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::True,
+ &InvalidAsDeclSpec);
+ }
+
+ return R != TPResult::False;
+}
+
+/// Disambiguates between a declaration in a condition, a
+/// simple-declaration in an init-statement, and an expression for
+/// a condition of a if/switch statement.
+///
+/// condition:
+/// expression
+/// type-specifier-seq declarator '=' assignment-expression
+/// [C++11] type-specifier-seq declarator '=' initializer-clause
+/// [C++11] type-specifier-seq declarator braced-init-list
+/// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt]
+/// '=' assignment-expression
+/// simple-declaration:
+/// decl-specifier-seq init-declarator-list[opt] ';'
+///
+/// Note that, unlike isCXXSimpleDeclaration, we must disambiguate all the way
+/// to the ';' to disambiguate cases like 'int(x))' (an expression) from
+/// 'int(x);' (a simple-declaration in an init-statement).
+Parser::ConditionOrInitStatement
+Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
+ bool CanBeForRangeDecl) {
+ ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement,
+ CanBeForRangeDecl);
+
+ if (CanBeInitStatement && Tok.is(tok::kw_using))
+ return ConditionOrInitStatement::InitStmtDecl;
+ if (State.update(isCXXDeclarationSpecifier(ImplicitTypenameContext::No)))
+ return State.result();
+
+ // It might be a declaration; we need tentative parsing.
+ RevertingTentativeParsingAction PA(*this);
+
+ // FIXME: A tag definition unambiguously tells us this is an init-statement.
+ if (State.update(TryConsumeDeclarationSpecifier()))
+ return State.result();
+ assert(Tok.is(tok::l_paren) && "Expected '('");
+
+ while (true) {
+ // Consume a declarator.
+ if (State.update(TryParseDeclarator(false/*mayBeAbstract*/)))
+ return State.result();
+
+ // Attributes, asm label, or an initializer imply this is not an expression.
+ // FIXME: Disambiguate properly after an = instead of assuming that it's a
+ // valid declaration.
+ if (Tok.isOneOf(tok::equal, tok::kw_asm, tok::kw___attribute) ||
+ (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace))) {
+ State.markNotExpression();
+ return State.result();
+ }
+
+ // A colon here identifies a for-range declaration.
+ if (State.CanBeForRangeDecl && Tok.is(tok::colon))
+ return ConditionOrInitStatement::ForRangeDecl;
+
+ // At this point, it can't be a condition any more, because a condition
+ // must have a brace-or-equal-initializer.
+ if (State.markNotCondition())
+ return State.result();
+
+ // Likewise, it can't be a for-range declaration any more.
+ if (State.markNotForRangeDecl())
+ return State.result();
+
+ // A parenthesized initializer could be part of an expression or a
+ // simple-declaration.
+ if (Tok.is(tok::l_paren)) {
+ ConsumeParen();
+ SkipUntil(tok::r_paren, StopAtSemi);
+ }
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ // We reached the end. If it can now be some kind of decl, then it is.
+ if (State.CanBeCondition && Tok.is(tok::r_paren))
+ return ConditionOrInitStatement::ConditionDecl;
+ else if (State.CanBeInitStatement && Tok.is(tok::semi))
+ return ConditionOrInitStatement::InitStmtDecl;
+ else
+ return ConditionOrInitStatement::Expression;
+}
+
+ /// Determine whether the next set of tokens contains a type-id.
+ ///
+ /// The context parameter states what context we're parsing right
+ /// now, which affects how this routine copes with the token
+ /// following the type-id. If the context is TypeIdInParens, we have
+ /// already parsed the '(' and we will cease lookahead when we hit
+ /// the corresponding ')'. If the context is
+ /// TypeIdAsTemplateArgument, we've already parsed the '<' or ','
+ /// before this template argument, and will cease lookahead when we
+ /// hit a '>', '>>' (in C++0x), or ','; or, in C++0x, an ellipsis immediately
+ /// preceding such. Returns true for a type-id and false for an expression.
+ /// If during the disambiguation process a parsing error is encountered,
+ /// the function returns true to let the declaration parsing code handle it.
+ ///
+ /// type-id:
+ /// type-specifier-seq abstract-declarator[opt]
+ ///
+bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
+
+ isAmbiguous = false;
+
+ // C++ 8.2p2:
+ // The ambiguity arising from the similarity between a function-style cast and
+ // a type-id can occur in different contexts. The ambiguity appears as a
+ // choice between a function-style cast expression and a declaration of a
+ // type. The resolution is that any construct that could possibly be a type-id
+ // in its syntactic context shall be considered a type-id.
+
+ TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
+ if (TPR != TPResult::Ambiguous)
+ return TPR != TPResult::False; // Returns true for TPResult::True or
+ // TPResult::Error.
+
+ // FIXME: Add statistics about the number of ambiguous statements encountered
+ // and how they were resolved (number of declarations+number of expressions).
+
+ // Ok, we have a simple-type-specifier/typename-specifier followed by a '('.
+ // We need tentative parsing...
+
+ RevertingTentativeParsingAction PA(*this);
+
+ // type-specifier-seq
+ TryConsumeDeclarationSpecifier();
+ assert(Tok.is(tok::l_paren) && "Expected '('");
+
+ // declarator
+ TPR = TryParseDeclarator(true/*mayBeAbstract*/, false/*mayHaveIdentifier*/);
+
+ // In case of an error, let the declaration parsing code handle it.
+ if (TPR == TPResult::Error)
+ TPR = TPResult::True;
+
+ if (TPR == TPResult::Ambiguous) {
+ // We are supposed to be inside parens, so if after the abstract declarator
+ // we encounter a ')' this is a type-id, otherwise it's an expression.
+ if (Context == TypeIdInParens && Tok.is(tok::r_paren)) {
+ TPR = TPResult::True;
+ isAmbiguous = true;
+
+ // We are supposed to be inside a template argument, so if after
+ // the abstract declarator we encounter a '>', '>>' (in C++0x), or
+ // ','; or, in C++0x, an ellipsis immediately preceding such, this
+ // is a type-id. Otherwise, it's an expression.
+ } else if (Context == TypeIdAsTemplateArgument &&
+ (Tok.isOneOf(tok::greater, tok::comma) ||
+ (getLangOpts().CPlusPlus11 &&
+ (Tok.isOneOf(tok::greatergreater,
+ tok::greatergreatergreater) ||
+ (Tok.is(tok::ellipsis) &&
+ NextToken().isOneOf(tok::greater, tok::greatergreater,
+ tok::greatergreatergreater,
+ tok::comma)))))) {
+ TPR = TPResult::True;
+ isAmbiguous = true;
+
+ } else
+ TPR = TPResult::False;
+ }
+
+ assert(TPR == TPResult::True || TPR == TPResult::False);
+ return TPR == TPResult::True;
+}
+
+/// Returns true if this is a C++11 attribute-specifier. Per
+/// C++11 [dcl.attr.grammar]p6, two consecutive left square bracket tokens
+/// always introduce an attribute. In Objective-C++11, this rule does not
+/// apply if either '[' begins a message-send.
+///
+/// If Disambiguate is true, we try harder to determine whether a '[[' starts
+/// an attribute-specifier, and return CAK_InvalidAttributeSpecifier if not.
+///
+/// If OuterMightBeMessageSend is true, we assume the outer '[' is either an
+/// Obj-C message send or the start of an attribute. Otherwise, we assume it
+/// is not an Obj-C message send.
+///
+/// C++11 [dcl.attr.grammar]:
+///
+/// attribute-specifier:
+/// '[' '[' attribute-list ']' ']'
+/// alignment-specifier
+///
+/// attribute-list:
+/// attribute[opt]
+/// attribute-list ',' attribute[opt]
+/// attribute '...'
+/// attribute-list ',' attribute '...'
+///
+/// attribute:
+/// attribute-token attribute-argument-clause[opt]
+///
+/// attribute-token:
+/// identifier
+/// identifier '::' identifier
+///
+/// attribute-argument-clause:
+/// '(' balanced-token-seq ')'
+Parser::CXX11AttributeKind
+Parser::isCXX11AttributeSpecifier(bool Disambiguate,
+ bool OuterMightBeMessageSend) {
+ if (Tok.is(tok::kw_alignas))
+ return CAK_AttributeSpecifier;
+
+ if (Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square))
+ return CAK_NotAttributeSpecifier;
+
+ // No tentative parsing if we don't need to look for ']]' or a lambda.
+ if (!Disambiguate && !getLangOpts().ObjC)
+ return CAK_AttributeSpecifier;
+
+ // '[[using ns: ...]]' is an attribute.
+ if (GetLookAheadToken(2).is(tok::kw_using))
+ return CAK_AttributeSpecifier;
+
+ RevertingTentativeParsingAction PA(*this);
+
+ // Opening brackets were checked for above.
+ ConsumeBracket();
+
+ if (!getLangOpts().ObjC) {
+ ConsumeBracket();
+
+ bool IsAttribute = SkipUntil(tok::r_square);
+ IsAttribute &= Tok.is(tok::r_square);
+
+ return IsAttribute ? CAK_AttributeSpecifier : CAK_InvalidAttributeSpecifier;
+ }
+
+ // In Obj-C++11, we need to distinguish four situations:
+ // 1a) int x[[attr]]; C++11 attribute.
+ // 1b) [[attr]]; C++11 statement attribute.
+ // 2) int x[[obj](){ return 1; }()]; Lambda in array size/index.
+ // 3a) int x[[obj get]]; Message send in array size/index.
+ // 3b) [[Class alloc] init]; Message send in message send.
+ // 4) [[obj]{ return self; }() doStuff]; Lambda in message send.
+ // (1) is an attribute, (2) is ill-formed, and (3) and (4) are accepted.
+
+ // Check to see if this is a lambda-expression.
+ // FIXME: If this disambiguation is too slow, fold the tentative lambda parse
+ // into the tentative attribute parse below.
+ {
+ RevertingTentativeParsingAction LambdaTPA(*this);
+ LambdaIntroducer Intro;
+ LambdaIntroducerTentativeParse Tentative;
+ if (ParseLambdaIntroducer(Intro, &Tentative)) {
+ // We hit a hard error after deciding this was not an attribute.
+ // FIXME: Don't parse and annotate expressions when disambiguating
+ // against an attribute.
+ return CAK_NotAttributeSpecifier;
+ }
+
+ switch (Tentative) {
+ case LambdaIntroducerTentativeParse::MessageSend:
+ // Case 3: The inner construct is definitely a message send, so the
+ // outer construct is definitely not an attribute.
+ return CAK_NotAttributeSpecifier;
+
+ case LambdaIntroducerTentativeParse::Success:
+ case LambdaIntroducerTentativeParse::Incomplete:
+ // This is a lambda-introducer or attribute-specifier.
+ if (Tok.is(tok::r_square))
+ // Case 1: C++11 attribute.
+ return CAK_AttributeSpecifier;
+
+ if (OuterMightBeMessageSend)
+ // Case 4: Lambda in message send.
+ return CAK_NotAttributeSpecifier;
+
+ // Case 2: Lambda in array size / index.
+ return CAK_InvalidAttributeSpecifier;
+
+ case LambdaIntroducerTentativeParse::Invalid:
+ // No idea what this is; we couldn't parse it as a lambda-introducer.
+ // Might still be an attribute-specifier or a message send.
+ break;
+ }
+ }
+
+ ConsumeBracket();
+
+ // If we don't have a lambda-introducer, then we have an attribute or a
+ // message-send.
+ bool IsAttribute = true;
+ while (Tok.isNot(tok::r_square)) {
+ if (Tok.is(tok::comma)) {
+ // Case 1: Stray commas can only occur in attributes.
+ return CAK_AttributeSpecifier;
+ }
+
+ // Parse the attribute-token, if present.
+ // C++11 [dcl.attr.grammar]:
+ // If a keyword or an alternative token that satisfies the syntactic
+ // requirements of an identifier is contained in an attribute-token,
+ // it is considered an identifier.
+ SourceLocation Loc;
+ if (!TryParseCXX11AttributeIdentifier(Loc)) {
+ IsAttribute = false;
+ break;
+ }
+ if (Tok.is(tok::coloncolon)) {
+ ConsumeToken();
+ if (!TryParseCXX11AttributeIdentifier(Loc)) {
+ IsAttribute = false;
+ break;
+ }
+ }
+
+ // Parse the attribute-argument-clause, if present.
+ if (Tok.is(tok::l_paren)) {
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren)) {
+ IsAttribute = false;
+ break;
+ }
+ }
+
+ TryConsumeToken(tok::ellipsis);
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ // An attribute must end ']]'.
+ if (IsAttribute) {
+ if (Tok.is(tok::r_square)) {
+ ConsumeBracket();
+ IsAttribute = Tok.is(tok::r_square);
+ } else {
+ IsAttribute = false;
+ }
+ }
+
+ if (IsAttribute)
+ // Case 1: C++11 statement attribute.
+ return CAK_AttributeSpecifier;
+
+ // Case 3: Message send.
+ return CAK_NotAttributeSpecifier;
+}
+
+bool Parser::TrySkipAttributes() {
+ while (Tok.isOneOf(tok::l_square, tok::kw___attribute, tok::kw___declspec,
+ tok::kw_alignas)) {
+ if (Tok.is(tok::l_square)) {
+ ConsumeBracket();
+ if (Tok.isNot(tok::l_square))
+ return false;
+ ConsumeBracket();
+ if (!SkipUntil(tok::r_square) || Tok.isNot(tok::r_square))
+ return false;
+ // Note that explicitly checking for `[[` and `]]` allows to fail as
+ // expected in the case of the Objective-C message send syntax.
+ ConsumeBracket();
+ } else {
+ ConsumeToken();
+ if (Tok.isNot(tok::l_paren))
+ return false;
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+Parser::TPResult Parser::TryParsePtrOperatorSeq() {
+ while (true) {
+ if (TryAnnotateOptionalCXXScopeToken(true))
+ return TPResult::Error;
+
+ if (Tok.isOneOf(tok::star, tok::amp, tok::caret, tok::ampamp) ||
+ (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
+ // ptr-operator
+ ConsumeAnyToken();
+
+ // Skip attributes.
+ if (!TrySkipAttributes())
+ return TPResult::Error;
+
+ while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict,
+ tok::kw__Nonnull, tok::kw__Nullable,
+ tok::kw__Nullable_result, tok::kw__Null_unspecified,
+ tok::kw__Atomic))
+ ConsumeToken();
+ } else {
+ return TPResult::True;
+ }
+ }
+}
+
+/// operator-function-id:
+/// 'operator' operator
+///
+/// operator: one of
+/// new delete new[] delete[] + - * / % ^ [...]
+///
+/// conversion-function-id:
+/// 'operator' conversion-type-id
+///
+/// conversion-type-id:
+/// type-specifier-seq conversion-declarator[opt]
+///
+/// conversion-declarator:
+/// ptr-operator conversion-declarator[opt]
+///
+/// literal-operator-id:
+/// 'operator' string-literal identifier
+/// 'operator' user-defined-string-literal
+Parser::TPResult Parser::TryParseOperatorId() {
+ assert(Tok.is(tok::kw_operator));
+ ConsumeToken();
+
+ // Maybe this is an operator-function-id.
+ switch (Tok.getKind()) {
+ case tok::kw_new: case tok::kw_delete:
+ ConsumeToken();
+ if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) {
+ ConsumeBracket();
+ ConsumeBracket();
+ }
+ return TPResult::True;
+
+#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemOnly) \
+ case tok::Token:
+#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemOnly)
+#include "clang/Basic/OperatorKinds.def"
+ ConsumeToken();
+ return TPResult::True;
+
+ case tok::l_square:
+ if (NextToken().is(tok::r_square)) {
+ ConsumeBracket();
+ ConsumeBracket();
+ return TPResult::True;
+ }
+ break;
+
+ case tok::l_paren:
+ if (NextToken().is(tok::r_paren)) {
+ ConsumeParen();
+ ConsumeParen();
+ return TPResult::True;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Maybe this is a literal-operator-id.
+ if (getLangOpts().CPlusPlus11 && isTokenStringLiteral()) {
+ bool FoundUDSuffix = false;
+ do {
+ FoundUDSuffix |= Tok.hasUDSuffix();
+ ConsumeStringToken();
+ } while (isTokenStringLiteral());
+
+ if (!FoundUDSuffix) {
+ if (Tok.is(tok::identifier))
+ ConsumeToken();
+ else
+ return TPResult::Error;
+ }
+ return TPResult::True;
+ }
+
+ // Maybe this is a conversion-function-id.
+ bool AnyDeclSpecifiers = false;
+ while (true) {
+ TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
+ if (TPR == TPResult::Error)
+ return TPR;
+ if (TPR == TPResult::False) {
+ if (!AnyDeclSpecifiers)
+ return TPResult::Error;
+ break;
+ }
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error)
+ return TPResult::Error;
+ AnyDeclSpecifiers = true;
+ }
+ return TryParsePtrOperatorSeq();
+}
+
+/// declarator:
+/// direct-declarator
+/// ptr-operator declarator
+///
+/// direct-declarator:
+/// declarator-id
+/// direct-declarator '(' parameter-declaration-clause ')'
+/// cv-qualifier-seq[opt] exception-specification[opt]
+/// direct-declarator '[' constant-expression[opt] ']'
+/// '(' declarator ')'
+/// [GNU] '(' attributes declarator ')'
+///
+/// abstract-declarator:
+/// ptr-operator abstract-declarator[opt]
+/// direct-abstract-declarator
+///
+/// direct-abstract-declarator:
+/// direct-abstract-declarator[opt]
+/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
+/// exception-specification[opt]
+/// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
+/// '(' abstract-declarator ')'
+/// [C++0x] ...
+///
+/// ptr-operator:
+/// '*' cv-qualifier-seq[opt]
+/// '&'
+/// [C++0x] '&&' [TODO]
+/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt]
+///
+/// cv-qualifier-seq:
+/// cv-qualifier cv-qualifier-seq[opt]
+///
+/// cv-qualifier:
+/// 'const'
+/// 'volatile'
+///
+/// declarator-id:
+/// '...'[opt] id-expression
+///
+/// id-expression:
+/// unqualified-id
+/// qualified-id [TODO]
+///
+/// unqualified-id:
+/// identifier
+/// operator-function-id
+/// conversion-function-id
+/// literal-operator-id
+/// '~' class-name [TODO]
+/// '~' decltype-specifier [TODO]
+/// template-id [TODO]
+///
+Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
+ bool mayHaveIdentifier,
+ bool mayHaveDirectInit) {
+ // declarator:
+ // direct-declarator
+ // ptr-operator declarator
+ if (TryParsePtrOperatorSeq() == TPResult::Error)
+ return TPResult::Error;
+
+ // direct-declarator:
+ // direct-abstract-declarator:
+ if (Tok.is(tok::ellipsis))
+ ConsumeToken();
+
+ if ((Tok.isOneOf(tok::identifier, tok::kw_operator) ||
+ (Tok.is(tok::annot_cxxscope) && (NextToken().is(tok::identifier) ||
+ NextToken().is(tok::kw_operator)))) &&
+ mayHaveIdentifier) {
+ // declarator-id
+ if (Tok.is(tok::annot_cxxscope)) {
+ CXXScopeSpec SS;
+ Actions.RestoreNestedNameSpecifierAnnotation(
+ Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS);
+ if (SS.isInvalid())
+ return TPResult::Error;
+ ConsumeAnnotationToken();
+ } else if (Tok.is(tok::identifier)) {
+ TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo());
+ }
+ if (Tok.is(tok::kw_operator)) {
+ if (TryParseOperatorId() == TPResult::Error)
+ return TPResult::Error;
+ } else
+ ConsumeToken();
+ } else if (Tok.is(tok::l_paren)) {
+ ConsumeParen();
+ if (mayBeAbstract &&
+ (Tok.is(tok::r_paren) || // 'int()' is a function.
+ // 'int(...)' is a function.
+ (Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren)) ||
+ isDeclarationSpecifier(
+ ImplicitTypenameContext::No))) { // 'int(int)' is a function.
+ // '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
+ // exception-specification[opt]
+ TPResult TPR = TryParseFunctionDeclarator();
+ if (TPR != TPResult::Ambiguous)
+ return TPR;
+ } else {
+ // '(' declarator ')'
+ // '(' attributes declarator ')'
+ // '(' abstract-declarator ')'
+ if (Tok.isOneOf(tok::kw___attribute, tok::kw___declspec, tok::kw___cdecl,
+ tok::kw___stdcall, tok::kw___fastcall, tok::kw___thiscall,
+ tok::kw___regcall, tok::kw___vectorcall))
+ return TPResult::True; // attributes indicate declaration
+ TPResult TPR = TryParseDeclarator(mayBeAbstract, mayHaveIdentifier);
+ if (TPR != TPResult::Ambiguous)
+ return TPR;
+ if (Tok.isNot(tok::r_paren))
+ return TPResult::False;
+ ConsumeParen();
+ }
+ } else if (!mayBeAbstract) {
+ return TPResult::False;
+ }
+
+ if (mayHaveDirectInit)
+ return TPResult::Ambiguous;
+
+ while (true) {
+ TPResult TPR(TPResult::Ambiguous);
+
+ if (Tok.is(tok::l_paren)) {
+ // Check whether we have a function declarator or a possible ctor-style
+ // initializer that follows the declarator. Note that ctor-style
+ // initializers are not possible in contexts where abstract declarators
+ // are allowed.
+ if (!mayBeAbstract && !isCXXFunctionDeclarator())
+ break;
+
+ // direct-declarator '(' parameter-declaration-clause ')'
+ // cv-qualifier-seq[opt] exception-specification[opt]
+ ConsumeParen();
+ TPR = TryParseFunctionDeclarator();
+ } else if (Tok.is(tok::l_square)) {
+ // direct-declarator '[' constant-expression[opt] ']'
+ // direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
+ TPR = TryParseBracketDeclarator();
+ } else if (Tok.is(tok::kw_requires)) {
+ // declarator requires-clause
+ // A requires clause indicates a function declaration.
+ TPR = TPResult::True;
+ } else {
+ break;
+ }
+
+ if (TPR != TPResult::Ambiguous)
+ return TPR;
+ }
+
+ return TPResult::Ambiguous;
+}
+
+bool Parser::isTentativelyDeclared(IdentifierInfo *II) {
+ return llvm::is_contained(TentativelyDeclaredIdentifiers, II);
+}
+
+namespace {
+class TentativeParseCCC final : public CorrectionCandidateCallback {
+public:
+ TentativeParseCCC(const Token &Next) {
+ WantRemainingKeywords = false;
+ WantTypeSpecifiers =
+ Next.isOneOf(tok::l_paren, tok::r_paren, tok::greater, tok::l_brace,
+ tok::identifier, tok::comma);
+ }
+
+ bool ValidateCandidate(const TypoCorrection &Candidate) override {
+ // Reject any candidate that only resolves to instance members since they
+ // aren't viable as standalone identifiers instead of member references.
+ if (Candidate.isResolved() && !Candidate.isKeyword() &&
+ llvm::all_of(Candidate,
+ [](NamedDecl *ND) { return ND->isCXXInstanceMember(); }))
+ return false;
+
+ return CorrectionCandidateCallback::ValidateCandidate(Candidate);
+ }
+
+ std::unique_ptr<CorrectionCandidateCallback> clone() override {
+ return std::make_unique<TentativeParseCCC>(*this);
+ }
+};
+}
+/// isCXXDeclarationSpecifier - Returns TPResult::True if it is a declaration
+/// specifier, TPResult::False if it is not, TPResult::Ambiguous if it could
+/// be either a decl-specifier or a function-style cast, and TPResult::Error
+/// if a parsing error was found and reported.
+///
+/// If InvalidAsDeclSpec is not null, some cases that would be ill-formed as
+/// declaration specifiers but possibly valid as some other kind of construct
+/// return TPResult::Ambiguous instead of TPResult::False. When this happens,
+/// the intent is to keep trying to disambiguate, on the basis that we might
+/// find a better reason to treat this construct as a declaration later on.
+/// When this happens and the name could possibly be valid in some other
+/// syntactic context, *InvalidAsDeclSpec is set to 'true'. The current cases
+/// that trigger this are:
+///
+/// * When parsing X::Y (with no 'typename') where X is dependent
+/// * When parsing X<Y> where X is undeclared
+///
+/// decl-specifier:
+/// storage-class-specifier
+/// type-specifier
+/// function-specifier
+/// 'friend'
+/// 'typedef'
+/// [C++11] 'constexpr'
+/// [C++20] 'consteval'
+/// [GNU] attributes declaration-specifiers[opt]
+///
+/// storage-class-specifier:
+/// 'register'
+/// 'static'
+/// 'extern'
+/// 'mutable'
+/// 'auto'
+/// [GNU] '__thread'
+/// [C++11] 'thread_local'
+/// [C11] '_Thread_local'
+///
+/// function-specifier:
+/// 'inline'
+/// 'virtual'
+/// 'explicit'
+///
+/// typedef-name:
+/// identifier
+///
+/// type-specifier:
+/// simple-type-specifier
+/// class-specifier
+/// enum-specifier
+/// elaborated-type-specifier
+/// typename-specifier
+/// cv-qualifier
+///
+/// simple-type-specifier:
+/// '::'[opt] nested-name-specifier[opt] type-name
+/// '::'[opt] nested-name-specifier 'template'
+/// simple-template-id [TODO]
+/// 'char'
+/// 'wchar_t'
+/// 'bool'
+/// 'short'
+/// 'int'
+/// 'long'
+/// 'signed'
+/// 'unsigned'
+/// 'float'
+/// 'double'
+/// 'void'
+/// [GNU] typeof-specifier
+/// [GNU] '_Complex'
+/// [C++11] 'auto'
+/// [GNU] '__auto_type'
+/// [C++11] 'decltype' ( expression )
+/// [C++1y] 'decltype' ( 'auto' )
+///
+/// type-name:
+/// class-name
+/// enum-name
+/// typedef-name
+///
+/// elaborated-type-specifier:
+/// class-key '::'[opt] nested-name-specifier[opt] identifier
+/// class-key '::'[opt] nested-name-specifier[opt] 'template'[opt]
+/// simple-template-id
+/// 'enum' '::'[opt] nested-name-specifier[opt] identifier
+///
+/// enum-name:
+/// identifier
+///
+/// enum-specifier:
+/// 'enum' identifier[opt] '{' enumerator-list[opt] '}'
+/// 'enum' identifier[opt] '{' enumerator-list ',' '}'
+///
+/// class-specifier:
+/// class-head '{' member-specification[opt] '}'
+///
+/// class-head:
+/// class-key identifier[opt] base-clause[opt]
+/// class-key nested-name-specifier identifier base-clause[opt]
+/// class-key nested-name-specifier[opt] simple-template-id
+/// base-clause[opt]
+///
+/// class-key:
+/// 'class'
+/// 'struct'
+/// 'union'
+///
+/// cv-qualifier:
+/// 'const'
+/// 'volatile'
+/// [GNU] restrict
+///
+Parser::TPResult
+Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
+ Parser::TPResult BracedCastResult,
+ bool *InvalidAsDeclSpec) {
+ auto IsPlaceholderSpecifier = [&](TemplateIdAnnotation *TemplateId,
+ int Lookahead) {
+ // We have a placeholder-constraint (we check for 'auto' or 'decltype' to
+ // distinguish 'C<int>;' from 'C<int> auto c = 1;')
+ return TemplateId->Kind == TNK_Concept_template &&
+ (GetLookAheadToken(Lookahead + 1)
+ .isOneOf(tok::kw_auto, tok::kw_decltype,
+ // If we have an identifier here, the user probably
+ // forgot the 'auto' in the placeholder constraint,
+ // e.g. 'C<int> x = 2;' This will be diagnosed nicely
+ // later, so disambiguate as a declaration.
+ tok::identifier,
+ // CVR qualifierslikely the same situation for the
+ // user, so let this be diagnosed nicely later. We
+ // cannot handle references here, as `C<int> & Other`
+ // and `C<int> && Other` are both legal.
+ tok::kw_const, tok::kw_volatile, tok::kw_restrict) ||
+ // While `C<int> && Other` is legal, doing so while not specifying a
+ // template argument is NOT, so see if we can fix up in that case at
+ // minimum. Concepts require at least 1 template parameter, so we
+ // can count on the argument count.
+ // FIXME: In the future, we migth be able to have SEMA look up the
+ // declaration for this concept, and see how many template
+ // parameters it has. If the concept isn't fully specified, it is
+ // possibly a situation where we want deduction, such as:
+ // `BinaryConcept<int> auto f = bar();`
+ (TemplateId->NumArgs == 0 &&
+ GetLookAheadToken(Lookahead + 1).isOneOf(tok::amp, tok::ampamp)));
+ };
+ switch (Tok.getKind()) {
+ case tok::identifier: {
+ // Check for need to substitute AltiVec __vector keyword
+ // for "vector" identifier.
+ if (TryAltiVecVectorToken())
+ return TPResult::True;
+
+ const Token &Next = NextToken();
+ // In 'foo bar', 'foo' is always a type name outside of Objective-C.
+ if (!getLangOpts().ObjC && Next.is(tok::identifier))
+ return TPResult::True;
+
+ if (Next.isNot(tok::coloncolon) && Next.isNot(tok::less)) {
+ // Determine whether this is a valid expression. If not, we will hit
+ // a parse error one way or another. In that case, tell the caller that
+ // this is ambiguous. Typo-correct to type and expression keywords and
+ // to types and identifiers, in order to try to recover from errors.
+ TentativeParseCCC CCC(Next);
+ switch (TryAnnotateName(&CCC)) {
+ case ANK_Error:
+ return TPResult::Error;
+ case ANK_TentativeDecl:
+ return TPResult::False;
+ case ANK_TemplateName:
+ // In C++17, this could be a type template for class template argument
+ // deduction. Try to form a type annotation for it. If we're in a
+ // template template argument, we'll undo this when checking the
+ // validity of the argument.
+ if (getLangOpts().CPlusPlus17) {
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
+ return TPResult::Error;
+ if (Tok.isNot(tok::identifier))
+ break;
+ }
+
+ // A bare type template-name which can't be a template template
+ // argument is an error, and was probably intended to be a type.
+ return GreaterThanIsOperator ? TPResult::True : TPResult::False;
+ case ANK_Unresolved:
+ return InvalidAsDeclSpec ? TPResult::Ambiguous : TPResult::False;
+ case ANK_Success:
+ break;
+ }
+ assert(Tok.isNot(tok::identifier) &&
+ "TryAnnotateName succeeded without producing an annotation");
+ } else {
+ // This might possibly be a type with a dependent scope specifier and
+ // a missing 'typename' keyword. Don't use TryAnnotateName in this case,
+ // since it will annotate as a primary expression, and we want to use the
+ // "missing 'typename'" logic.
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
+ return TPResult::Error;
+ // If annotation failed, assume it's a non-type.
+ // FIXME: If this happens due to an undeclared identifier, treat it as
+ // ambiguous.
+ if (Tok.is(tok::identifier))
+ return TPResult::False;
+ }
+
+ // We annotated this token as something. Recurse to handle whatever we got.
+ return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult,
+ InvalidAsDeclSpec);
+ }
+
+ case tok::kw_typename: // typename T::type
+ // Annotate typenames and C++ scope specifiers. If we get one, just
+ // recurse to handle whatever we get.
+ if (TryAnnotateTypeOrScopeToken(ImplicitTypenameContext::Yes))
+ return TPResult::Error;
+ return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes,
+ BracedCastResult, InvalidAsDeclSpec);
+
+ case tok::coloncolon: { // ::foo::bar
+ const Token &Next = NextToken();
+ if (Next.isOneOf(tok::kw_new, // ::new
+ tok::kw_delete)) // ::delete
+ return TPResult::False;
+ [[fallthrough]];
+ }
+ case tok::kw___super:
+ case tok::kw_decltype:
+ // Annotate typenames and C++ scope specifiers. If we get one, just
+ // recurse to handle whatever we get.
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
+ return TPResult::Error;
+ return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult,
+ InvalidAsDeclSpec);
+
+ // decl-specifier:
+ // storage-class-specifier
+ // type-specifier
+ // function-specifier
+ // 'friend'
+ // 'typedef'
+ // 'constexpr'
+ case tok::kw_friend:
+ case tok::kw_typedef:
+ case tok::kw_constexpr:
+ case tok::kw_consteval:
+ case tok::kw_constinit:
+ // storage-class-specifier
+ case tok::kw_register:
+ case tok::kw_static:
+ case tok::kw_extern:
+ case tok::kw_mutable:
+ case tok::kw_auto:
+ case tok::kw___thread:
+ case tok::kw_thread_local:
+ case tok::kw__Thread_local:
+ // function-specifier
+ case tok::kw_inline:
+ case tok::kw_virtual:
+ case tok::kw_explicit:
+
+ // Modules
+ case tok::kw___module_private__:
+
+ // Debugger support
+ case tok::kw___unknown_anytype:
+
+ // type-specifier:
+ // simple-type-specifier
+ // class-specifier
+ // enum-specifier
+ // elaborated-type-specifier
+ // typename-specifier
+ // cv-qualifier
+
+ // class-specifier
+ // elaborated-type-specifier
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw_union:
+ case tok::kw___interface:
+ // enum-specifier
+ case tok::kw_enum:
+ // cv-qualifier
+ case tok::kw_const:
+ case tok::kw_volatile:
+ return TPResult::True;
+
+ // OpenCL address space qualifiers
+ case tok::kw_private:
+ if (!getLangOpts().OpenCL)
+ return TPResult::False;
+ [[fallthrough]];
+ case tok::kw___private:
+ case tok::kw___local:
+ case tok::kw___global:
+ case tok::kw___constant:
+ case tok::kw___generic:
+ // OpenCL access qualifiers
+ case tok::kw___read_only:
+ case tok::kw___write_only:
+ case tok::kw___read_write:
+ // OpenCL pipe
+ case tok::kw_pipe:
+
+ // HLSL address space qualifiers
+ case tok::kw_groupshared:
+
+ // GNU
+ case tok::kw_restrict:
+ case tok::kw__Complex:
+ case tok::kw___attribute:
+ case tok::kw___auto_type:
+ return TPResult::True;
+
+ // Microsoft
+ case tok::kw___declspec:
+ case tok::kw___cdecl:
+ case tok::kw___stdcall:
+ case tok::kw___fastcall:
+ case tok::kw___thiscall:
+ case tok::kw___regcall:
+ case tok::kw___vectorcall:
+ case tok::kw___w64:
+ case tok::kw___sptr:
+ case tok::kw___uptr:
+ case tok::kw___ptr64:
+ case tok::kw___ptr32:
+ case tok::kw___forceinline:
+ case tok::kw___unaligned:
+ case tok::kw__Nonnull:
+ case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
+ case tok::kw__Null_unspecified:
+ case tok::kw___kindof:
+ return TPResult::True;
+
+ // Borland
+ case tok::kw___pascal:
+ return TPResult::True;
+
+ // AltiVec
+ case tok::kw___vector:
+ return TPResult::True;
+
+ case tok::annot_template_id: {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ // If lookup for the template-name found nothing, don't assume we have a
+ // definitive disambiguation result yet.
+ if ((TemplateId->hasInvalidName() ||
+ TemplateId->Kind == TNK_Undeclared_template) &&
+ InvalidAsDeclSpec) {
+ // 'template-id(' can be a valid expression but not a valid decl spec if
+ // the template-name is not declared, but we don't consider this to be a
+ // definitive disambiguation. In any other context, it's an error either
+ // way.
+ *InvalidAsDeclSpec = NextToken().is(tok::l_paren);
+ return TPResult::Ambiguous;
+ }
+ if (TemplateId->hasInvalidName())
+ return TPResult::Error;
+ if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/0))
+ return TPResult::True;
+ if (TemplateId->Kind != TNK_Type_template)
+ return TPResult::False;
+ CXXScopeSpec SS;
+ AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
+ assert(Tok.is(tok::annot_typename));
+ goto case_typename;
+ }
+
+ case tok::annot_cxxscope: // foo::bar or ::foo::bar, but already parsed
+ // We've already annotated a scope; try to annotate a type.
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
+ return TPResult::Error;
+ if (!Tok.is(tok::annot_typename)) {
+ if (Tok.is(tok::annot_cxxscope) &&
+ NextToken().is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId =
+ takeTemplateIdAnnotation(NextToken());
+ if (TemplateId->hasInvalidName()) {
+ if (InvalidAsDeclSpec) {
+ *InvalidAsDeclSpec = NextToken().is(tok::l_paren);
+ return TPResult::Ambiguous;
+ }
+ return TPResult::Error;
+ }
+ if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/1))
+ return TPResult::True;
+ }
+ // If the next token is an identifier or a type qualifier, then this
+ // can't possibly be a valid expression either.
+ if (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier)) {
+ CXXScopeSpec SS;
+ Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
+ Tok.getAnnotationRange(),
+ SS);
+ if (SS.getScopeRep() && SS.getScopeRep()->isDependent()) {
+ RevertingTentativeParsingAction PA(*this);
+ ConsumeAnnotationToken();
+ ConsumeToken();
+ bool isIdentifier = Tok.is(tok::identifier);
+ TPResult TPR = TPResult::False;
+ if (!isIdentifier)
+ TPR = isCXXDeclarationSpecifier(
+ AllowImplicitTypename, BracedCastResult, InvalidAsDeclSpec);
+
+ if (isIdentifier ||
+ TPR == TPResult::True || TPR == TPResult::Error)
+ return TPResult::Error;
+
+ if (InvalidAsDeclSpec) {
+ // We can't tell whether this is a missing 'typename' or a valid
+ // expression.
+ *InvalidAsDeclSpec = true;
+ return TPResult::Ambiguous;
+ } else {
+ // In MS mode, if InvalidAsDeclSpec is not provided, and the tokens
+ // are or the form *) or &) *> or &> &&>, this can't be an expression.
+ // The typename must be missing.
+ if (getLangOpts().MSVCCompat) {
+ if (((Tok.is(tok::amp) || Tok.is(tok::star)) &&
+ (NextToken().is(tok::r_paren) ||
+ NextToken().is(tok::greater))) ||
+ (Tok.is(tok::ampamp) && NextToken().is(tok::greater)))
+ return TPResult::True;
+ }
+ }
+ } else {
+ // Try to resolve the name. If it doesn't exist, assume it was
+ // intended to name a type and keep disambiguating.
+ switch (TryAnnotateName(/*CCC=*/nullptr, AllowImplicitTypename)) {
+ case ANK_Error:
+ return TPResult::Error;
+ case ANK_TentativeDecl:
+ return TPResult::False;
+ case ANK_TemplateName:
+ // In C++17, this could be a type template for class template
+ // argument deduction.
+ if (getLangOpts().CPlusPlus17) {
+ if (TryAnnotateTypeOrScopeToken())
+ return TPResult::Error;
+ if (Tok.isNot(tok::identifier))
+ break;
+ }
+
+ // A bare type template-name which can't be a template template
+ // argument is an error, and was probably intended to be a type.
+ // In C++17, this could be class template argument deduction.
+ return (getLangOpts().CPlusPlus17 || GreaterThanIsOperator)
+ ? TPResult::True
+ : TPResult::False;
+ case ANK_Unresolved:
+ return InvalidAsDeclSpec ? TPResult::Ambiguous : TPResult::False;
+ case ANK_Success:
+ break;
+ }
+
+ // Annotated it, check again.
+ assert(Tok.isNot(tok::annot_cxxscope) ||
+ NextToken().isNot(tok::identifier));
+ return isCXXDeclarationSpecifier(AllowImplicitTypename,
+ BracedCastResult, InvalidAsDeclSpec);
+ }
+ }
+ return TPResult::False;
+ }
+ // If that succeeded, fallthrough into the generic simple-type-id case.
+ [[fallthrough]];
+
+ // The ambiguity resides in a simple-type-specifier/typename-specifier
+ // followed by a '('. The '(' could either be the start of:
+ //
+ // direct-declarator:
+ // '(' declarator ')'
+ //
+ // direct-abstract-declarator:
+ // '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
+ // exception-specification[opt]
+ // '(' abstract-declarator ')'
+ //
+ // or part of a function-style cast expression:
+ //
+ // simple-type-specifier '(' expression-list[opt] ')'
+ //
+
+ // simple-type-specifier:
+
+ case tok::annot_typename:
+ case_typename:
+ // In Objective-C, we might have a protocol-qualified type.
+ if (getLangOpts().ObjC && NextToken().is(tok::less)) {
+ // Tentatively parse the protocol qualifiers.
+ RevertingTentativeParsingAction PA(*this);
+ ConsumeAnyToken(); // The type token
+
+ TPResult TPR = TryParseProtocolQualifiers();
+ bool isFollowedByParen = Tok.is(tok::l_paren);
+ bool isFollowedByBrace = Tok.is(tok::l_brace);
+
+ if (TPR == TPResult::Error)
+ return TPResult::Error;
+
+ if (isFollowedByParen)
+ return TPResult::Ambiguous;
+
+ if (getLangOpts().CPlusPlus11 && isFollowedByBrace)
+ return BracedCastResult;
+
+ return TPResult::True;
+ }
+ [[fallthrough]];
+
+ case tok::kw_char:
+ case tok::kw_wchar_t:
+ case tok::kw_char8_t:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ case tok::kw_bool:
+ case tok::kw_short:
+ case tok::kw_int:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_half:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw___bf16:
+ case tok::kw__Float16:
+ case tok::kw___float128:
+ case tok::kw___ibm128:
+ case tok::kw_void:
+ case tok::annot_decltype:
+#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
+#include "clang/Basic/OpenCLImageTypes.def"
+ if (NextToken().is(tok::l_paren))
+ return TPResult::Ambiguous;
+
+ // This is a function-style cast in all cases we disambiguate other than
+ // one:
+ // struct S {
+ // enum E : int { a = 4 }; // enum
+ // enum E : int { 4 }; // bit-field
+ // };
+ if (getLangOpts().CPlusPlus11 && NextToken().is(tok::l_brace))
+ return BracedCastResult;
+
+ if (isStartOfObjCClassMessageMissingOpenBracket())
+ return TPResult::False;
+
+ return TPResult::True;
+
+ // GNU typeof support.
+ case tok::kw_typeof: {
+ if (NextToken().isNot(tok::l_paren))
+ return TPResult::True;
+
+ RevertingTentativeParsingAction PA(*this);
+
+ TPResult TPR = TryParseTypeofSpecifier();
+ bool isFollowedByParen = Tok.is(tok::l_paren);
+ bool isFollowedByBrace = Tok.is(tok::l_brace);
+
+ if (TPR == TPResult::Error)
+ return TPResult::Error;
+
+ if (isFollowedByParen)
+ return TPResult::Ambiguous;
+
+ if (getLangOpts().CPlusPlus11 && isFollowedByBrace)
+ return BracedCastResult;
+
+ return TPResult::True;
+ }
+
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
+ return TPResult::True;
+
+ // C11 _Atomic
+ case tok::kw__Atomic:
+ return TPResult::True;
+
+ case tok::kw__BitInt:
+ case tok::kw__ExtInt: {
+ if (NextToken().isNot(tok::l_paren))
+ return TPResult::Error;
+ RevertingTentativeParsingAction PA(*this);
+ ConsumeToken();
+ ConsumeParen();
+
+ if (!SkipUntil(tok::r_paren, StopAtSemi))
+ return TPResult::Error;
+
+ if (Tok.is(tok::l_paren))
+ return TPResult::Ambiguous;
+
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace))
+ return BracedCastResult;
+
+ return TPResult::True;
+ }
+ default:
+ return TPResult::False;
+ }
+}
+
+bool Parser::isCXXDeclarationSpecifierAType() {
+ switch (Tok.getKind()) {
+ // typename-specifier
+ case tok::annot_decltype:
+ case tok::annot_template_id:
+ case tok::annot_typename:
+ case tok::kw_typeof:
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
+ return true;
+
+ // elaborated-type-specifier
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw_union:
+ case tok::kw___interface:
+ case tok::kw_enum:
+ return true;
+
+ // simple-type-specifier
+ case tok::kw_char:
+ case tok::kw_wchar_t:
+ case tok::kw_char8_t:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ case tok::kw_bool:
+ case tok::kw_short:
+ case tok::kw_int:
+ case tok::kw__ExtInt:
+ case tok::kw__BitInt:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_half:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw___bf16:
+ case tok::kw__Float16:
+ case tok::kw___float128:
+ case tok::kw___ibm128:
+ case tok::kw_void:
+ case tok::kw___unknown_anytype:
+ case tok::kw___auto_type:
+#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
+#include "clang/Basic/OpenCLImageTypes.def"
+ return true;
+
+ case tok::kw_auto:
+ return getLangOpts().CPlusPlus11;
+
+ case tok::kw__Atomic:
+ // "_Atomic foo"
+ return NextToken().is(tok::l_paren);
+
+ default:
+ return false;
+ }
+}
+
+/// [GNU] typeof-specifier:
+/// 'typeof' '(' expressions ')'
+/// 'typeof' '(' type-name ')'
+///
+Parser::TPResult Parser::TryParseTypeofSpecifier() {
+ assert(Tok.is(tok::kw_typeof) && "Expected 'typeof'!");
+ ConsumeToken();
+
+ assert(Tok.is(tok::l_paren) && "Expected '('");
+ // Parse through the parens after 'typeof'.
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren, StopAtSemi))
+ return TPResult::Error;
+
+ return TPResult::Ambiguous;
+}
+
+/// [ObjC] protocol-qualifiers:
+//// '<' identifier-list '>'
+Parser::TPResult Parser::TryParseProtocolQualifiers() {
+ assert(Tok.is(tok::less) && "Expected '<' for qualifier list");
+ ConsumeToken();
+ do {
+ if (Tok.isNot(tok::identifier))
+ return TPResult::Error;
+ ConsumeToken();
+
+ if (Tok.is(tok::comma)) {
+ ConsumeToken();
+ continue;
+ }
+
+ if (Tok.is(tok::greater)) {
+ ConsumeToken();
+ return TPResult::Ambiguous;
+ }
+ } while (false);
+
+ return TPResult::Error;
+}
+
+/// isCXXFunctionDeclarator - Disambiguates between a function declarator or
+/// a constructor-style initializer, when parsing declaration statements.
+/// Returns true for function declarator and false for constructor-style
+/// initializer.
+/// If during the disambiguation process a parsing error is encountered,
+/// the function returns true to let the declaration parsing code handle it.
+///
+/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
+/// exception-specification[opt]
+///
+bool Parser::isCXXFunctionDeclarator(
+ bool *IsAmbiguous, ImplicitTypenameContext AllowImplicitTypename) {
+
+ // C++ 8.2p1:
+ // The ambiguity arising from the similarity between a function-style cast and
+ // a declaration mentioned in 6.8 can also occur in the context of a
+ // declaration. In that context, the choice is between a function declaration
+ // with a redundant set of parentheses around a parameter name and an object
+ // declaration with a function-style cast as the initializer. Just as for the
+ // ambiguities mentioned in 6.8, the resolution is to consider any construct
+ // that could possibly be a declaration a declaration.
+
+ RevertingTentativeParsingAction PA(*this);
+
+ ConsumeParen();
+ bool InvalidAsDeclaration = false;
+ TPResult TPR = TryParseParameterDeclarationClause(
+ &InvalidAsDeclaration, /*VersusTemplateArgument=*/false,
+ AllowImplicitTypename);
+ if (TPR == TPResult::Ambiguous) {
+ if (Tok.isNot(tok::r_paren))
+ TPR = TPResult::False;
+ else {
+ const Token &Next = NextToken();
+ if (Next.isOneOf(tok::amp, tok::ampamp, tok::kw_const, tok::kw_volatile,
+ tok::kw_throw, tok::kw_noexcept, tok::l_square,
+ tok::l_brace, tok::kw_try, tok::equal, tok::arrow) ||
+ isCXX11VirtSpecifier(Next))
+ // The next token cannot appear after a constructor-style initializer,
+ // and can appear next in a function definition. This must be a function
+ // declarator.
+ TPR = TPResult::True;
+ else if (InvalidAsDeclaration)
+ // Use the absence of 'typename' as a tie-breaker.
+ TPR = TPResult::False;
+ }
+ }
+
+ if (IsAmbiguous && TPR == TPResult::Ambiguous)
+ *IsAmbiguous = true;
+
+ // In case of an error, let the declaration parsing code handle it.
+ return TPR != TPResult::False;
+}
+
+/// parameter-declaration-clause:
+/// parameter-declaration-list[opt] '...'[opt]
+/// parameter-declaration-list ',' '...'
+///
+/// parameter-declaration-list:
+/// parameter-declaration
+/// parameter-declaration-list ',' parameter-declaration
+///
+/// parameter-declaration:
+/// attribute-specifier-seq[opt] decl-specifier-seq declarator attributes[opt]
+/// attribute-specifier-seq[opt] decl-specifier-seq declarator attributes[opt]
+/// '=' assignment-expression
+/// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
+/// attributes[opt]
+/// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
+/// attributes[opt] '=' assignment-expression
+///
+Parser::TPResult Parser::TryParseParameterDeclarationClause(
+ bool *InvalidAsDeclaration, bool VersusTemplateArgument,
+ ImplicitTypenameContext AllowImplicitTypename) {
+
+ if (Tok.is(tok::r_paren))
+ return TPResult::Ambiguous;
+
+ // parameter-declaration-list[opt] '...'[opt]
+ // parameter-declaration-list ',' '...'
+ //
+ // parameter-declaration-list:
+ // parameter-declaration
+ // parameter-declaration-list ',' parameter-declaration
+ //
+ while (true) {
+ // '...'[opt]
+ if (Tok.is(tok::ellipsis)) {
+ ConsumeToken();
+ if (Tok.is(tok::r_paren))
+ return TPResult::True; // '...)' is a sign of a function declarator.
+ else
+ return TPResult::False;
+ }
+
+ // An attribute-specifier-seq here is a sign of a function declarator.
+ if (isCXX11AttributeSpecifier(/*Disambiguate*/false,
+ /*OuterMightBeMessageSend*/true))
+ return TPResult::True;
+
+ ParsedAttributes attrs(AttrFactory);
+ MaybeParseMicrosoftAttributes(attrs);
+
+ // decl-specifier-seq
+ // A parameter-declaration's initializer must be preceded by an '=', so
+ // decl-specifier-seq '{' is not a parameter in C++11.
+ TPResult TPR = isCXXDeclarationSpecifier(
+ AllowImplicitTypename, TPResult::False, InvalidAsDeclaration);
+ // A declaration-specifier (not followed by '(' or '{') means this can't be
+ // an expression, but it could still be a template argument.
+ if (TPR != TPResult::Ambiguous &&
+ !(VersusTemplateArgument && TPR == TPResult::True))
+ return TPR;
+
+ bool SeenType = false;
+ do {
+ SeenType |= isCXXDeclarationSpecifierAType();
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error)
+ return TPResult::Error;
+
+ // If we see a parameter name, this can't be a template argument.
+ if (SeenType && Tok.is(tok::identifier))
+ return TPResult::True;
+
+ TPR = isCXXDeclarationSpecifier(AllowImplicitTypename, TPResult::False,
+ InvalidAsDeclaration);
+ if (TPR == TPResult::Error)
+ return TPR;
+
+ // Two declaration-specifiers means this can't be an expression.
+ if (TPR == TPResult::True && !VersusTemplateArgument)
+ return TPR;
+ } while (TPR != TPResult::False);
+
+ // declarator
+ // abstract-declarator[opt]
+ TPR = TryParseDeclarator(true/*mayBeAbstract*/);
+ if (TPR != TPResult::Ambiguous)
+ return TPR;
+
+ // [GNU] attributes[opt]
+ if (Tok.is(tok::kw___attribute))
+ return TPResult::True;
+
+ // If we're disambiguating a template argument in a default argument in
+ // a class definition versus a parameter declaration, an '=' here
+ // disambiguates the parse one way or the other.
+ // If this is a parameter, it must have a default argument because
+ // (a) the previous parameter did, and
+ // (b) this must be the first declaration of the function, so we can't
+ // inherit any default arguments from elsewhere.
+ // FIXME: If we reach a ')' without consuming any '>'s, then this must
+ // also be a function parameter (that's missing its default argument).
+ if (VersusTemplateArgument)
+ return Tok.is(tok::equal) ? TPResult::True : TPResult::False;
+
+ if (Tok.is(tok::equal)) {
+ // '=' assignment-expression
+ // Parse through assignment-expression.
+ if (!SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch))
+ return TPResult::Error;
+ }
+
+ if (Tok.is(tok::ellipsis)) {
+ ConsumeToken();
+ if (Tok.is(tok::r_paren))
+ return TPResult::True; // '...)' is a sign of a function declarator.
+ else
+ return TPResult::False;
+ }
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ return TPResult::Ambiguous;
+}
+
+/// TryParseFunctionDeclarator - We parsed a '(' and we want to try to continue
+/// parsing as a function declarator.
+/// If TryParseFunctionDeclarator fully parsed the function declarator, it will
+/// return TPResult::Ambiguous, otherwise it will return either False() or
+/// Error().
+///
+/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
+/// exception-specification[opt]
+///
+/// exception-specification:
+/// 'throw' '(' type-id-list[opt] ')'
+///
+Parser::TPResult Parser::TryParseFunctionDeclarator() {
+ // The '(' is already parsed.
+
+ TPResult TPR = TryParseParameterDeclarationClause();
+ if (TPR == TPResult::Ambiguous && Tok.isNot(tok::r_paren))
+ TPR = TPResult::False;
+
+ if (TPR == TPResult::False || TPR == TPResult::Error)
+ return TPR;
+
+ // Parse through the parens.
+ if (!SkipUntil(tok::r_paren, StopAtSemi))
+ return TPResult::Error;
+
+ // cv-qualifier-seq
+ while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw___unaligned,
+ tok::kw_restrict))
+ ConsumeToken();
+
+ // ref-qualifier[opt]
+ if (Tok.isOneOf(tok::amp, tok::ampamp))
+ ConsumeToken();
+
+ // exception-specification
+ if (Tok.is(tok::kw_throw)) {
+ ConsumeToken();
+ if (Tok.isNot(tok::l_paren))
+ return TPResult::Error;
+
+ // Parse through the parens after 'throw'.
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren, StopAtSemi))
+ return TPResult::Error;
+ }
+ if (Tok.is(tok::kw_noexcept)) {
+ ConsumeToken();
+ // Possibly an expression as well.
+ if (Tok.is(tok::l_paren)) {
+ // Find the matching rparen.
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren, StopAtSemi))
+ return TPResult::Error;
+ }
+ }
+
+ return TPResult::Ambiguous;
+}
+
+/// '[' constant-expression[opt] ']'
+///
+Parser::TPResult Parser::TryParseBracketDeclarator() {
+ ConsumeBracket();
+
+ // A constant-expression cannot begin with a '{', but the
+ // expr-or-braced-init-list of a postfix-expression can.
+ if (Tok.is(tok::l_brace))
+ return TPResult::False;
+
+ if (!SkipUntil(tok::r_square, tok::comma, StopAtSemi | StopBeforeMatch))
+ return TPResult::Error;
+
+ // If we hit a comma before the ']', this is not a constant-expression,
+ // but might still be the expr-or-braced-init-list of a postfix-expression.
+ if (Tok.isNot(tok::r_square))
+ return TPResult::False;
+
+ ConsumeBracket();
+ return TPResult::Ambiguous;
+}
+
+/// Determine whether we might be looking at the '<' template-argument-list '>'
+/// of a template-id or simple-template-id, rather than a less-than comparison.
+/// This will often fail and produce an ambiguity, but should never be wrong
+/// if it returns True or False.
+Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
+ if (!TokensToSkip) {
+ if (Tok.isNot(tok::less))
+ return TPResult::False;
+ if (NextToken().is(tok::greater))
+ return TPResult::True;
+ }
+
+ RevertingTentativeParsingAction PA(*this);
+
+ while (TokensToSkip) {
+ ConsumeAnyToken();
+ --TokensToSkip;
+ }
+
+ if (!TryConsumeToken(tok::less))
+ return TPResult::False;
+
+ // We can't do much to tell an expression apart from a template-argument,
+ // but one good distinguishing factor is that a "decl-specifier" not
+ // followed by '(' or '{' can't appear in an expression.
+ bool InvalidAsTemplateArgumentList = false;
+ if (isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::False,
+ &InvalidAsTemplateArgumentList) ==
+ TPResult::True)
+ return TPResult::True;
+ if (InvalidAsTemplateArgumentList)
+ return TPResult::False;
+
+ // FIXME: In many contexts, X<thing1, Type> can only be a
+ // template-argument-list. But that's not true in general:
+ //
+ // using b = int;
+ // void f() {
+ // int a = A<B, b, c = C>D; // OK, declares b, not a template-id.
+ //
+ // X<Y<0, int> // ', int>' might be end of X's template argument list
+ //
+ // We might be able to disambiguate a few more cases if we're careful.
+
+ // A template-argument-list must be terminated by a '>'.
+ if (SkipUntil({tok::greater, tok::greatergreater, tok::greatergreatergreater},
+ StopAtSemi | StopBeforeMatch))
+ return TPResult::Ambiguous;
+ return TPResult::False;
+}
+
+/// Determine whether we might be looking at the '(' of a C++20 explicit(bool)
+/// in an earlier language mode.
+Parser::TPResult Parser::isExplicitBool() {
+ assert(Tok.is(tok::l_paren) && "expected to be looking at a '(' token");
+
+ RevertingTentativeParsingAction PA(*this);
+ ConsumeParen();
+
+ // We can only have 'explicit' on a constructor, conversion function, or
+ // deduction guide. The declarator of a deduction guide cannot be
+ // parenthesized, so we know this isn't a deduction guide. So the only
+ // thing we need to check for is some number of parens followed by either
+ // the current class name or 'operator'.
+ while (Tok.is(tok::l_paren))
+ ConsumeParen();
+
+ if (TryAnnotateOptionalCXXScopeToken())
+ return TPResult::Error;
+
+ // Class-scope constructor and conversion function names can't really be
+ // qualified, but we get better diagnostics if we assume they can be.
+ CXXScopeSpec SS;
+ if (Tok.is(tok::annot_cxxscope)) {
+ Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
+ Tok.getAnnotationRange(),
+ SS);
+ ConsumeAnnotationToken();
+ }
+
+ // 'explicit(operator' might be explicit(bool) or the declaration of a
+ // conversion function, but it's probably a conversion function.
+ if (Tok.is(tok::kw_operator))
+ return TPResult::Ambiguous;
+
+ // If this can't be a constructor name, it can only be explicit(bool).
+ if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
+ return TPResult::True;
+ if (!Actions.isCurrentClassName(Tok.is(tok::identifier)
+ ? *Tok.getIdentifierInfo()
+ : *takeTemplateIdAnnotation(Tok)->Name,
+ getCurScope(), &SS))
+ return TPResult::True;
+ // Formally, we must have a right-paren after the constructor name to match
+ // the grammar for a constructor. But clang permits a parenthesized
+ // constructor declarator, so also allow a constructor declarator to follow
+ // with no ')' token after the constructor name.
+ if (!NextToken().is(tok::r_paren) &&
+ !isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(),
+ /*DeductionGuide=*/false))
+ return TPResult::True;
+
+ // Might be explicit(bool) or a parenthesized constructor name.
+ return TPResult::Ambiguous;
+}
diff --git a/contrib/libs/clang16/lib/Parse/Parser.cpp b/contrib/libs/clang16/lib/Parse/Parser.cpp
new file mode 100644
index 0000000000..6db3dc3156
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/Parser.cpp
@@ -0,0 +1,2735 @@
+//===--- Parser.cpp - C Language Family Parser ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Parser interfaces.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Parse/Parser.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/ParsedTemplate.h"
+#include "clang/Sema/Scope.h"
+#include "llvm/Support/Path.h"
+using namespace clang;
+
+
+namespace {
+/// A comment handler that passes comments found by the preprocessor
+/// to the parser action.
+class ActionCommentHandler : public CommentHandler {
+ Sema &S;
+
+public:
+ explicit ActionCommentHandler(Sema &S) : S(S) { }
+
+ bool HandleComment(Preprocessor &PP, SourceRange Comment) override {
+ S.ActOnComment(Comment);
+ return false;
+ }
+};
+} // end anonymous namespace
+
+IdentifierInfo *Parser::getSEHExceptKeyword() {
+ // __except is accepted as a (contextual) keyword
+ if (!Ident__except && (getLangOpts().MicrosoftExt || getLangOpts().Borland))
+ Ident__except = PP.getIdentifierInfo("__except");
+
+ return Ident__except;
+}
+
+Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
+ : PP(pp), PreferredType(pp.isCodeCompletionEnabled()), Actions(actions),
+ Diags(PP.getDiagnostics()), GreaterThanIsOperator(true),
+ ColonIsSacred(false), InMessageExpression(false),
+ TemplateParameterDepth(0), ParsingInObjCContainer(false) {
+ SkipFunctionBodies = pp.isCodeCompletionEnabled() || skipFunctionBodies;
+ Tok.startToken();
+ Tok.setKind(tok::eof);
+ Actions.CurScope = nullptr;
+ NumCachedScopes = 0;
+ CurParsedObjCImpl = nullptr;
+
+ // Add #pragma handlers. These are removed and destroyed in the
+ // destructor.
+ initializePragmaHandlers();
+
+ CommentSemaHandler.reset(new ActionCommentHandler(actions));
+ PP.addCommentHandler(CommentSemaHandler.get());
+
+ PP.setCodeCompletionHandler(*this);
+}
+
+DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) {
+ return Diags.Report(Loc, DiagID);
+}
+
+DiagnosticBuilder Parser::Diag(const Token &Tok, unsigned DiagID) {
+ return Diag(Tok.getLocation(), DiagID);
+}
+
+/// Emits a diagnostic suggesting parentheses surrounding a
+/// given range.
+///
+/// \param Loc The location where we'll emit the diagnostic.
+/// \param DK The kind of diagnostic to emit.
+/// \param ParenRange Source range enclosing code that should be parenthesized.
+void Parser::SuggestParentheses(SourceLocation Loc, unsigned DK,
+ SourceRange ParenRange) {
+ SourceLocation EndLoc = PP.getLocForEndOfToken(ParenRange.getEnd());
+ if (!ParenRange.getEnd().isFileID() || EndLoc.isInvalid()) {
+ // We can't display the parentheses, so just dig the
+ // warning/error and return.
+ Diag(Loc, DK);
+ return;
+ }
+
+ Diag(Loc, DK)
+ << FixItHint::CreateInsertion(ParenRange.getBegin(), "(")
+ << FixItHint::CreateInsertion(EndLoc, ")");
+}
+
+static bool IsCommonTypo(tok::TokenKind ExpectedTok, const Token &Tok) {
+ switch (ExpectedTok) {
+ case tok::semi:
+ return Tok.is(tok::colon) || Tok.is(tok::comma); // : or , for ;
+ default: return false;
+ }
+}
+
+bool Parser::ExpectAndConsume(tok::TokenKind ExpectedTok, unsigned DiagID,
+ StringRef Msg) {
+ if (Tok.is(ExpectedTok) || Tok.is(tok::code_completion)) {
+ ConsumeAnyToken();
+ return false;
+ }
+
+ // Detect common single-character typos and resume.
+ if (IsCommonTypo(ExpectedTok, Tok)) {
+ SourceLocation Loc = Tok.getLocation();
+ {
+ DiagnosticBuilder DB = Diag(Loc, DiagID);
+ DB << FixItHint::CreateReplacement(
+ SourceRange(Loc), tok::getPunctuatorSpelling(ExpectedTok));
+ if (DiagID == diag::err_expected)
+ DB << ExpectedTok;
+ else if (DiagID == diag::err_expected_after)
+ DB << Msg << ExpectedTok;
+ else
+ DB << Msg;
+ }
+
+ // Pretend there wasn't a problem.
+ ConsumeAnyToken();
+ return false;
+ }
+
+ SourceLocation EndLoc = PP.getLocForEndOfToken(PrevTokLocation);
+ const char *Spelling = nullptr;
+ if (EndLoc.isValid())
+ Spelling = tok::getPunctuatorSpelling(ExpectedTok);
+
+ DiagnosticBuilder DB =
+ Spelling
+ ? Diag(EndLoc, DiagID) << FixItHint::CreateInsertion(EndLoc, Spelling)
+ : Diag(Tok, DiagID);
+ if (DiagID == diag::err_expected)
+ DB << ExpectedTok;
+ else if (DiagID == diag::err_expected_after)
+ DB << Msg << ExpectedTok;
+ else
+ DB << Msg;
+
+ return true;
+}
+
+bool Parser::ExpectAndConsumeSemi(unsigned DiagID, StringRef TokenUsed) {
+ if (TryConsumeToken(tok::semi))
+ return false;
+
+ if (Tok.is(tok::code_completion)) {
+ handleUnexpectedCodeCompletionToken();
+ return false;
+ }
+
+ if ((Tok.is(tok::r_paren) || Tok.is(tok::r_square)) &&
+ NextToken().is(tok::semi)) {
+ Diag(Tok, diag::err_extraneous_token_before_semi)
+ << PP.getSpelling(Tok)
+ << FixItHint::CreateRemoval(Tok.getLocation());
+ ConsumeAnyToken(); // The ')' or ']'.
+ ConsumeToken(); // The ';'.
+ return false;
+ }
+
+ return ExpectAndConsume(tok::semi, DiagID , TokenUsed);
+}
+
+void Parser::ConsumeExtraSemi(ExtraSemiKind Kind, DeclSpec::TST TST) {
+ if (!Tok.is(tok::semi)) return;
+
+ bool HadMultipleSemis = false;
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc = Tok.getLocation();
+ ConsumeToken();
+
+ while ((Tok.is(tok::semi) && !Tok.isAtStartOfLine())) {
+ HadMultipleSemis = true;
+ EndLoc = Tok.getLocation();
+ ConsumeToken();
+ }
+
+ // C++11 allows extra semicolons at namespace scope, but not in any of the
+ // other contexts.
+ if (Kind == OutsideFunction && getLangOpts().CPlusPlus) {
+ if (getLangOpts().CPlusPlus11)
+ Diag(StartLoc, diag::warn_cxx98_compat_top_level_semi)
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+ else
+ Diag(StartLoc, diag::ext_extra_semi_cxx11)
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+ return;
+ }
+
+ if (Kind != AfterMemberFunctionDefinition || HadMultipleSemis)
+ Diag(StartLoc, diag::ext_extra_semi)
+ << Kind << DeclSpec::getSpecifierName(TST,
+ Actions.getASTContext().getPrintingPolicy())
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+ else
+ // A single semicolon is valid after a member function definition.
+ Diag(StartLoc, diag::warn_extra_semi_after_mem_fn_def)
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+}
+
+bool Parser::expectIdentifier() {
+ if (Tok.is(tok::identifier))
+ return false;
+ if (const auto *II = Tok.getIdentifierInfo()) {
+ if (II->isCPlusPlusKeyword(getLangOpts())) {
+ Diag(Tok, diag::err_expected_token_instead_of_objcxx_keyword)
+ << tok::identifier << Tok.getIdentifierInfo();
+ // Objective-C++: Recover by treating this keyword as a valid identifier.
+ return false;
+ }
+ }
+ Diag(Tok, diag::err_expected) << tok::identifier;
+ return true;
+}
+
+void Parser::checkCompoundToken(SourceLocation FirstTokLoc,
+ tok::TokenKind FirstTokKind, CompoundToken Op) {
+ if (FirstTokLoc.isInvalid())
+ return;
+ SourceLocation SecondTokLoc = Tok.getLocation();
+
+ // If either token is in a macro, we expect both tokens to come from the same
+ // macro expansion.
+ if ((FirstTokLoc.isMacroID() || SecondTokLoc.isMacroID()) &&
+ PP.getSourceManager().getFileID(FirstTokLoc) !=
+ PP.getSourceManager().getFileID(SecondTokLoc)) {
+ Diag(FirstTokLoc, diag::warn_compound_token_split_by_macro)
+ << (FirstTokKind == Tok.getKind()) << FirstTokKind << Tok.getKind()
+ << static_cast<int>(Op) << SourceRange(FirstTokLoc);
+ Diag(SecondTokLoc, diag::note_compound_token_split_second_token_here)
+ << (FirstTokKind == Tok.getKind()) << Tok.getKind()
+ << SourceRange(SecondTokLoc);
+ return;
+ }
+
+ // We expect the tokens to abut.
+ if (Tok.hasLeadingSpace() || Tok.isAtStartOfLine()) {
+ SourceLocation SpaceLoc = PP.getLocForEndOfToken(FirstTokLoc);
+ if (SpaceLoc.isInvalid())
+ SpaceLoc = FirstTokLoc;
+ Diag(SpaceLoc, diag::warn_compound_token_split_by_whitespace)
+ << (FirstTokKind == Tok.getKind()) << FirstTokKind << Tok.getKind()
+ << static_cast<int>(Op) << SourceRange(FirstTokLoc, SecondTokLoc);
+ return;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Error recovery.
+//===----------------------------------------------------------------------===//
+
+static bool HasFlagsSet(Parser::SkipUntilFlags L, Parser::SkipUntilFlags R) {
+ return (static_cast<unsigned>(L) & static_cast<unsigned>(R)) != 0;
+}
+
+/// SkipUntil - Read tokens until we get to the specified token, then consume
+/// it (unless no flag StopBeforeMatch). Because we cannot guarantee that the
+/// token will ever occur, this skips to the next token, or to some likely
+/// good stopping point. If StopAtSemi is true, skipping will stop at a ';'
+/// character.
+///
+/// If SkipUntil finds the specified token, it returns true, otherwise it
+/// returns false.
+bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, SkipUntilFlags Flags) {
+ // We always want this function to skip at least one token if the first token
+ // isn't T and if not at EOF.
+ bool isFirstTokenSkipped = true;
+ while (true) {
+ // If we found one of the tokens, stop and return true.
+ for (unsigned i = 0, NumToks = Toks.size(); i != NumToks; ++i) {
+ if (Tok.is(Toks[i])) {
+ if (HasFlagsSet(Flags, StopBeforeMatch)) {
+ // Noop, don't consume the token.
+ } else {
+ ConsumeAnyToken();
+ }
+ return true;
+ }
+ }
+
+ // Important special case: The caller has given up and just wants us to
+ // skip the rest of the file. Do this without recursing, since we can
+ // get here precisely because the caller detected too much recursion.
+ if (Toks.size() == 1 && Toks[0] == tok::eof &&
+ !HasFlagsSet(Flags, StopAtSemi) &&
+ !HasFlagsSet(Flags, StopAtCodeCompletion)) {
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+ return true;
+ }
+
+ switch (Tok.getKind()) {
+ case tok::eof:
+ // Ran out of tokens.
+ return false;
+
+ case tok::annot_pragma_openmp:
+ case tok::annot_attr_openmp:
+ case tok::annot_pragma_openmp_end:
+ // Stop before an OpenMP pragma boundary.
+ if (OpenMPDirectiveParsing)
+ return false;
+ ConsumeAnnotationToken();
+ break;
+ case tok::annot_module_begin:
+ case tok::annot_module_end:
+ case tok::annot_module_include:
+ // Stop before we change submodules. They generally indicate a "good"
+ // place to pick up parsing again (except in the special case where
+ // we're trying to skip to EOF).
+ return false;
+
+ case tok::code_completion:
+ if (!HasFlagsSet(Flags, StopAtCodeCompletion))
+ handleUnexpectedCodeCompletionToken();
+ return false;
+
+ case tok::l_paren:
+ // Recursively skip properly-nested parens.
+ ConsumeParen();
+ if (HasFlagsSet(Flags, StopAtCodeCompletion))
+ SkipUntil(tok::r_paren, StopAtCodeCompletion);
+ else
+ SkipUntil(tok::r_paren);
+ break;
+ case tok::l_square:
+ // Recursively skip properly-nested square brackets.
+ ConsumeBracket();
+ if (HasFlagsSet(Flags, StopAtCodeCompletion))
+ SkipUntil(tok::r_square, StopAtCodeCompletion);
+ else
+ SkipUntil(tok::r_square);
+ break;
+ case tok::l_brace:
+ // Recursively skip properly-nested braces.
+ ConsumeBrace();
+ if (HasFlagsSet(Flags, StopAtCodeCompletion))
+ SkipUntil(tok::r_brace, StopAtCodeCompletion);
+ else
+ SkipUntil(tok::r_brace);
+ break;
+ case tok::question:
+ // Recursively skip ? ... : pairs; these function as brackets. But
+ // still stop at a semicolon if requested.
+ ConsumeToken();
+ SkipUntil(tok::colon,
+ SkipUntilFlags(unsigned(Flags) &
+ unsigned(StopAtCodeCompletion | StopAtSemi)));
+ break;
+
+ // Okay, we found a ']' or '}' or ')', which we think should be balanced.
+ // Since the user wasn't looking for this token (if they were, it would
+ // already be handled), this isn't balanced. If there is a LHS token at a
+ // higher level, we will assume that this matches the unbalanced token
+ // and return it. Otherwise, this is a spurious RHS token, which we skip.
+ case tok::r_paren:
+ if (ParenCount && !isFirstTokenSkipped)
+ return false; // Matches something.
+ ConsumeParen();
+ break;
+ case tok::r_square:
+ if (BracketCount && !isFirstTokenSkipped)
+ return false; // Matches something.
+ ConsumeBracket();
+ break;
+ case tok::r_brace:
+ if (BraceCount && !isFirstTokenSkipped)
+ return false; // Matches something.
+ ConsumeBrace();
+ break;
+
+ case tok::semi:
+ if (HasFlagsSet(Flags, StopAtSemi))
+ return false;
+ [[fallthrough]];
+ default:
+ // Skip this token.
+ ConsumeAnyToken();
+ break;
+ }
+ isFirstTokenSkipped = false;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Scope manipulation
+//===----------------------------------------------------------------------===//
+
+/// EnterScope - Start a new scope.
+void Parser::EnterScope(unsigned ScopeFlags) {
+ if (NumCachedScopes) {
+ Scope *N = ScopeCache[--NumCachedScopes];
+ N->Init(getCurScope(), ScopeFlags);
+ Actions.CurScope = N;
+ } else {
+ Actions.CurScope = new Scope(getCurScope(), ScopeFlags, Diags);
+ }
+}
+
+/// ExitScope - Pop a scope off the scope stack.
+void Parser::ExitScope() {
+ assert(getCurScope() && "Scope imbalance!");
+
+ // Inform the actions module that this scope is going away if there are any
+ // decls in it.
+ Actions.ActOnPopScope(Tok.getLocation(), getCurScope());
+
+ Scope *OldScope = getCurScope();
+ Actions.CurScope = OldScope->getParent();
+
+ if (NumCachedScopes == ScopeCacheSize)
+ delete OldScope;
+ else
+ ScopeCache[NumCachedScopes++] = OldScope;
+}
+
+/// Set the flags for the current scope to ScopeFlags. If ManageFlags is false,
+/// this object does nothing.
+Parser::ParseScopeFlags::ParseScopeFlags(Parser *Self, unsigned ScopeFlags,
+ bool ManageFlags)
+ : CurScope(ManageFlags ? Self->getCurScope() : nullptr) {
+ if (CurScope) {
+ OldFlags = CurScope->getFlags();
+ CurScope->setFlags(ScopeFlags);
+ }
+}
+
+/// Restore the flags for the current scope to what they were before this
+/// object overrode them.
+Parser::ParseScopeFlags::~ParseScopeFlags() {
+ if (CurScope)
+ CurScope->setFlags(OldFlags);
+}
+
+
+//===----------------------------------------------------------------------===//
+// C99 6.9: External Definitions.
+//===----------------------------------------------------------------------===//
+
+Parser::~Parser() {
+ // If we still have scopes active, delete the scope tree.
+ delete getCurScope();
+ Actions.CurScope = nullptr;
+
+ // Free the scope cache.
+ for (unsigned i = 0, e = NumCachedScopes; i != e; ++i)
+ delete ScopeCache[i];
+
+ resetPragmaHandlers();
+
+ PP.removeCommentHandler(CommentSemaHandler.get());
+
+ PP.clearCodeCompletionHandler();
+
+ DestroyTemplateIds();
+}
+
+/// Initialize - Warm up the parser.
+///
+void Parser::Initialize() {
+ // Create the translation unit scope. Install it as the current scope.
+ assert(getCurScope() == nullptr && "A scope is already active?");
+ EnterScope(Scope::DeclScope);
+ Actions.ActOnTranslationUnitScope(getCurScope());
+
+ // Initialization for Objective-C context sensitive keywords recognition.
+ // Referenced in Parser::ParseObjCTypeQualifierList.
+ if (getLangOpts().ObjC) {
+ ObjCTypeQuals[objc_in] = &PP.getIdentifierTable().get("in");
+ ObjCTypeQuals[objc_out] = &PP.getIdentifierTable().get("out");
+ ObjCTypeQuals[objc_inout] = &PP.getIdentifierTable().get("inout");
+ ObjCTypeQuals[objc_oneway] = &PP.getIdentifierTable().get("oneway");
+ ObjCTypeQuals[objc_bycopy] = &PP.getIdentifierTable().get("bycopy");
+ ObjCTypeQuals[objc_byref] = &PP.getIdentifierTable().get("byref");
+ ObjCTypeQuals[objc_nonnull] = &PP.getIdentifierTable().get("nonnull");
+ ObjCTypeQuals[objc_nullable] = &PP.getIdentifierTable().get("nullable");
+ ObjCTypeQuals[objc_null_unspecified]
+ = &PP.getIdentifierTable().get("null_unspecified");
+ }
+
+ Ident_instancetype = nullptr;
+ Ident_final = nullptr;
+ Ident_sealed = nullptr;
+ Ident_abstract = nullptr;
+ Ident_override = nullptr;
+ Ident_GNU_final = nullptr;
+ Ident_import = nullptr;
+ Ident_module = nullptr;
+
+ Ident_super = &PP.getIdentifierTable().get("super");
+
+ Ident_vector = nullptr;
+ Ident_bool = nullptr;
+ Ident_Bool = nullptr;
+ Ident_pixel = nullptr;
+ if (getLangOpts().AltiVec || getLangOpts().ZVector) {
+ Ident_vector = &PP.getIdentifierTable().get("vector");
+ Ident_bool = &PP.getIdentifierTable().get("bool");
+ Ident_Bool = &PP.getIdentifierTable().get("_Bool");
+ }
+ if (getLangOpts().AltiVec)
+ Ident_pixel = &PP.getIdentifierTable().get("pixel");
+
+ Ident_introduced = nullptr;
+ Ident_deprecated = nullptr;
+ Ident_obsoleted = nullptr;
+ Ident_unavailable = nullptr;
+ Ident_strict = nullptr;
+ Ident_replacement = nullptr;
+
+ Ident_language = Ident_defined_in = Ident_generated_declaration = nullptr;
+
+ Ident__except = nullptr;
+
+ Ident__exception_code = Ident__exception_info = nullptr;
+ Ident__abnormal_termination = Ident___exception_code = nullptr;
+ Ident___exception_info = Ident___abnormal_termination = nullptr;
+ Ident_GetExceptionCode = Ident_GetExceptionInfo = nullptr;
+ Ident_AbnormalTermination = nullptr;
+
+ if(getLangOpts().Borland) {
+ Ident__exception_info = PP.getIdentifierInfo("_exception_info");
+ Ident___exception_info = PP.getIdentifierInfo("__exception_info");
+ Ident_GetExceptionInfo = PP.getIdentifierInfo("GetExceptionInformation");
+ Ident__exception_code = PP.getIdentifierInfo("_exception_code");
+ Ident___exception_code = PP.getIdentifierInfo("__exception_code");
+ Ident_GetExceptionCode = PP.getIdentifierInfo("GetExceptionCode");
+ Ident__abnormal_termination = PP.getIdentifierInfo("_abnormal_termination");
+ Ident___abnormal_termination = PP.getIdentifierInfo("__abnormal_termination");
+ Ident_AbnormalTermination = PP.getIdentifierInfo("AbnormalTermination");
+
+ PP.SetPoisonReason(Ident__exception_code,diag::err_seh___except_block);
+ PP.SetPoisonReason(Ident___exception_code,diag::err_seh___except_block);
+ PP.SetPoisonReason(Ident_GetExceptionCode,diag::err_seh___except_block);
+ PP.SetPoisonReason(Ident__exception_info,diag::err_seh___except_filter);
+ PP.SetPoisonReason(Ident___exception_info,diag::err_seh___except_filter);
+ PP.SetPoisonReason(Ident_GetExceptionInfo,diag::err_seh___except_filter);
+ PP.SetPoisonReason(Ident__abnormal_termination,diag::err_seh___finally_block);
+ PP.SetPoisonReason(Ident___abnormal_termination,diag::err_seh___finally_block);
+ PP.SetPoisonReason(Ident_AbnormalTermination,diag::err_seh___finally_block);
+ }
+
+ if (getLangOpts().CPlusPlusModules) {
+ Ident_import = PP.getIdentifierInfo("import");
+ Ident_module = PP.getIdentifierInfo("module");
+ }
+
+ Actions.Initialize();
+
+ // Prime the lexer look-ahead.
+ ConsumeToken();
+}
+
+void Parser::DestroyTemplateIds() {
+ for (TemplateIdAnnotation *Id : TemplateIds)
+ Id->Destroy();
+ TemplateIds.clear();
+}
+
+/// Parse the first top-level declaration in a translation unit.
+///
+/// translation-unit:
+/// [C] external-declaration
+/// [C] translation-unit external-declaration
+/// [C++] top-level-declaration-seq[opt]
+/// [C++20] global-module-fragment[opt] module-declaration
+/// top-level-declaration-seq[opt] private-module-fragment[opt]
+///
+/// Note that in C, it is an error if there is no first declaration.
+bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result,
+ Sema::ModuleImportState &ImportState) {
+ Actions.ActOnStartOfTranslationUnit();
+
+ // For C++20 modules, a module decl must be the first in the TU. We also
+ // need to track module imports.
+ ImportState = Sema::ModuleImportState::FirstDecl;
+ bool NoTopLevelDecls = ParseTopLevelDecl(Result, ImportState);
+
+ // C11 6.9p1 says translation units must have at least one top-level
+ // declaration. C++ doesn't have this restriction. We also don't want to
+ // complain if we have a precompiled header, although technically if the PCH
+ // is empty we should still emit the (pedantic) diagnostic.
+ // If the main file is a header, we're only pretending it's a TU; don't warn.
+ if (NoTopLevelDecls && !Actions.getASTContext().getExternalSource() &&
+ !getLangOpts().CPlusPlus && !getLangOpts().IsHeaderFile)
+ Diag(diag::ext_empty_translation_unit);
+
+ return NoTopLevelDecls;
+}
+
+/// ParseTopLevelDecl - Parse one top-level declaration, return whatever the
+/// action tells us to. This returns true if the EOF was encountered.
+///
+/// top-level-declaration:
+/// declaration
+/// [C++20] module-import-declaration
+bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
+ Sema::ModuleImportState &ImportState) {
+ DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this);
+
+ // Skip over the EOF token, flagging end of previous input for incremental
+ // processing
+ if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::eof))
+ ConsumeToken();
+
+ Result = nullptr;
+ switch (Tok.getKind()) {
+ case tok::annot_pragma_unused:
+ HandlePragmaUnused();
+ return false;
+
+ case tok::kw_export:
+ switch (NextToken().getKind()) {
+ case tok::kw_module:
+ goto module_decl;
+
+ // Note: no need to handle kw_import here. We only form kw_import under
+ // the Modules TS, and in that case 'export import' is parsed as an
+ // export-declaration containing an import-declaration.
+
+ // Recognize context-sensitive C++20 'export module' and 'export import'
+ // declarations.
+ case tok::identifier: {
+ IdentifierInfo *II = NextToken().getIdentifierInfo();
+ if ((II == Ident_module || II == Ident_import) &&
+ GetLookAheadToken(2).isNot(tok::coloncolon)) {
+ if (II == Ident_module)
+ goto module_decl;
+ else
+ goto import_decl;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+
+ case tok::kw_module:
+ module_decl:
+ Result = ParseModuleDecl(ImportState);
+ return false;
+
+ case tok::kw_import:
+ import_decl: {
+ Decl *ImportDecl = ParseModuleImport(SourceLocation(), ImportState);
+ Result = Actions.ConvertDeclToDeclGroup(ImportDecl);
+ return false;
+ }
+
+ case tok::annot_module_include: {
+ auto Loc = Tok.getLocation();
+ Module *Mod = reinterpret_cast<Module *>(Tok.getAnnotationValue());
+ // FIXME: We need a better way to disambiguate C++ clang modules and
+ // standard C++ modules.
+ if (!getLangOpts().CPlusPlusModules || !Mod->isHeaderUnit())
+ Actions.ActOnModuleInclude(Loc, Mod);
+ else {
+ DeclResult Import =
+ Actions.ActOnModuleImport(Loc, SourceLocation(), Loc, Mod);
+ Decl *ImportDecl = Import.isInvalid() ? nullptr : Import.get();
+ Result = Actions.ConvertDeclToDeclGroup(ImportDecl);
+ }
+ ConsumeAnnotationToken();
+ return false;
+ }
+
+ case tok::annot_module_begin:
+ Actions.ActOnModuleBegin(Tok.getLocation(), reinterpret_cast<Module *>(
+ Tok.getAnnotationValue()));
+ ConsumeAnnotationToken();
+ ImportState = Sema::ModuleImportState::NotACXX20Module;
+ return false;
+
+ case tok::annot_module_end:
+ Actions.ActOnModuleEnd(Tok.getLocation(), reinterpret_cast<Module *>(
+ Tok.getAnnotationValue()));
+ ConsumeAnnotationToken();
+ ImportState = Sema::ModuleImportState::NotACXX20Module;
+ return false;
+
+ case tok::eof:
+ // Check whether -fmax-tokens= was reached.
+ if (PP.getMaxTokens() != 0 && PP.getTokenCount() > PP.getMaxTokens()) {
+ PP.Diag(Tok.getLocation(), diag::warn_max_tokens_total)
+ << PP.getTokenCount() << PP.getMaxTokens();
+ SourceLocation OverrideLoc = PP.getMaxTokensOverrideLoc();
+ if (OverrideLoc.isValid()) {
+ PP.Diag(OverrideLoc, diag::note_max_tokens_total_override);
+ }
+ }
+
+ // Late template parsing can begin.
+ Actions.SetLateTemplateParser(LateTemplateParserCallback, nullptr, this);
+ Actions.ActOnEndOfTranslationUnit();
+ //else don't tell Sema that we ended parsing: more input might come.
+ return true;
+
+ case tok::identifier:
+ // C++2a [basic.link]p3:
+ // A token sequence beginning with 'export[opt] module' or
+ // 'export[opt] import' and not immediately followed by '::'
+ // is never interpreted as the declaration of a top-level-declaration.
+ if ((Tok.getIdentifierInfo() == Ident_module ||
+ Tok.getIdentifierInfo() == Ident_import) &&
+ NextToken().isNot(tok::coloncolon)) {
+ if (Tok.getIdentifierInfo() == Ident_module)
+ goto module_decl;
+ else
+ goto import_decl;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ParsedAttributes DeclAttrs(AttrFactory);
+ ParsedAttributes DeclSpecAttrs(AttrFactory);
+ // GNU attributes are applied to the declaration specification while the
+ // standard attributes are applied to the declaration. We parse the two
+ // attribute sets into different containters so we can apply them during
+ // the regular parsing process.
+ while (MaybeParseCXX11Attributes(DeclAttrs) ||
+ MaybeParseGNUAttributes(DeclSpecAttrs))
+ ;
+
+ Result = ParseExternalDeclaration(DeclAttrs, DeclSpecAttrs);
+ // An empty Result might mean a line with ';' or some parsing error, ignore
+ // it.
+ if (Result) {
+ if (ImportState == Sema::ModuleImportState::FirstDecl)
+ // First decl was not modular.
+ ImportState = Sema::ModuleImportState::NotACXX20Module;
+ else if (ImportState == Sema::ModuleImportState::ImportAllowed)
+ // Non-imports disallow further imports.
+ ImportState = Sema::ModuleImportState::ImportFinished;
+ else if (ImportState ==
+ Sema::ModuleImportState::PrivateFragmentImportAllowed)
+ // Non-imports disallow further imports.
+ ImportState = Sema::ModuleImportState::PrivateFragmentImportFinished;
+ }
+ return false;
+}
+
+/// ParseExternalDeclaration:
+///
+/// The `Attrs` that are passed in are C++11 attributes and appertain to the
+/// declaration.
+///
+/// external-declaration: [C99 6.9], declaration: [C++ dcl.dcl]
+/// function-definition
+/// declaration
+/// [GNU] asm-definition
+/// [GNU] __extension__ external-declaration
+/// [OBJC] objc-class-definition
+/// [OBJC] objc-class-declaration
+/// [OBJC] objc-alias-declaration
+/// [OBJC] objc-protocol-definition
+/// [OBJC] objc-method-definition
+/// [OBJC] @end
+/// [C++] linkage-specification
+/// [GNU] asm-definition:
+/// simple-asm-expr ';'
+/// [C++11] empty-declaration
+/// [C++11] attribute-declaration
+///
+/// [C++11] empty-declaration:
+/// ';'
+///
+/// [C++0x/GNU] 'extern' 'template' declaration
+///
+/// [Modules-TS] module-import-declaration
+///
+Parser::DeclGroupPtrTy
+Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
+ ParsedAttributes &DeclSpecAttrs,
+ ParsingDeclSpec *DS) {
+ DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this);
+ ParenBraceBracketBalancer BalancerRAIIObj(*this);
+
+ if (PP.isCodeCompletionReached()) {
+ cutOffParsing();
+ return nullptr;
+ }
+
+ Decl *SingleDecl = nullptr;
+ switch (Tok.getKind()) {
+ case tok::annot_pragma_vis:
+ HandlePragmaVisibility();
+ return nullptr;
+ case tok::annot_pragma_pack:
+ HandlePragmaPack();
+ return nullptr;
+ case tok::annot_pragma_msstruct:
+ HandlePragmaMSStruct();
+ return nullptr;
+ case tok::annot_pragma_align:
+ HandlePragmaAlign();
+ return nullptr;
+ case tok::annot_pragma_weak:
+ HandlePragmaWeak();
+ return nullptr;
+ case tok::annot_pragma_weakalias:
+ HandlePragmaWeakAlias();
+ return nullptr;
+ case tok::annot_pragma_redefine_extname:
+ HandlePragmaRedefineExtname();
+ return nullptr;
+ case tok::annot_pragma_fp_contract:
+ HandlePragmaFPContract();
+ return nullptr;
+ case tok::annot_pragma_fenv_access:
+ case tok::annot_pragma_fenv_access_ms:
+ HandlePragmaFEnvAccess();
+ return nullptr;
+ case tok::annot_pragma_fenv_round:
+ HandlePragmaFEnvRound();
+ return nullptr;
+ case tok::annot_pragma_float_control:
+ HandlePragmaFloatControl();
+ return nullptr;
+ case tok::annot_pragma_fp:
+ HandlePragmaFP();
+ break;
+ case tok::annot_pragma_opencl_extension:
+ HandlePragmaOpenCLExtension();
+ return nullptr;
+ case tok::annot_attr_openmp:
+ case tok::annot_pragma_openmp: {
+ AccessSpecifier AS = AS_none;
+ return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, Attrs);
+ }
+ case tok::annot_pragma_ms_pointers_to_members:
+ HandlePragmaMSPointersToMembers();
+ return nullptr;
+ case tok::annot_pragma_ms_vtordisp:
+ HandlePragmaMSVtorDisp();
+ return nullptr;
+ case tok::annot_pragma_ms_pragma:
+ HandlePragmaMSPragma();
+ return nullptr;
+ case tok::annot_pragma_dump:
+ HandlePragmaDump();
+ return nullptr;
+ case tok::annot_pragma_attribute:
+ HandlePragmaAttribute();
+ return nullptr;
+ case tok::semi:
+ // Either a C++11 empty-declaration or attribute-declaration.
+ SingleDecl =
+ Actions.ActOnEmptyDeclaration(getCurScope(), Attrs, Tok.getLocation());
+ ConsumeExtraSemi(OutsideFunction);
+ break;
+ case tok::r_brace:
+ Diag(Tok, diag::err_extraneous_closing_brace);
+ ConsumeBrace();
+ return nullptr;
+ case tok::eof:
+ Diag(Tok, diag::err_expected_external_declaration);
+ return nullptr;
+ case tok::kw___extension__: {
+ // __extension__ silences extension warnings in the subexpression.
+ ExtensionRAIIObject O(Diags); // Use RAII to do this.
+ ConsumeToken();
+ return ParseExternalDeclaration(Attrs, DeclSpecAttrs);
+ }
+ case tok::kw_asm: {
+ ProhibitAttributes(Attrs);
+
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc;
+
+ ExprResult Result(ParseSimpleAsm(/*ForAsmLabel*/ false, &EndLoc));
+
+ // Check if GNU-style InlineAsm is disabled.
+ // Empty asm string is allowed because it will not introduce
+ // any assembly code.
+ if (!(getLangOpts().GNUAsm || Result.isInvalid())) {
+ const auto *SL = cast<StringLiteral>(Result.get());
+ if (!SL->getString().trim().empty())
+ Diag(StartLoc, diag::err_gnu_inline_asm_disabled);
+ }
+
+ ExpectAndConsume(tok::semi, diag::err_expected_after,
+ "top-level asm block");
+
+ if (Result.isInvalid())
+ return nullptr;
+ SingleDecl = Actions.ActOnFileScopeAsmDecl(Result.get(), StartLoc, EndLoc);
+ break;
+ }
+ case tok::at:
+ return ParseObjCAtDirectives(Attrs, DeclSpecAttrs);
+ case tok::minus:
+ case tok::plus:
+ if (!getLangOpts().ObjC) {
+ Diag(Tok, diag::err_expected_external_declaration);
+ ConsumeToken();
+ return nullptr;
+ }
+ SingleDecl = ParseObjCMethodDefinition();
+ break;
+ case tok::code_completion:
+ cutOffParsing();
+ if (CurParsedObjCImpl) {
+ // Code-complete Objective-C methods even without leading '-'/'+' prefix.
+ Actions.CodeCompleteObjCMethodDecl(getCurScope(),
+ /*IsInstanceMethod=*/std::nullopt,
+ /*ReturnType=*/nullptr);
+ }
+ Actions.CodeCompleteOrdinaryName(
+ getCurScope(),
+ CurParsedObjCImpl ? Sema::PCC_ObjCImplementation : Sema::PCC_Namespace);
+ return nullptr;
+ case tok::kw_import: {
+ Sema::ModuleImportState IS = Sema::ModuleImportState::NotACXX20Module;
+ if (getLangOpts().CPlusPlusModules) {
+ llvm_unreachable("not expecting a c++20 import here");
+ ProhibitAttributes(Attrs);
+ }
+ SingleDecl = ParseModuleImport(SourceLocation(), IS);
+ } break;
+ case tok::kw_export:
+ if (getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS) {
+ ProhibitAttributes(Attrs);
+ SingleDecl = ParseExportDeclaration();
+ break;
+ }
+ // This must be 'export template'. Parse it so we can diagnose our lack
+ // of support.
+ [[fallthrough]];
+ case tok::kw_using:
+ case tok::kw_namespace:
+ case tok::kw_typedef:
+ case tok::kw_template:
+ case tok::kw_static_assert:
+ case tok::kw__Static_assert:
+ // A function definition cannot start with any of these keywords.
+ {
+ SourceLocation DeclEnd;
+ return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
+ DeclSpecAttrs);
+ }
+
+ case tok::kw_cbuffer:
+ case tok::kw_tbuffer:
+ if (getLangOpts().HLSL) {
+ SourceLocation DeclEnd;
+ return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
+ DeclSpecAttrs);
+ }
+ goto dont_know;
+
+ case tok::kw_static:
+ // Parse (then ignore) 'static' prior to a template instantiation. This is
+ // a GCC extension that we intentionally do not support.
+ if (getLangOpts().CPlusPlus && NextToken().is(tok::kw_template)) {
+ Diag(ConsumeToken(), diag::warn_static_inline_explicit_inst_ignored)
+ << 0;
+ SourceLocation DeclEnd;
+ return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
+ DeclSpecAttrs);
+ }
+ goto dont_know;
+
+ case tok::kw_inline:
+ if (getLangOpts().CPlusPlus) {
+ tok::TokenKind NextKind = NextToken().getKind();
+
+ // Inline namespaces. Allowed as an extension even in C++03.
+ if (NextKind == tok::kw_namespace) {
+ SourceLocation DeclEnd;
+ return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
+ DeclSpecAttrs);
+ }
+
+ // Parse (then ignore) 'inline' prior to a template instantiation. This is
+ // a GCC extension that we intentionally do not support.
+ if (NextKind == tok::kw_template) {
+ Diag(ConsumeToken(), diag::warn_static_inline_explicit_inst_ignored)
+ << 1;
+ SourceLocation DeclEnd;
+ return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
+ DeclSpecAttrs);
+ }
+ }
+ goto dont_know;
+
+ case tok::kw_extern:
+ if (getLangOpts().CPlusPlus && NextToken().is(tok::kw_template)) {
+ // Extern templates
+ SourceLocation ExternLoc = ConsumeToken();
+ SourceLocation TemplateLoc = ConsumeToken();
+ Diag(ExternLoc, getLangOpts().CPlusPlus11 ?
+ diag::warn_cxx98_compat_extern_template :
+ diag::ext_extern_template) << SourceRange(ExternLoc, TemplateLoc);
+ SourceLocation DeclEnd;
+ return Actions.ConvertDeclToDeclGroup(ParseExplicitInstantiation(
+ DeclaratorContext::File, ExternLoc, TemplateLoc, DeclEnd, Attrs));
+ }
+ goto dont_know;
+
+ case tok::kw___if_exists:
+ case tok::kw___if_not_exists:
+ ParseMicrosoftIfExistsExternalDeclaration();
+ return nullptr;
+
+ case tok::kw_module:
+ Diag(Tok, diag::err_unexpected_module_decl);
+ SkipUntil(tok::semi);
+ return nullptr;
+
+ default:
+ dont_know:
+ if (Tok.isEditorPlaceholder()) {
+ ConsumeToken();
+ return nullptr;
+ }
+ if (PP.isIncrementalProcessingEnabled() &&
+ !isDeclarationStatement(/*DisambiguatingWithExpression=*/true))
+ return ParseTopLevelStmtDecl();
+
+ // We can't tell whether this is a function-definition or declaration yet.
+ if (!SingleDecl)
+ return ParseDeclarationOrFunctionDefinition(Attrs, DeclSpecAttrs, DS);
+ }
+
+ // This routine returns a DeclGroup, if the thing we parsed only contains a
+ // single decl, convert it now.
+ return Actions.ConvertDeclToDeclGroup(SingleDecl);
+}
+
+/// Determine whether the current token, if it occurs after a
+/// declarator, continues a declaration or declaration list.
+bool Parser::isDeclarationAfterDeclarator() {
+ // Check for '= delete' or '= default'
+ if (getLangOpts().CPlusPlus && Tok.is(tok::equal)) {
+ const Token &KW = NextToken();
+ if (KW.is(tok::kw_default) || KW.is(tok::kw_delete))
+ return false;
+ }
+
+ return Tok.is(tok::equal) || // int X()= -> not a function def
+ Tok.is(tok::comma) || // int X(), -> not a function def
+ Tok.is(tok::semi) || // int X(); -> not a function def
+ Tok.is(tok::kw_asm) || // int X() __asm__ -> not a function def
+ Tok.is(tok::kw___attribute) || // int X() __attr__ -> not a function def
+ (getLangOpts().CPlusPlus &&
+ Tok.is(tok::l_paren)); // int X(0) -> not a function def [C++]
+}
+
+/// Determine whether the current token, if it occurs after a
+/// declarator, indicates the start of a function definition.
+bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) {
+ assert(Declarator.isFunctionDeclarator() && "Isn't a function declarator");
+ if (Tok.is(tok::l_brace)) // int X() {}
+ return true;
+
+ // Handle K&R C argument lists: int X(f) int f; {}
+ if (!getLangOpts().CPlusPlus &&
+ Declarator.getFunctionTypeInfo().isKNRPrototype())
+ return isDeclarationSpecifier(ImplicitTypenameContext::No);
+
+ if (getLangOpts().CPlusPlus && Tok.is(tok::equal)) {
+ const Token &KW = NextToken();
+ return KW.is(tok::kw_default) || KW.is(tok::kw_delete);
+ }
+
+ return Tok.is(tok::colon) || // X() : Base() {} (used for ctors)
+ Tok.is(tok::kw_try); // X() try { ... }
+}
+
+/// Parse either a function-definition or a declaration. We can't tell which
+/// we have until we read up to the compound-statement in function-definition.
+/// TemplateParams, if non-NULL, provides the template parameters when we're
+/// parsing a C++ template-declaration.
+///
+/// function-definition: [C99 6.9.1]
+/// decl-specs declarator declaration-list[opt] compound-statement
+/// [C90] function-definition: [C99 6.7.1] - implicit int result
+/// [C90] decl-specs[opt] declarator declaration-list[opt] compound-statement
+///
+/// declaration: [C99 6.7]
+/// declaration-specifiers init-declarator-list[opt] ';'
+/// [!C99] init-declarator-list ';' [TODO: warn in c99 mode]
+/// [OMP] threadprivate-directive
+/// [OMP] allocate-directive [TODO]
+///
+Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal(
+ ParsedAttributes &Attrs, ParsedAttributes &DeclSpecAttrs,
+ ParsingDeclSpec &DS, AccessSpecifier AS) {
+ // Because we assume that the DeclSpec has not yet been initialised, we simply
+ // overwrite the source range and attribute the provided leading declspec
+ // attributes.
+ assert(DS.getSourceRange().isInvalid() &&
+ "expected uninitialised source range");
+ DS.SetRangeStart(DeclSpecAttrs.Range.getBegin());
+ DS.SetRangeEnd(DeclSpecAttrs.Range.getEnd());
+ DS.takeAttributesFrom(DeclSpecAttrs);
+
+ MaybeParseMicrosoftAttributes(DS.getAttributes());
+ // Parse the common declaration-specifiers piece.
+ ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS,
+ DeclSpecContext::DSC_top_level);
+
+ // If we had a free-standing type definition with a missing semicolon, we
+ // may get this far before the problem becomes obvious.
+ if (DS.hasTagDefinition() && DiagnoseMissingSemiAfterTagDefinition(
+ DS, AS, DeclSpecContext::DSC_top_level))
+ return nullptr;
+
+ // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
+ // declaration-specifiers init-declarator-list[opt] ';'
+ if (Tok.is(tok::semi)) {
+ auto LengthOfTSTToken = [](DeclSpec::TST TKind) {
+ assert(DeclSpec::isDeclRep(TKind));
+ switch(TKind) {
+ case DeclSpec::TST_class:
+ return 5;
+ case DeclSpec::TST_struct:
+ return 6;
+ case DeclSpec::TST_union:
+ return 5;
+ case DeclSpec::TST_enum:
+ return 4;
+ case DeclSpec::TST_interface:
+ return 9;
+ default:
+ llvm_unreachable("we only expect to get the length of the class/struct/union/enum");
+ }
+
+ };
+ // Suggest correct location to fix '[[attrib]] struct' to 'struct [[attrib]]'
+ SourceLocation CorrectLocationForAttributes =
+ DeclSpec::isDeclRep(DS.getTypeSpecType())
+ ? DS.getTypeSpecTypeLoc().getLocWithOffset(
+ LengthOfTSTToken(DS.getTypeSpecType()))
+ : SourceLocation();
+ ProhibitAttributes(Attrs, CorrectLocationForAttributes);
+ ConsumeToken();
+ RecordDecl *AnonRecord = nullptr;
+ Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(
+ getCurScope(), AS_none, DS, ParsedAttributesView::none(), AnonRecord);
+ DS.complete(TheDecl);
+ if (AnonRecord) {
+ Decl* decls[] = {AnonRecord, TheDecl};
+ return Actions.BuildDeclaratorGroup(decls);
+ }
+ return Actions.ConvertDeclToDeclGroup(TheDecl);
+ }
+
+ // ObjC2 allows prefix attributes on class interfaces and protocols.
+ // FIXME: This still needs better diagnostics. We should only accept
+ // attributes here, no types, etc.
+ if (getLangOpts().ObjC && Tok.is(tok::at)) {
+ SourceLocation AtLoc = ConsumeToken(); // the "@"
+ if (!Tok.isObjCAtKeyword(tok::objc_interface) &&
+ !Tok.isObjCAtKeyword(tok::objc_protocol) &&
+ !Tok.isObjCAtKeyword(tok::objc_implementation)) {
+ Diag(Tok, diag::err_objc_unexpected_attr);
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+
+ DS.abort();
+ DS.takeAttributesFrom(Attrs);
+
+ const char *PrevSpec = nullptr;
+ unsigned DiagID;
+ if (DS.SetTypeSpecType(DeclSpec::TST_unspecified, AtLoc, PrevSpec, DiagID,
+ Actions.getASTContext().getPrintingPolicy()))
+ Diag(AtLoc, DiagID) << PrevSpec;
+
+ if (Tok.isObjCAtKeyword(tok::objc_protocol))
+ return ParseObjCAtProtocolDeclaration(AtLoc, DS.getAttributes());
+
+ if (Tok.isObjCAtKeyword(tok::objc_implementation))
+ return ParseObjCAtImplementationDeclaration(AtLoc, DS.getAttributes());
+
+ return Actions.ConvertDeclToDeclGroup(
+ ParseObjCAtInterfaceDeclaration(AtLoc, DS.getAttributes()));
+ }
+
+ // If the declspec consisted only of 'extern' and we have a string
+ // literal following it, this must be a C++ linkage specifier like
+ // 'extern "C"'.
+ if (getLangOpts().CPlusPlus && isTokenStringLiteral() &&
+ DS.getStorageClassSpec() == DeclSpec::SCS_extern &&
+ DS.getParsedSpecifiers() == DeclSpec::PQ_StorageClassSpecifier) {
+ ProhibitAttributes(Attrs);
+ Decl *TheDecl = ParseLinkage(DS, DeclaratorContext::File);
+ return Actions.ConvertDeclToDeclGroup(TheDecl);
+ }
+
+ return ParseDeclGroup(DS, DeclaratorContext::File, Attrs);
+}
+
+Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition(
+ ParsedAttributes &Attrs, ParsedAttributes &DeclSpecAttrs,
+ ParsingDeclSpec *DS, AccessSpecifier AS) {
+ if (DS) {
+ return ParseDeclOrFunctionDefInternal(Attrs, DeclSpecAttrs, *DS, AS);
+ } else {
+ ParsingDeclSpec PDS(*this);
+ // Must temporarily exit the objective-c container scope for
+ // parsing c constructs and re-enter objc container scope
+ // afterwards.
+ ObjCDeclContextSwitch ObjCDC(*this);
+
+ return ParseDeclOrFunctionDefInternal(Attrs, DeclSpecAttrs, PDS, AS);
+ }
+}
+
+/// ParseFunctionDefinition - We parsed and verified that the specified
+/// Declarator is well formed. If this is a K&R-style function, read the
+/// parameters declaration-list, then start the compound-statement.
+///
+/// function-definition: [C99 6.9.1]
+/// decl-specs declarator declaration-list[opt] compound-statement
+/// [C90] function-definition: [C99 6.7.1] - implicit int result
+/// [C90] decl-specs[opt] declarator declaration-list[opt] compound-statement
+/// [C++] function-definition: [C++ 8.4]
+/// decl-specifier-seq[opt] declarator ctor-initializer[opt]
+/// function-body
+/// [C++] function-definition: [C++ 8.4]
+/// decl-specifier-seq[opt] declarator function-try-block
+///
+Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
+ const ParsedTemplateInfo &TemplateInfo,
+ LateParsedAttrList *LateParsedAttrs) {
+ // Poison SEH identifiers so they are flagged as illegal in function bodies.
+ PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true);
+ const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
+ TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
+
+ // If this is C89 and the declspecs were completely missing, fudge in an
+ // implicit int. We do this here because this is the only place where
+ // declaration-specifiers are completely optional in the grammar.
+ if (getLangOpts().isImplicitIntRequired() && D.getDeclSpec().isEmpty()) {
+ Diag(D.getIdentifierLoc(), diag::warn_missing_type_specifier)
+ << D.getDeclSpec().getSourceRange();
+ const char *PrevSpec;
+ unsigned DiagID;
+ const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
+ D.getMutableDeclSpec().SetTypeSpecType(DeclSpec::TST_int,
+ D.getIdentifierLoc(),
+ PrevSpec, DiagID,
+ Policy);
+ D.SetRangeBegin(D.getDeclSpec().getSourceRange().getBegin());
+ }
+
+ // If this declaration was formed with a K&R-style identifier list for the
+ // arguments, parse declarations for all of the args next.
+ // int foo(a,b) int a; float b; {}
+ if (FTI.isKNRPrototype())
+ ParseKNRParamDeclarations(D);
+
+ // We should have either an opening brace or, in a C++ constructor,
+ // we may have a colon.
+ if (Tok.isNot(tok::l_brace) &&
+ (!getLangOpts().CPlusPlus ||
+ (Tok.isNot(tok::colon) && Tok.isNot(tok::kw_try) &&
+ Tok.isNot(tok::equal)))) {
+ Diag(Tok, diag::err_expected_fn_body);
+
+ // Skip over garbage, until we get to '{'. Don't eat the '{'.
+ SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch);
+
+ // If we didn't find the '{', bail out.
+ if (Tok.isNot(tok::l_brace))
+ return nullptr;
+ }
+
+ // Check to make sure that any normal attributes are allowed to be on
+ // a definition. Late parsed attributes are checked at the end.
+ if (Tok.isNot(tok::equal)) {
+ for (const ParsedAttr &AL : D.getAttributes())
+ if (AL.isKnownToGCC() && !AL.isStandardAttributeSyntax())
+ Diag(AL.getLoc(), diag::warn_attribute_on_function_definition) << AL;
+ }
+
+ // In delayed template parsing mode, for function template we consume the
+ // tokens and store them for late parsing at the end of the translation unit.
+ if (getLangOpts().DelayedTemplateParsing && Tok.isNot(tok::equal) &&
+ TemplateInfo.Kind == ParsedTemplateInfo::Template &&
+ Actions.canDelayFunctionBody(D)) {
+ MultiTemplateParamsArg TemplateParameterLists(*TemplateInfo.TemplateParams);
+
+ ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+ Scope *ParentScope = getCurScope()->getParent();
+
+ D.setFunctionDefinitionKind(FunctionDefinitionKind::Definition);
+ Decl *DP = Actions.HandleDeclarator(ParentScope, D,
+ TemplateParameterLists);
+ D.complete(DP);
+ D.getMutableDeclSpec().abort();
+
+ if (SkipFunctionBodies && (!DP || Actions.canSkipFunctionBody(DP)) &&
+ trySkippingFunctionBody()) {
+ BodyScope.Exit();
+ return Actions.ActOnSkippedFunctionBody(DP);
+ }
+
+ CachedTokens Toks;
+ LexTemplateFunctionForLateParsing(Toks);
+
+ if (DP) {
+ FunctionDecl *FnD = DP->getAsFunction();
+ Actions.CheckForFunctionRedefinition(FnD);
+ Actions.MarkAsLateParsedTemplate(FnD, DP, Toks);
+ }
+ return DP;
+ }
+ else if (CurParsedObjCImpl &&
+ !TemplateInfo.TemplateParams &&
+ (Tok.is(tok::l_brace) || Tok.is(tok::kw_try) ||
+ Tok.is(tok::colon)) &&
+ Actions.CurContext->isTranslationUnit()) {
+ ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+ Scope *ParentScope = getCurScope()->getParent();
+
+ D.setFunctionDefinitionKind(FunctionDefinitionKind::Definition);
+ Decl *FuncDecl = Actions.HandleDeclarator(ParentScope, D,
+ MultiTemplateParamsArg());
+ D.complete(FuncDecl);
+ D.getMutableDeclSpec().abort();
+ if (FuncDecl) {
+ // Consume the tokens and store them for later parsing.
+ StashAwayMethodOrFunctionBodyTokens(FuncDecl);
+ CurParsedObjCImpl->HasCFunction = true;
+ return FuncDecl;
+ }
+ // FIXME: Should we really fall through here?
+ }
+
+ // Enter a scope for the function body.
+ ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+
+ // Parse function body eagerly if it is either '= delete;' or '= default;' as
+ // ActOnStartOfFunctionDef needs to know whether the function is deleted.
+ Sema::FnBodyKind BodyKind = Sema::FnBodyKind::Other;
+ SourceLocation KWLoc;
+ if (TryConsumeToken(tok::equal)) {
+ assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='");
+
+ if (TryConsumeToken(tok::kw_delete, KWLoc)) {
+ Diag(KWLoc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_defaulted_deleted_function
+ : diag::ext_defaulted_deleted_function)
+ << 1 /* deleted */;
+ BodyKind = Sema::FnBodyKind::Delete;
+ } else if (TryConsumeToken(tok::kw_default, KWLoc)) {
+ Diag(KWLoc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_defaulted_deleted_function
+ : diag::ext_defaulted_deleted_function)
+ << 0 /* defaulted */;
+ BodyKind = Sema::FnBodyKind::Default;
+ } else {
+ llvm_unreachable("function definition after = not 'delete' or 'default'");
+ }
+
+ if (Tok.is(tok::comma)) {
+ Diag(KWLoc, diag::err_default_delete_in_multiple_declaration)
+ << (BodyKind == Sema::FnBodyKind::Delete);
+ SkipUntil(tok::semi);
+ } else if (ExpectAndConsume(tok::semi, diag::err_expected_after,
+ BodyKind == Sema::FnBodyKind::Delete
+ ? "delete"
+ : "default")) {
+ SkipUntil(tok::semi);
+ }
+ }
+
+ // Tell the actions module that we have entered a function definition with the
+ // specified Declarator for the function.
+ Sema::SkipBodyInfo SkipBody;
+ Decl *Res = Actions.ActOnStartOfFunctionDef(getCurScope(), D,
+ TemplateInfo.TemplateParams
+ ? *TemplateInfo.TemplateParams
+ : MultiTemplateParamsArg(),
+ &SkipBody, BodyKind);
+
+ if (SkipBody.ShouldSkip) {
+ // Do NOT enter SkipFunctionBody if we already consumed the tokens.
+ if (BodyKind == Sema::FnBodyKind::Other)
+ SkipFunctionBody();
+
+ return Res;
+ }
+
+ // Break out of the ParsingDeclarator context before we parse the body.
+ D.complete(Res);
+
+ // Break out of the ParsingDeclSpec context, too. This const_cast is
+ // safe because we're always the sole owner.
+ D.getMutableDeclSpec().abort();
+
+ if (BodyKind != Sema::FnBodyKind::Other) {
+ Actions.SetFunctionBodyKind(Res, KWLoc, BodyKind);
+ Stmt *GeneratedBody = Res ? Res->getBody() : nullptr;
+ Actions.ActOnFinishFunctionBody(Res, GeneratedBody, false);
+ return Res;
+ }
+
+ // With abbreviated function templates - we need to explicitly add depth to
+ // account for the implicit template parameter list induced by the template.
+ if (auto *Template = dyn_cast_or_null<FunctionTemplateDecl>(Res))
+ if (Template->isAbbreviated() &&
+ Template->getTemplateParameters()->getParam(0)->isImplicit())
+ // First template parameter is implicit - meaning no explicit template
+ // parameter list was specified.
+ CurTemplateDepthTracker.addDepth(1);
+
+ if (SkipFunctionBodies && (!Res || Actions.canSkipFunctionBody(Res)) &&
+ trySkippingFunctionBody()) {
+ BodyScope.Exit();
+ Actions.ActOnSkippedFunctionBody(Res);
+ return Actions.ActOnFinishFunctionBody(Res, nullptr, false);
+ }
+
+ if (Tok.is(tok::kw_try))
+ return ParseFunctionTryBlock(Res, BodyScope);
+
+ // If we have a colon, then we're probably parsing a C++
+ // ctor-initializer.
+ if (Tok.is(tok::colon)) {
+ ParseConstructorInitializer(Res);
+
+ // Recover from error.
+ if (!Tok.is(tok::l_brace)) {
+ BodyScope.Exit();
+ Actions.ActOnFinishFunctionBody(Res, nullptr);
+ return Res;
+ }
+ } else
+ Actions.ActOnDefaultCtorInitializers(Res);
+
+ // Late attributes are parsed in the same scope as the function body.
+ if (LateParsedAttrs)
+ ParseLexedAttributeList(*LateParsedAttrs, Res, false, true);
+
+ return ParseFunctionStatementBody(Res, BodyScope);
+}
+
+void Parser::SkipFunctionBody() {
+ if (Tok.is(tok::equal)) {
+ SkipUntil(tok::semi);
+ return;
+ }
+
+ bool IsFunctionTryBlock = Tok.is(tok::kw_try);
+ if (IsFunctionTryBlock)
+ ConsumeToken();
+
+ CachedTokens Skipped;
+ if (ConsumeAndStoreFunctionPrologue(Skipped))
+ SkipMalformedDecl();
+ else {
+ SkipUntil(tok::r_brace);
+ while (IsFunctionTryBlock && Tok.is(tok::kw_catch)) {
+ SkipUntil(tok::l_brace);
+ SkipUntil(tok::r_brace);
+ }
+ }
+}
+
+/// ParseKNRParamDeclarations - Parse 'declaration-list[opt]' which provides
+/// types for a function with a K&R-style identifier list for arguments.
+void Parser::ParseKNRParamDeclarations(Declarator &D) {
+ // We know that the top-level of this declarator is a function.
+ DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
+
+ // Enter function-declaration scope, limiting any declarators to the
+ // function prototype scope, including parameter declarators.
+ ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
+ Scope::FunctionDeclarationScope | Scope::DeclScope);
+
+ // Read all the argument declarations.
+ while (isDeclarationSpecifier(ImplicitTypenameContext::No)) {
+ SourceLocation DSStart = Tok.getLocation();
+
+ // Parse the common declaration-specifiers piece.
+ DeclSpec DS(AttrFactory);
+ ParseDeclarationSpecifiers(DS);
+
+ // C99 6.9.1p6: 'each declaration in the declaration list shall have at
+ // least one declarator'.
+ // NOTE: GCC just makes this an ext-warn. It's not clear what it does with
+ // the declarations though. It's trivial to ignore them, really hard to do
+ // anything else with them.
+ if (TryConsumeToken(tok::semi)) {
+ Diag(DSStart, diag::err_declaration_does_not_declare_param);
+ continue;
+ }
+
+ // C99 6.9.1p6: Declarations shall contain no storage-class specifiers other
+ // than register.
+ if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified &&
+ DS.getStorageClassSpec() != DeclSpec::SCS_register) {
+ Diag(DS.getStorageClassSpecLoc(),
+ diag::err_invalid_storage_class_in_func_decl);
+ DS.ClearStorageClassSpecs();
+ }
+ if (DS.getThreadStorageClassSpec() != DeclSpec::TSCS_unspecified) {
+ Diag(DS.getThreadStorageClassSpecLoc(),
+ diag::err_invalid_storage_class_in_func_decl);
+ DS.ClearStorageClassSpecs();
+ }
+
+ // Parse the first declarator attached to this declspec.
+ Declarator ParmDeclarator(DS, ParsedAttributesView::none(),
+ DeclaratorContext::KNRTypeList);
+ ParseDeclarator(ParmDeclarator);
+
+ // Handle the full declarator list.
+ while (true) {
+ // If attributes are present, parse them.
+ MaybeParseGNUAttributes(ParmDeclarator);
+
+ // Ask the actions module to compute the type for this declarator.
+ Decl *Param =
+ Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator);
+
+ if (Param &&
+ // A missing identifier has already been diagnosed.
+ ParmDeclarator.getIdentifier()) {
+
+ // Scan the argument list looking for the correct param to apply this
+ // type.
+ for (unsigned i = 0; ; ++i) {
+ // C99 6.9.1p6: those declarators shall declare only identifiers from
+ // the identifier list.
+ if (i == FTI.NumParams) {
+ Diag(ParmDeclarator.getIdentifierLoc(), diag::err_no_matching_param)
+ << ParmDeclarator.getIdentifier();
+ break;
+ }
+
+ if (FTI.Params[i].Ident == ParmDeclarator.getIdentifier()) {
+ // Reject redefinitions of parameters.
+ if (FTI.Params[i].Param) {
+ Diag(ParmDeclarator.getIdentifierLoc(),
+ diag::err_param_redefinition)
+ << ParmDeclarator.getIdentifier();
+ } else {
+ FTI.Params[i].Param = Param;
+ }
+ break;
+ }
+ }
+ }
+
+ // If we don't have a comma, it is either the end of the list (a ';') or
+ // an error, bail out.
+ if (Tok.isNot(tok::comma))
+ break;
+
+ ParmDeclarator.clear();
+
+ // Consume the comma.
+ ParmDeclarator.setCommaLoc(ConsumeToken());
+
+ // Parse the next declarator.
+ ParseDeclarator(ParmDeclarator);
+ }
+
+ // Consume ';' and continue parsing.
+ if (!ExpectAndConsumeSemi(diag::err_expected_semi_declaration))
+ continue;
+
+ // Otherwise recover by skipping to next semi or mandatory function body.
+ if (SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch))
+ break;
+ TryConsumeToken(tok::semi);
+ }
+
+ // The actions module must verify that all arguments were declared.
+ Actions.ActOnFinishKNRParamDeclarations(getCurScope(), D, Tok.getLocation());
+}
+
+
+/// ParseAsmStringLiteral - This is just a normal string-literal, but is not
+/// allowed to be a wide string, and is not subject to character translation.
+/// Unlike GCC, we also diagnose an empty string literal when parsing for an
+/// asm label as opposed to an asm statement, because such a construct does not
+/// behave well.
+///
+/// [GNU] asm-string-literal:
+/// string-literal
+///
+ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) {
+ if (!isTokenStringLiteral()) {
+ Diag(Tok, diag::err_expected_string_literal)
+ << /*Source='in...'*/0 << "'asm'";
+ return ExprError();
+ }
+
+ ExprResult AsmString(ParseStringLiteralExpression());
+ if (!AsmString.isInvalid()) {
+ const auto *SL = cast<StringLiteral>(AsmString.get());
+ if (!SL->isOrdinary()) {
+ Diag(Tok, diag::err_asm_operand_wide_string_literal)
+ << SL->isWide()
+ << SL->getSourceRange();
+ return ExprError();
+ }
+ if (ForAsmLabel && SL->getString().empty()) {
+ Diag(Tok, diag::err_asm_operand_wide_string_literal)
+ << 2 /* an empty */ << SL->getSourceRange();
+ return ExprError();
+ }
+ }
+ return AsmString;
+}
+
+/// ParseSimpleAsm
+///
+/// [GNU] simple-asm-expr:
+/// 'asm' '(' asm-string-literal ')'
+///
+ExprResult Parser::ParseSimpleAsm(bool ForAsmLabel, SourceLocation *EndLoc) {
+ assert(Tok.is(tok::kw_asm) && "Not an asm!");
+ SourceLocation Loc = ConsumeToken();
+
+ if (isGNUAsmQualifier(Tok)) {
+ // Remove from the end of 'asm' to the end of the asm qualifier.
+ SourceRange RemovalRange(PP.getLocForEndOfToken(Loc),
+ PP.getLocForEndOfToken(Tok.getLocation()));
+ Diag(Tok, diag::err_global_asm_qualifier_ignored)
+ << GNUAsmQualifiers::getQualifierName(getGNUAsmQualifier(Tok))
+ << FixItHint::CreateRemoval(RemovalRange);
+ ConsumeToken();
+ }
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected_lparen_after) << "asm";
+ return ExprError();
+ }
+
+ ExprResult Result(ParseAsmStringLiteral(ForAsmLabel));
+
+ if (!Result.isInvalid()) {
+ // Close the paren and get the location of the end bracket
+ T.consumeClose();
+ if (EndLoc)
+ *EndLoc = T.getCloseLocation();
+ } else if (SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch)) {
+ if (EndLoc)
+ *EndLoc = Tok.getLocation();
+ ConsumeParen();
+ }
+
+ return Result;
+}
+
+/// Get the TemplateIdAnnotation from the token and put it in the
+/// cleanup pool so that it gets destroyed when parsing the current top level
+/// declaration is finished.
+TemplateIdAnnotation *Parser::takeTemplateIdAnnotation(const Token &tok) {
+ assert(tok.is(tok::annot_template_id) && "Expected template-id token");
+ TemplateIdAnnotation *
+ Id = static_cast<TemplateIdAnnotation *>(tok.getAnnotationValue());
+ return Id;
+}
+
+void Parser::AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation) {
+ // Push the current token back into the token stream (or revert it if it is
+ // cached) and use an annotation scope token for current token.
+ if (PP.isBacktrackEnabled())
+ PP.RevertCachedTokens(1);
+ else
+ PP.EnterToken(Tok, /*IsReinject=*/true);
+ Tok.setKind(tok::annot_cxxscope);
+ Tok.setAnnotationValue(Actions.SaveNestedNameSpecifierAnnotation(SS));
+ Tok.setAnnotationRange(SS.getRange());
+
+ // In case the tokens were cached, have Preprocessor replace them
+ // with the annotation token. We don't need to do this if we've
+ // just reverted back to a prior state.
+ if (IsNewAnnotation)
+ PP.AnnotateCachedTokens(Tok);
+}
+
+/// Attempt to classify the name at the current token position. This may
+/// form a type, scope or primary expression annotation, or replace the token
+/// with a typo-corrected keyword. This is only appropriate when the current
+/// name must refer to an entity which has already been declared.
+///
+/// \param CCC Indicates how to perform typo-correction for this name. If NULL,
+/// no typo correction will be performed.
+/// \param AllowImplicitTypename Whether we are in a context where a dependent
+/// nested-name-specifier without typename is treated as a type (e.g.
+/// T::type).
+Parser::AnnotatedNameKind
+Parser::TryAnnotateName(CorrectionCandidateCallback *CCC,
+ ImplicitTypenameContext AllowImplicitTypename) {
+ assert(Tok.is(tok::identifier) || Tok.is(tok::annot_cxxscope));
+
+ const bool EnteringContext = false;
+ const bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope);
+
+ CXXScopeSpec SS;
+ if (getLangOpts().CPlusPlus &&
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ EnteringContext))
+ return ANK_Error;
+
+ if (Tok.isNot(tok::identifier) || SS.isInvalid()) {
+ if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation,
+ AllowImplicitTypename))
+ return ANK_Error;
+ return ANK_Unresolved;
+ }
+
+ IdentifierInfo *Name = Tok.getIdentifierInfo();
+ SourceLocation NameLoc = Tok.getLocation();
+
+ // FIXME: Move the tentative declaration logic into ClassifyName so we can
+ // typo-correct to tentatively-declared identifiers.
+ if (isTentativelyDeclared(Name) && SS.isEmpty()) {
+ // Identifier has been tentatively declared, and thus cannot be resolved as
+ // an expression. Fall back to annotating it as a type.
+ if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation,
+ AllowImplicitTypename))
+ return ANK_Error;
+ return Tok.is(tok::annot_typename) ? ANK_Success : ANK_TentativeDecl;
+ }
+
+ Token Next = NextToken();
+
+ // Look up and classify the identifier. We don't perform any typo-correction
+ // after a scope specifier, because in general we can't recover from typos
+ // there (eg, after correcting 'A::template B<X>::C' [sic], we would need to
+ // jump back into scope specifier parsing).
+ Sema::NameClassification Classification = Actions.ClassifyName(
+ getCurScope(), SS, Name, NameLoc, Next, SS.isEmpty() ? CCC : nullptr);
+
+ // If name lookup found nothing and we guessed that this was a template name,
+ // double-check before committing to that interpretation. C++20 requires that
+ // we interpret this as a template-id if it can be, but if it can't be, then
+ // this is an error recovery case.
+ if (Classification.getKind() == Sema::NC_UndeclaredTemplate &&
+ isTemplateArgumentList(1) == TPResult::False) {
+ // It's not a template-id; re-classify without the '<' as a hint.
+ Token FakeNext = Next;
+ FakeNext.setKind(tok::unknown);
+ Classification =
+ Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, FakeNext,
+ SS.isEmpty() ? CCC : nullptr);
+ }
+
+ switch (Classification.getKind()) {
+ case Sema::NC_Error:
+ return ANK_Error;
+
+ case Sema::NC_Keyword:
+ // The identifier was typo-corrected to a keyword.
+ Tok.setIdentifierInfo(Name);
+ Tok.setKind(Name->getTokenID());
+ PP.TypoCorrectToken(Tok);
+ if (SS.isNotEmpty())
+ AnnotateScopeToken(SS, !WasScopeAnnotation);
+ // We've "annotated" this as a keyword.
+ return ANK_Success;
+
+ case Sema::NC_Unknown:
+ // It's not something we know about. Leave it unannotated.
+ break;
+
+ case Sema::NC_Type: {
+ if (TryAltiVecVectorToken())
+ // vector has been found as a type id when altivec is enabled but
+ // this is followed by a declaration specifier so this is really the
+ // altivec vector token. Leave it unannotated.
+ break;
+ SourceLocation BeginLoc = NameLoc;
+ if (SS.isNotEmpty())
+ BeginLoc = SS.getBeginLoc();
+
+ /// An Objective-C object type followed by '<' is a specialization of
+ /// a parameterized class type or a protocol-qualified type.
+ ParsedType Ty = Classification.getType();
+ if (getLangOpts().ObjC && NextToken().is(tok::less) &&
+ (Ty.get()->isObjCObjectType() ||
+ Ty.get()->isObjCObjectPointerType())) {
+ // Consume the name.
+ SourceLocation IdentifierLoc = ConsumeToken();
+ SourceLocation NewEndLoc;
+ TypeResult NewType
+ = parseObjCTypeArgsAndProtocolQualifiers(IdentifierLoc, Ty,
+ /*consumeLastToken=*/false,
+ NewEndLoc);
+ if (NewType.isUsable())
+ Ty = NewType.get();
+ else if (Tok.is(tok::eof)) // Nothing to do here, bail out...
+ return ANK_Error;
+ }
+
+ Tok.setKind(tok::annot_typename);
+ setTypeAnnotation(Tok, Ty);
+ Tok.setAnnotationEndLoc(Tok.getLocation());
+ Tok.setLocation(BeginLoc);
+ PP.AnnotateCachedTokens(Tok);
+ return ANK_Success;
+ }
+
+ case Sema::NC_OverloadSet:
+ Tok.setKind(tok::annot_overload_set);
+ setExprAnnotation(Tok, Classification.getExpression());
+ Tok.setAnnotationEndLoc(NameLoc);
+ if (SS.isNotEmpty())
+ Tok.setLocation(SS.getBeginLoc());
+ PP.AnnotateCachedTokens(Tok);
+ return ANK_Success;
+
+ case Sema::NC_NonType:
+ if (TryAltiVecVectorToken())
+ // vector has been found as a non-type id when altivec is enabled but
+ // this is followed by a declaration specifier so this is really the
+ // altivec vector token. Leave it unannotated.
+ break;
+ Tok.setKind(tok::annot_non_type);
+ setNonTypeAnnotation(Tok, Classification.getNonTypeDecl());
+ Tok.setLocation(NameLoc);
+ Tok.setAnnotationEndLoc(NameLoc);
+ PP.AnnotateCachedTokens(Tok);
+ if (SS.isNotEmpty())
+ AnnotateScopeToken(SS, !WasScopeAnnotation);
+ return ANK_Success;
+
+ case Sema::NC_UndeclaredNonType:
+ case Sema::NC_DependentNonType:
+ Tok.setKind(Classification.getKind() == Sema::NC_UndeclaredNonType
+ ? tok::annot_non_type_undeclared
+ : tok::annot_non_type_dependent);
+ setIdentifierAnnotation(Tok, Name);
+ Tok.setLocation(NameLoc);
+ Tok.setAnnotationEndLoc(NameLoc);
+ PP.AnnotateCachedTokens(Tok);
+ if (SS.isNotEmpty())
+ AnnotateScopeToken(SS, !WasScopeAnnotation);
+ return ANK_Success;
+
+ case Sema::NC_TypeTemplate:
+ if (Next.isNot(tok::less)) {
+ // This may be a type template being used as a template template argument.
+ if (SS.isNotEmpty())
+ AnnotateScopeToken(SS, !WasScopeAnnotation);
+ return ANK_TemplateName;
+ }
+ [[fallthrough]];
+ case Sema::NC_VarTemplate:
+ case Sema::NC_FunctionTemplate:
+ case Sema::NC_UndeclaredTemplate: {
+ // We have a type, variable or function template followed by '<'.
+ ConsumeToken();
+ UnqualifiedId Id;
+ Id.setIdentifier(Name, NameLoc);
+ if (AnnotateTemplateIdToken(
+ TemplateTy::make(Classification.getTemplateName()),
+ Classification.getTemplateNameKind(), SS, SourceLocation(), Id))
+ return ANK_Error;
+ return ANK_Success;
+ }
+ case Sema::NC_Concept: {
+ UnqualifiedId Id;
+ Id.setIdentifier(Name, NameLoc);
+ if (Next.is(tok::less))
+ // We have a concept name followed by '<'. Consume the identifier token so
+ // we reach the '<' and annotate it.
+ ConsumeToken();
+ if (AnnotateTemplateIdToken(
+ TemplateTy::make(Classification.getTemplateName()),
+ Classification.getTemplateNameKind(), SS, SourceLocation(), Id,
+ /*AllowTypeAnnotation=*/false, /*TypeConstraint=*/true))
+ return ANK_Error;
+ return ANK_Success;
+ }
+ }
+
+ // Unable to classify the name, but maybe we can annotate a scope specifier.
+ if (SS.isNotEmpty())
+ AnnotateScopeToken(SS, !WasScopeAnnotation);
+ return ANK_Unresolved;
+}
+
+bool Parser::TryKeywordIdentFallback(bool DisableKeyword) {
+ assert(Tok.isNot(tok::identifier));
+ Diag(Tok, diag::ext_keyword_as_ident)
+ << PP.getSpelling(Tok)
+ << DisableKeyword;
+ if (DisableKeyword)
+ Tok.getIdentifierInfo()->revertTokenIDToIdentifier();
+ Tok.setKind(tok::identifier);
+ return true;
+}
+
+/// TryAnnotateTypeOrScopeToken - If the current token position is on a
+/// typename (possibly qualified in C++) or a C++ scope specifier not followed
+/// by a typename, TryAnnotateTypeOrScopeToken will replace one or more tokens
+/// with a single annotation token representing the typename or C++ scope
+/// respectively.
+/// This simplifies handling of C++ scope specifiers and allows efficient
+/// backtracking without the need to re-parse and resolve nested-names and
+/// typenames.
+/// It will mainly be called when we expect to treat identifiers as typenames
+/// (if they are typenames). For example, in C we do not expect identifiers
+/// inside expressions to be treated as typenames so it will not be called
+/// for expressions in C.
+/// The benefit for C/ObjC is that a typename will be annotated and
+/// Actions.getTypeName will not be needed to be called again (e.g. getTypeName
+/// will not be called twice, once to check whether we have a declaration
+/// specifier, and another one to get the actual type inside
+/// ParseDeclarationSpecifiers).
+///
+/// This returns true if an error occurred.
+///
+/// Note that this routine emits an error if you call it with ::new or ::delete
+/// as the current tokens, so only call it in contexts where these are invalid.
+bool Parser::TryAnnotateTypeOrScopeToken(
+ ImplicitTypenameContext AllowImplicitTypename) {
+ assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
+ Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) ||
+ Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) ||
+ Tok.is(tok::kw___super)) &&
+ "Cannot be a type or scope token!");
+
+ if (Tok.is(tok::kw_typename)) {
+ // MSVC lets you do stuff like:
+ // typename typedef T_::D D;
+ //
+ // We will consume the typedef token here and put it back after we have
+ // parsed the first identifier, transforming it into something more like:
+ // typename T_::D typedef D;
+ if (getLangOpts().MSVCCompat && NextToken().is(tok::kw_typedef)) {
+ Token TypedefToken;
+ PP.Lex(TypedefToken);
+ bool Result = TryAnnotateTypeOrScopeToken(AllowImplicitTypename);
+ PP.EnterToken(Tok, /*IsReinject=*/true);
+ Tok = TypedefToken;
+ if (!Result)
+ Diag(Tok.getLocation(), diag::warn_expected_qualified_after_typename);
+ return Result;
+ }
+
+ // Parse a C++ typename-specifier, e.g., "typename T::type".
+ //
+ // typename-specifier:
+ // 'typename' '::' [opt] nested-name-specifier identifier
+ // 'typename' '::' [opt] nested-name-specifier template [opt]
+ // simple-template-id
+ SourceLocation TypenameLoc = ConsumeToken();
+ CXXScopeSpec SS;
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false, nullptr,
+ /*IsTypename*/ true))
+ return true;
+ if (SS.isEmpty()) {
+ if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) ||
+ Tok.is(tok::annot_decltype)) {
+ // Attempt to recover by skipping the invalid 'typename'
+ if (Tok.is(tok::annot_decltype) ||
+ (!TryAnnotateTypeOrScopeToken(AllowImplicitTypename) &&
+ Tok.isAnnotation())) {
+ unsigned DiagID = diag::err_expected_qualified_after_typename;
+ // MS compatibility: MSVC permits using known types with typename.
+ // e.g. "typedef typename T* pointer_type"
+ if (getLangOpts().MicrosoftExt)
+ DiagID = diag::warn_expected_qualified_after_typename;
+ Diag(Tok.getLocation(), DiagID);
+ return false;
+ }
+ }
+ if (Tok.isEditorPlaceholder())
+ return true;
+
+ Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename);
+ return true;
+ }
+
+ TypeResult Ty;
+ if (Tok.is(tok::identifier)) {
+ // FIXME: check whether the next token is '<', first!
+ Ty = Actions.ActOnTypenameType(getCurScope(), TypenameLoc, SS,
+ *Tok.getIdentifierInfo(),
+ Tok.getLocation());
+ } else if (Tok.is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ if (!TemplateId->mightBeType()) {
+ Diag(Tok, diag::err_typename_refers_to_non_type_template)
+ << Tok.getAnnotationRange();
+ return true;
+ }
+
+ ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
+ TemplateId->NumArgs);
+
+ Ty = TemplateId->isInvalid()
+ ? TypeError()
+ : Actions.ActOnTypenameType(
+ getCurScope(), TypenameLoc, SS, TemplateId->TemplateKWLoc,
+ TemplateId->Template, TemplateId->Name,
+ TemplateId->TemplateNameLoc, TemplateId->LAngleLoc,
+ TemplateArgsPtr, TemplateId->RAngleLoc);
+ } else {
+ Diag(Tok, diag::err_expected_type_name_after_typename)
+ << SS.getRange();
+ return true;
+ }
+
+ SourceLocation EndLoc = Tok.getLastLoc();
+ Tok.setKind(tok::annot_typename);
+ setTypeAnnotation(Tok, Ty);
+ Tok.setAnnotationEndLoc(EndLoc);
+ Tok.setLocation(TypenameLoc);
+ PP.AnnotateCachedTokens(Tok);
+ return false;
+ }
+
+ // Remembers whether the token was originally a scope annotation.
+ bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope);
+
+ CXXScopeSpec SS;
+ if (getLangOpts().CPlusPlus)
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext*/ false))
+ return true;
+
+ return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation,
+ AllowImplicitTypename);
+}
+
+/// Try to annotate a type or scope token, having already parsed an
+/// optional scope specifier. \p IsNewScope should be \c true unless the scope
+/// specifier was extracted from an existing tok::annot_cxxscope annotation.
+bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(
+ CXXScopeSpec &SS, bool IsNewScope,
+ ImplicitTypenameContext AllowImplicitTypename) {
+ if (Tok.is(tok::identifier)) {
+ // Determine whether the identifier is a type name.
+ if (ParsedType Ty = Actions.getTypeName(
+ *Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS,
+ false, NextToken().is(tok::period), nullptr,
+ /*IsCtorOrDtorName=*/false,
+ /*NonTrivialTypeSourceInfo=*/true,
+ /*IsClassTemplateDeductionContext=*/true, AllowImplicitTypename)) {
+ SourceLocation BeginLoc = Tok.getLocation();
+ if (SS.isNotEmpty()) // it was a C++ qualified type name.
+ BeginLoc = SS.getBeginLoc();
+
+ /// An Objective-C object type followed by '<' is a specialization of
+ /// a parameterized class type or a protocol-qualified type.
+ if (getLangOpts().ObjC && NextToken().is(tok::less) &&
+ (Ty.get()->isObjCObjectType() ||
+ Ty.get()->isObjCObjectPointerType())) {
+ // Consume the name.
+ SourceLocation IdentifierLoc = ConsumeToken();
+ SourceLocation NewEndLoc;
+ TypeResult NewType
+ = parseObjCTypeArgsAndProtocolQualifiers(IdentifierLoc, Ty,
+ /*consumeLastToken=*/false,
+ NewEndLoc);
+ if (NewType.isUsable())
+ Ty = NewType.get();
+ else if (Tok.is(tok::eof)) // Nothing to do here, bail out...
+ return false;
+ }
+
+ // This is a typename. Replace the current token in-place with an
+ // annotation type token.
+ Tok.setKind(tok::annot_typename);
+ setTypeAnnotation(Tok, Ty);
+ Tok.setAnnotationEndLoc(Tok.getLocation());
+ Tok.setLocation(BeginLoc);
+
+ // In case the tokens were cached, have Preprocessor replace
+ // them with the annotation token.
+ PP.AnnotateCachedTokens(Tok);
+ return false;
+ }
+
+ if (!getLangOpts().CPlusPlus) {
+ // If we're in C, the only place we can have :: tokens is C2x
+ // attribute which is parsed elsewhere. If the identifier is not a type,
+ // then it can't be scope either, just early exit.
+ return false;
+ }
+
+ // If this is a template-id, annotate with a template-id or type token.
+ // FIXME: This appears to be dead code. We already have formed template-id
+ // tokens when parsing the scope specifier; this can never form a new one.
+ if (NextToken().is(tok::less)) {
+ TemplateTy Template;
+ UnqualifiedId TemplateName;
+ TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ bool MemberOfUnknownSpecialization;
+ if (TemplateNameKind TNK = Actions.isTemplateName(
+ getCurScope(), SS,
+ /*hasTemplateKeyword=*/false, TemplateName,
+ /*ObjectType=*/nullptr, /*EnteringContext*/false, Template,
+ MemberOfUnknownSpecialization)) {
+ // Only annotate an undeclared template name as a template-id if the
+ // following tokens have the form of a template argument list.
+ if (TNK != TNK_Undeclared_template ||
+ isTemplateArgumentList(1) != TPResult::False) {
+ // Consume the identifier.
+ ConsumeToken();
+ if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
+ TemplateName)) {
+ // If an unrecoverable error occurred, we need to return true here,
+ // because the token stream is in a damaged state. We may not
+ // return a valid identifier.
+ return true;
+ }
+ }
+ }
+ }
+
+ // The current token, which is either an identifier or a
+ // template-id, is not part of the annotation. Fall through to
+ // push that token back into the stream and complete the C++ scope
+ // specifier annotation.
+ }
+
+ if (Tok.is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ if (TemplateId->Kind == TNK_Type_template) {
+ // A template-id that refers to a type was parsed into a
+ // template-id annotation in a context where we weren't allowed
+ // to produce a type annotation token. Update the template-id
+ // annotation token to a type annotation token now.
+ AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
+ return false;
+ }
+ }
+
+ if (SS.isEmpty())
+ return false;
+
+ // A C++ scope specifier that isn't followed by a typename.
+ AnnotateScopeToken(SS, IsNewScope);
+ return false;
+}
+
+/// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only
+/// annotates C++ scope specifiers and template-ids. This returns
+/// true if there was an error that could not be recovered from.
+///
+/// Note that this routine emits an error if you call it with ::new or ::delete
+/// as the current tokens, so only call it in contexts where these are invalid.
+bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) {
+ assert(getLangOpts().CPlusPlus &&
+ "Call sites of this function should be guarded by checking for C++");
+ assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!");
+
+ CXXScopeSpec SS;
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ EnteringContext))
+ return true;
+ if (SS.isEmpty())
+ return false;
+
+ AnnotateScopeToken(SS, true);
+ return false;
+}
+
+bool Parser::isTokenEqualOrEqualTypo() {
+ tok::TokenKind Kind = Tok.getKind();
+ switch (Kind) {
+ default:
+ return false;
+ case tok::ampequal: // &=
+ case tok::starequal: // *=
+ case tok::plusequal: // +=
+ case tok::minusequal: // -=
+ case tok::exclaimequal: // !=
+ case tok::slashequal: // /=
+ case tok::percentequal: // %=
+ case tok::lessequal: // <=
+ case tok::lesslessequal: // <<=
+ case tok::greaterequal: // >=
+ case tok::greatergreaterequal: // >>=
+ case tok::caretequal: // ^=
+ case tok::pipeequal: // |=
+ case tok::equalequal: // ==
+ Diag(Tok, diag::err_invalid_token_after_declarator_suggest_equal)
+ << Kind
+ << FixItHint::CreateReplacement(SourceRange(Tok.getLocation()), "=");
+ [[fallthrough]];
+ case tok::equal:
+ return true;
+ }
+}
+
+SourceLocation Parser::handleUnexpectedCodeCompletionToken() {
+ assert(Tok.is(tok::code_completion));
+ PrevTokLocation = Tok.getLocation();
+
+ for (Scope *S = getCurScope(); S; S = S->getParent()) {
+ if (S->isFunctionScope()) {
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(),
+ Sema::PCC_RecoveryInFunction);
+ return PrevTokLocation;
+ }
+
+ if (S->isClassScope()) {
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Class);
+ return PrevTokLocation;
+ }
+ }
+
+ cutOffParsing();
+ Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Namespace);
+ return PrevTokLocation;
+}
+
+// Code-completion pass-through functions
+
+void Parser::CodeCompleteDirective(bool InConditional) {
+ Actions.CodeCompletePreprocessorDirective(InConditional);
+}
+
+void Parser::CodeCompleteInConditionalExclusion() {
+ Actions.CodeCompleteInPreprocessorConditionalExclusion(getCurScope());
+}
+
+void Parser::CodeCompleteMacroName(bool IsDefinition) {
+ Actions.CodeCompletePreprocessorMacroName(IsDefinition);
+}
+
+void Parser::CodeCompletePreprocessorExpression() {
+ Actions.CodeCompletePreprocessorExpression();
+}
+
+void Parser::CodeCompleteMacroArgument(IdentifierInfo *Macro,
+ MacroInfo *MacroInfo,
+ unsigned ArgumentIndex) {
+ Actions.CodeCompletePreprocessorMacroArgument(getCurScope(), Macro, MacroInfo,
+ ArgumentIndex);
+}
+
+void Parser::CodeCompleteIncludedFile(llvm::StringRef Dir, bool IsAngled) {
+ Actions.CodeCompleteIncludedFile(Dir, IsAngled);
+}
+
+void Parser::CodeCompleteNaturalLanguage() {
+ Actions.CodeCompleteNaturalLanguage();
+}
+
+bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) {
+ assert((Tok.is(tok::kw___if_exists) || Tok.is(tok::kw___if_not_exists)) &&
+ "Expected '__if_exists' or '__if_not_exists'");
+ Result.IsIfExists = Tok.is(tok::kw___if_exists);
+ Result.KeywordLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected_lparen_after)
+ << (Result.IsIfExists? "__if_exists" : "__if_not_exists");
+ return true;
+ }
+
+ // Parse nested-name-specifier.
+ if (getLangOpts().CPlusPlus)
+ ParseOptionalCXXScopeSpecifier(Result.SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false);
+
+ // Check nested-name specifier.
+ if (Result.SS.isInvalid()) {
+ T.skipToEnd();
+ return true;
+ }
+
+ // Parse the unqualified-id.
+ SourceLocation TemplateKWLoc; // FIXME: parsed, but unused.
+ if (ParseUnqualifiedId(Result.SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false, /*EnteringContext*/ false,
+ /*AllowDestructorName*/ true,
+ /*AllowConstructorName*/ true,
+ /*AllowDeductionGuide*/ false, &TemplateKWLoc,
+ Result.Name)) {
+ T.skipToEnd();
+ return true;
+ }
+
+ if (T.consumeClose())
+ return true;
+
+ // Check if the symbol exists.
+ switch (Actions.CheckMicrosoftIfExistsSymbol(getCurScope(), Result.KeywordLoc,
+ Result.IsIfExists, Result.SS,
+ Result.Name)) {
+ case Sema::IER_Exists:
+ Result.Behavior = Result.IsIfExists ? IEB_Parse : IEB_Skip;
+ break;
+
+ case Sema::IER_DoesNotExist:
+ Result.Behavior = !Result.IsIfExists ? IEB_Parse : IEB_Skip;
+ break;
+
+ case Sema::IER_Dependent:
+ Result.Behavior = IEB_Dependent;
+ break;
+
+ case Sema::IER_Error:
+ return true;
+ }
+
+ return false;
+}
+
+void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
+ IfExistsCondition Result;
+ if (ParseMicrosoftIfExistsCondition(Result))
+ return;
+
+ BalancedDelimiterTracker Braces(*this, tok::l_brace);
+ if (Braces.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_brace;
+ return;
+ }
+
+ switch (Result.Behavior) {
+ case IEB_Parse:
+ // Parse declarations below.
+ break;
+
+ case IEB_Dependent:
+ llvm_unreachable("Cannot have a dependent external declaration");
+
+ case IEB_Skip:
+ Braces.skipToEnd();
+ return;
+ }
+
+ // Parse the declarations.
+ // FIXME: Support module import within __if_exists?
+ while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+ ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
+ DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs, EmptyDeclSpecAttrs);
+ if (Result && !getCurScope()->getParent())
+ Actions.getASTConsumer().HandleTopLevelDecl(Result.get());
+ }
+ Braces.consumeClose();
+}
+
+/// Parse a declaration beginning with the 'module' keyword or C++20
+/// context-sensitive keyword (optionally preceded by 'export').
+///
+/// module-declaration: [Modules TS + P0629R0]
+/// 'export'[opt] 'module' module-name attribute-specifier-seq[opt] ';'
+///
+/// global-module-fragment: [C++2a]
+/// 'module' ';' top-level-declaration-seq[opt]
+/// module-declaration: [C++2a]
+/// 'export'[opt] 'module' module-name module-partition[opt]
+/// attribute-specifier-seq[opt] ';'
+/// private-module-fragment: [C++2a]
+/// 'module' ':' 'private' ';' top-level-declaration-seq[opt]
+Parser::DeclGroupPtrTy
+Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
+ SourceLocation StartLoc = Tok.getLocation();
+
+ Sema::ModuleDeclKind MDK = TryConsumeToken(tok::kw_export)
+ ? Sema::ModuleDeclKind::Interface
+ : Sema::ModuleDeclKind::Implementation;
+
+ assert(
+ (Tok.is(tok::kw_module) ||
+ (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_module)) &&
+ "not a module declaration");
+ SourceLocation ModuleLoc = ConsumeToken();
+
+ // Attributes appear after the module name, not before.
+ // FIXME: Suggest moving the attributes later with a fixit.
+ DiagnoseAndSkipCXX11Attributes();
+
+ // Parse a global-module-fragment, if present.
+ if (getLangOpts().CPlusPlusModules && Tok.is(tok::semi)) {
+ SourceLocation SemiLoc = ConsumeToken();
+ if (ImportState != Sema::ModuleImportState::FirstDecl) {
+ Diag(StartLoc, diag::err_global_module_introducer_not_at_start)
+ << SourceRange(StartLoc, SemiLoc);
+ return nullptr;
+ }
+ if (MDK == Sema::ModuleDeclKind::Interface) {
+ Diag(StartLoc, diag::err_module_fragment_exported)
+ << /*global*/0 << FixItHint::CreateRemoval(StartLoc);
+ }
+ ImportState = Sema::ModuleImportState::GlobalFragment;
+ return Actions.ActOnGlobalModuleFragmentDecl(ModuleLoc);
+ }
+
+ // Parse a private-module-fragment, if present.
+ if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon) &&
+ NextToken().is(tok::kw_private)) {
+ if (MDK == Sema::ModuleDeclKind::Interface) {
+ Diag(StartLoc, diag::err_module_fragment_exported)
+ << /*private*/1 << FixItHint::CreateRemoval(StartLoc);
+ }
+ ConsumeToken();
+ SourceLocation PrivateLoc = ConsumeToken();
+ DiagnoseAndSkipCXX11Attributes();
+ ExpectAndConsumeSemi(diag::err_private_module_fragment_expected_semi);
+ ImportState = ImportState == Sema::ModuleImportState::ImportAllowed
+ ? Sema::ModuleImportState::PrivateFragmentImportAllowed
+ : Sema::ModuleImportState::PrivateFragmentImportFinished;
+ return Actions.ActOnPrivateModuleFragmentDecl(ModuleLoc, PrivateLoc);
+ }
+
+ SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
+ if (ParseModuleName(ModuleLoc, Path, /*IsImport*/ false))
+ return nullptr;
+
+ // Parse the optional module-partition.
+ SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
+ if (Tok.is(tok::colon)) {
+ SourceLocation ColonLoc = ConsumeToken();
+ if (!getLangOpts().CPlusPlusModules)
+ Diag(ColonLoc, diag::err_unsupported_module_partition)
+ << SourceRange(ColonLoc, Partition.back().second);
+ // Recover by ignoring the partition name.
+ else if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/ false))
+ return nullptr;
+ }
+
+ // We don't support any module attributes yet; just parse them and diagnose.
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+ ProhibitCXX11Attributes(Attrs, diag::err_attribute_not_module_attr,
+ /*DiagnoseEmptyAttrs=*/false,
+ /*WarnOnUnknownAttrs=*/true);
+
+ ExpectAndConsumeSemi(diag::err_module_expected_semi);
+
+ return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
+ ImportState);
+}
+
+/// Parse a module import declaration. This is essentially the same for
+/// Objective-C and C++20 except for the leading '@' (in ObjC) and the
+/// trailing optional attributes (in C++).
+///
+/// [ObjC] @import declaration:
+/// '@' 'import' module-name ';'
+/// [ModTS] module-import-declaration:
+/// 'import' module-name attribute-specifier-seq[opt] ';'
+/// [C++20] module-import-declaration:
+/// 'export'[opt] 'import' module-name
+/// attribute-specifier-seq[opt] ';'
+/// 'export'[opt] 'import' module-partition
+/// attribute-specifier-seq[opt] ';'
+/// 'export'[opt] 'import' header-name
+/// attribute-specifier-seq[opt] ';'
+Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
+ Sema::ModuleImportState &ImportState) {
+ SourceLocation StartLoc = AtLoc.isInvalid() ? Tok.getLocation() : AtLoc;
+
+ SourceLocation ExportLoc;
+ TryConsumeToken(tok::kw_export, ExportLoc);
+
+ assert((AtLoc.isInvalid() ? Tok.isOneOf(tok::kw_import, tok::identifier)
+ : Tok.isObjCAtKeyword(tok::objc_import)) &&
+ "Improper start to module import");
+ bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import);
+ SourceLocation ImportLoc = ConsumeToken();
+
+ // For C++20 modules, we can have "name" or ":Partition name" as valid input.
+ SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
+ bool IsPartition = false;
+ Module *HeaderUnit = nullptr;
+ if (Tok.is(tok::header_name)) {
+ // This is a header import that the preprocessor decided we should skip
+ // because it was malformed in some way. Parse and ignore it; it's already
+ // been diagnosed.
+ ConsumeToken();
+ } else if (Tok.is(tok::annot_header_unit)) {
+ // This is a header import that the preprocessor mapped to a module import.
+ HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
+ ConsumeAnnotationToken();
+ } else if (Tok.is(tok::colon)) {
+ SourceLocation ColonLoc = ConsumeToken();
+ if (!getLangOpts().CPlusPlusModules)
+ Diag(ColonLoc, diag::err_unsupported_module_partition)
+ << SourceRange(ColonLoc, Path.back().second);
+ // Recover by leaving partition empty.
+ else if (ParseModuleName(ColonLoc, Path, /*IsImport*/ true))
+ return nullptr;
+ else
+ IsPartition = true;
+ } else {
+ if (ParseModuleName(ImportLoc, Path, /*IsImport*/ true))
+ return nullptr;
+ }
+
+ ParsedAttributes Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+ // We don't support any module import attributes yet.
+ ProhibitCXX11Attributes(Attrs, diag::err_attribute_not_import_attr,
+ /*DiagnoseEmptyAttrs=*/false,
+ /*WarnOnUnknownAttrs=*/true);
+
+ if (PP.hadModuleLoaderFatalFailure()) {
+ // With a fatal failure in the module loader, we abort parsing.
+ cutOffParsing();
+ return nullptr;
+ }
+
+ // Diagnose mis-imports.
+ bool SeenError = true;
+ switch (ImportState) {
+ case Sema::ModuleImportState::ImportAllowed:
+ SeenError = false;
+ break;
+ case Sema::ModuleImportState::FirstDecl:
+ case Sema::ModuleImportState::NotACXX20Module:
+ // We can only import a partition within a module purview.
+ if (IsPartition)
+ Diag(ImportLoc, diag::err_partition_import_outside_module);
+ else
+ SeenError = false;
+ break;
+ case Sema::ModuleImportState::GlobalFragment:
+ case Sema::ModuleImportState::PrivateFragmentImportAllowed:
+ // We can only have pre-processor directives in the global module fragment
+ // which allows pp-import, but not of a partition (since the global module
+ // does not have partitions).
+ // We cannot import a partition into a private module fragment, since
+ // [module.private.frag]/1 disallows private module fragments in a multi-
+ // TU module.
+ if (IsPartition || (HeaderUnit && HeaderUnit->Kind !=
+ Module::ModuleKind::ModuleHeaderUnit))
+ Diag(ImportLoc, diag::err_import_in_wrong_fragment)
+ << IsPartition
+ << (ImportState == Sema::ModuleImportState::GlobalFragment ? 0 : 1);
+ else
+ SeenError = false;
+ break;
+ case Sema::ModuleImportState::ImportFinished:
+ case Sema::ModuleImportState::PrivateFragmentImportFinished:
+ if (getLangOpts().CPlusPlusModules)
+ Diag(ImportLoc, diag::err_import_not_allowed_here);
+ else
+ SeenError = false;
+ break;
+ }
+ if (SeenError) {
+ ExpectAndConsumeSemi(diag::err_module_expected_semi);
+ return nullptr;
+ }
+
+ DeclResult Import;
+ if (HeaderUnit)
+ Import =
+ Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
+ else if (!Path.empty())
+ Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path,
+ IsPartition);
+ ExpectAndConsumeSemi(diag::err_module_expected_semi);
+ if (Import.isInvalid())
+ return nullptr;
+
+ // Using '@import' in framework headers requires modules to be enabled so that
+ // the header is parseable. Emit a warning to make the user aware.
+ if (IsObjCAtImport && AtLoc.isValid()) {
+ auto &SrcMgr = PP.getSourceManager();
+ auto FE = SrcMgr.getFileEntryRefForID(SrcMgr.getFileID(AtLoc));
+ if (FE && llvm::sys::path::parent_path(FE->getDir().getName())
+ .endswith(".framework"))
+ Diags.Report(AtLoc, diag::warn_atimport_in_framework_header);
+ }
+
+ return Import.get();
+}
+
+/// Parse a C++ Modules TS / Objective-C module name (both forms use the same
+/// grammar).
+///
+/// module-name:
+/// module-name-qualifier[opt] identifier
+/// module-name-qualifier:
+/// module-name-qualifier[opt] identifier '.'
+bool Parser::ParseModuleName(
+ SourceLocation UseLoc,
+ SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
+ bool IsImport) {
+ // Parse the module path.
+ while (true) {
+ if (!Tok.is(tok::identifier)) {
+ if (Tok.is(tok::code_completion)) {
+ cutOffParsing();
+ Actions.CodeCompleteModuleImport(UseLoc, Path);
+ return true;
+ }
+
+ Diag(Tok, diag::err_module_expected_ident) << IsImport;
+ SkipUntil(tok::semi);
+ return true;
+ }
+
+ // Record this part of the module path.
+ Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
+ ConsumeToken();
+
+ if (Tok.isNot(tok::period))
+ return false;
+
+ ConsumeToken();
+ }
+}
+
+/// Try recover parser when module annotation appears where it must not
+/// be found.
+/// \returns false if the recover was successful and parsing may be continued, or
+/// true if parser must bail out to top level and handle the token there.
+bool Parser::parseMisplacedModuleImport() {
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::annot_module_end:
+ // If we recovered from a misplaced module begin, we expect to hit a
+ // misplaced module end too. Stay in the current context when this
+ // happens.
+ if (MisplacedModuleBeginCount) {
+ --MisplacedModuleBeginCount;
+ Actions.ActOnModuleEnd(Tok.getLocation(),
+ reinterpret_cast<Module *>(
+ Tok.getAnnotationValue()));
+ ConsumeAnnotationToken();
+ continue;
+ }
+ // Inform caller that recovery failed, the error must be handled at upper
+ // level. This will generate the desired "missing '}' at end of module"
+ // diagnostics on the way out.
+ return true;
+ case tok::annot_module_begin:
+ // Recover by entering the module (Sema will diagnose).
+ Actions.ActOnModuleBegin(Tok.getLocation(),
+ reinterpret_cast<Module *>(
+ Tok.getAnnotationValue()));
+ ConsumeAnnotationToken();
+ ++MisplacedModuleBeginCount;
+ continue;
+ case tok::annot_module_include:
+ // Module import found where it should not be, for instance, inside a
+ // namespace. Recover by importing the module.
+ Actions.ActOnModuleInclude(Tok.getLocation(),
+ reinterpret_cast<Module *>(
+ Tok.getAnnotationValue()));
+ ConsumeAnnotationToken();
+ // If there is another module import, process it.
+ continue;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool BalancedDelimiterTracker::diagnoseOverflow() {
+ P.Diag(P.Tok, diag::err_bracket_depth_exceeded)
+ << P.getLangOpts().BracketDepth;
+ P.Diag(P.Tok, diag::note_bracket_depth);
+ P.cutOffParsing();
+ return true;
+}
+
+bool BalancedDelimiterTracker::expectAndConsume(unsigned DiagID,
+ const char *Msg,
+ tok::TokenKind SkipToTok) {
+ LOpen = P.Tok.getLocation();
+ if (P.ExpectAndConsume(Kind, DiagID, Msg)) {
+ if (SkipToTok != tok::unknown)
+ P.SkipUntil(SkipToTok, Parser::StopAtSemi);
+ return true;
+ }
+
+ if (getDepth() < P.getLangOpts().BracketDepth)
+ return false;
+
+ return diagnoseOverflow();
+}
+
+bool BalancedDelimiterTracker::diagnoseMissingClose() {
+ assert(!P.Tok.is(Close) && "Should have consumed closing delimiter");
+
+ if (P.Tok.is(tok::annot_module_end))
+ P.Diag(P.Tok, diag::err_missing_before_module_end) << Close;
+ else
+ P.Diag(P.Tok, diag::err_expected) << Close;
+ P.Diag(LOpen, diag::note_matching) << Kind;
+
+ // If we're not already at some kind of closing bracket, skip to our closing
+ // token.
+ if (P.Tok.isNot(tok::r_paren) && P.Tok.isNot(tok::r_brace) &&
+ P.Tok.isNot(tok::r_square) &&
+ P.SkipUntil(Close, FinalToken,
+ Parser::StopAtSemi | Parser::StopBeforeMatch) &&
+ P.Tok.is(Close))
+ LClose = P.ConsumeAnyToken();
+ return true;
+}
+
+void BalancedDelimiterTracker::skipToEnd() {
+ P.SkipUntil(Close, Parser::StopBeforeMatch);
+ consumeClose();
+}
diff --git a/contrib/libs/clang16/lib/Parse/ya.make b/contrib/libs/clang16/lib/Parse/ya.make
new file mode 100644
index 0000000000..90f77e4945
--- /dev/null
+++ b/contrib/libs/clang16/lib/Parse/ya.make
@@ -0,0 +1,51 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/clang16
+ contrib/libs/clang16/include
+ contrib/libs/clang16/lib/AST
+ contrib/libs/clang16/lib/Basic
+ contrib/libs/clang16/lib/Lex
+ contrib/libs/clang16/lib/Sema
+ contrib/libs/llvm16
+ contrib/libs/llvm16/lib/Frontend/OpenMP
+ contrib/libs/llvm16/lib/MC
+ contrib/libs/llvm16/lib/MC/MCParser
+ contrib/libs/llvm16/lib/Support
+ contrib/libs/llvm16/lib/TargetParser
+)
+
+ADDINCL(
+ contrib/libs/clang16/lib/Parse
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ ParseAST.cpp
+ ParseCXXInlineMethods.cpp
+ ParseDecl.cpp
+ ParseDeclCXX.cpp
+ ParseExpr.cpp
+ ParseExprCXX.cpp
+ ParseHLSL.cpp
+ ParseInit.cpp
+ ParseObjc.cpp
+ ParseOpenMP.cpp
+ ParsePragma.cpp
+ ParseStmt.cpp
+ ParseStmtAsm.cpp
+ ParseTemplate.cpp
+ ParseTentative.cpp
+ Parser.cpp
+)
+
+END()