diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/lib/Parse | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/lib/Parse')
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, ¶mAttrs); + + // 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() |