Mercurial > hg > CbC > CbC_llvm
changeset 156:8b836e0217ba
recovery ParseDecl.cpp
author | anatofuz |
---|---|
date | Thu, 12 Mar 2020 14:57:53 +0900 |
parents | 70c77e05b61e |
children | 5fe240291530 |
files | clang/lib/Parse/ParseDecl.cpp |
diffstat | 1 files changed, 2053 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/clang/lib/Parse/ParseDecl.cpp Wed Mar 11 19:59:35 2020 +0900 +++ b/clang/lib/Parse/ParseDecl.cpp Thu Mar 12 14:57:53 2020 +0900 @@ -1,8 +1,2055 @@ - +//===--- 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/Parse/Parser.h" +#include "clang/Parse/RAIIObjectsForParser.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/PrettyDeclStackTrace.h" +#include "clang/Basic/AddressSpaces.h" +#include "clang/Basic/Attributes.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Parse/ParseDiagnostic.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/ParsedTemplate.h" +#include "clang/Sema/Scope.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" + +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, 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; +} + +/// 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, + SourceLocation *endLoc, + LateParsedAttrList *LateAttrs, + Declarator *D) { + assert(Tok.is(tok::kw___attribute) && "Not a GNU attribute list!"); + + 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; + 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); + if (endLoc) + *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; + } + } + } +} + +/// 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 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, + SourceLocation *EndLoc, + 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); + + // 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 = attributeHasIdentifierArg(*AttrName) || + attributeHasVariadicIdentifierArg(*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(); + + // Parse the non-empty comma-separated list of expressions. + 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 (AttributeIsTypeArgAttr) { + TypeResult T = ParseTypeName(); + if (T.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return 0; + } + if (T.isUsable()) + TheParsedType = T.get(); + break; // FIXME: Multiple type arguments are not implemented. + } else if (Tok.is(tok::identifier) && + attributeHasVariadicIdentifierArg(*AttrName)) { + 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)); + } + + 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_TypeTagForDatatype) { + ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, + ScopeName, ScopeLoc, Syntax); + return; + } else if (attributeIsTypeArgAttr(*AttrName)) { + ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, EndLoc, 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. + llvm::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_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) { + // If the attribute isn't known, we will not attempt to parse any + // arguments. + if (!hasAttribute(AttrSyntax::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 (!Attrs.empty() && Attrs.begin()->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, + SourceLocation *End) { + assert(getLangOpts().DeclSpecKeyword && "__declspec keyword is not enabled"); + assert(Tok.is(tok::kw___declspec) && "Not a declspec!"); + + 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; + + // 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(); + if (End) + *End = T.getCloseLocation(); + } +} + +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::ParseOpenCLQualifiers(ParsedAttributes &Attrs) { + IdentifierInfo *AttrName = Tok.getIdentifierInfo(); + SourceLocation AttrNameLoc = Tok.getLocation(); + 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__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->getCharByteWidth() != 1) { + 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, llvm::array_lengthof(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); +} + +// Late Parsed Attributes: +// See other examples of late parsing in lib/Parse/ParseCXXInlineMethods + +void Parser::LateParsedDeclaration::ParseLexedAttributes() {} + +void Parser::LateParsedClass::ParseLexedAttributes() { + Self->ParseLexedAttributes(*Class); +} + +void Parser::LateParsedAttribute::ParseLexedAttributes() { + Self->ParseLexedAttribute(*this, true, false); +} + +/// Wrapper class which calls ParseLexedAttribute, after setting up the +/// scope appropriately. +void Parser::ParseLexedAttributes(ParsingClass &Class) { + // Deal with templates + // FIXME: Test cases to make sure this does the right thing for templates. + bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; + ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, + HasTemplateScope); + if (HasTemplateScope) + Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); + + // Set or update the scope flags. + bool AlreadyHasClassScope = Class.TopLevelClass; + unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope; + ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope); + ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope); + + // Enter the scope of nested classes + if (!AlreadyHasClassScope) + Actions.ActOnStartDelayedMemberDeclarations(getCurScope(), + Class.TagOrTemplate); + if (!Class.LateParsedDeclarations.empty()) { + for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i){ + Class.LateParsedDeclarations[i]->ParseLexedAttributes(); + } + } + + if (!AlreadyHasClassScope) + Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(), + Class.TagOrTemplate); +} + +/// 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); + SourceLocation endLoc; + + 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. + bool HasTemplateScope = EnterScope && D->isTemplateDecl(); + ParseScope TempScope(this, Scope::TemplateParamScope, HasTemplateScope); + if (HasTemplateScope) + Actions.ActOnReenterTemplateScope(Actions.CurScope, D); + + // If the Decl is on a function, add function parameters to the scope. + bool HasFunScope = EnterScope && D->isFunctionOrFunctionTemplate(); + ParseScope FnScope( + this, Scope::FnScope | Scope::DeclScope | Scope::CompoundStmtScope, + HasFunScope); + if (HasFunScope) + Actions.ActOnReenterFunctionContext(Actions.CurScope, D); + + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc, + nullptr, SourceLocation(), ParsedAttr::AS_GNU, + nullptr); + + if (HasFunScope) { + Actions.ActOnExitFunctionContext(); + FnScope.Exit(); // Pop scope, and remove Decls from IdResolver + } + if (HasTemplateScope) { + TempScope.Exit(); + } + } else { + // If there are multiple decls, then the decl cannot be within the + // function scope. + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc, + 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::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(ParsedAttributesWithRange &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(ParsedAttributesWithRange &Attrs, + unsigned DiagID) { + for (const ParsedAttr &AL : Attrs) { + if (!AL.isCXX11Attribute() && !AL.isC2xAttribute()) + continue; + if (AL.getKind() == ParsedAttr::UnknownAttribute) + Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored) << AL; + else { + Diag(AL.getLoc(), DiagID) << AL; + AL.setInvalid(); + } + } +} + +// 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(ParsedAttributesWithRange &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, + ParsedAttributesWithRange &attrs, + 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(attrs); + SingleDecl = ParseDeclarationStartingWithTemplate(Context, DeclEnd, attrs); + 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(attrs); + SourceLocation InlineLoc = ConsumeToken(); + return ParseNamespace(Context, DeclEnd, InlineLoc); + } + return ParseSimpleDeclaration(Context, DeclEnd, attrs, true, nullptr, + DeclSpecStart); + case tok::kw_namespace: + ProhibitAttributes(attrs); + return ParseNamespace(Context, DeclEnd); + case tok::kw_using: + return ParseUsingDirectiveOrDeclaration(Context, ParsedTemplateInfo(), + DeclEnd, attrs); + case tok::kw_static_assert: + case tok::kw__Static_assert: + ProhibitAttributes(attrs); + SingleDecl = ParseStaticAssertDeclaration(DeclEnd); + break; + default: + return ParseSimpleDeclaration(Context, DeclEnd, attrs, 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, + ParsedAttributesWithRange &Attrs, bool RequireSemi, ForRangeInit *FRI, + SourceLocation *DeclSpecStart) { + // Parse the common declaration-specifiers piece. + ParsingDeclSpec DS(*this); + + 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(Attrs); + DeclEnd = Tok.getLocation(); + if (RequireSemi) ConsumeToken(); + RecordDecl *AnonRecord = nullptr; + Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, + DS, AnonRecord); + DS.complete(TheDecl); + if (AnonRecord) { + Decl* decls[] = {AnonRecord, TheDecl}; + return Actions.BuildDeclaratorGroup(decls); + } + return Actions.ConvertDeclToDeclGroup(TheDecl); + } + + if (DeclSpecStart) + DS.SetRangeStart(*DeclSpecStart); + + DS.takeAttributesFrom(Attrs); + return ParseDeclGroup(DS, Context, &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::MemberContext && + getLangOpts().CPlusPlus11 && NextToken().is(tok::l_square); + + case tok::colon: // Might be a typo for '::' or an unnamed bit-field. + return Context == DeclaratorContext::MemberContext || + 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::MemberContext || + (getLangOpts().CPlusPlus && + Context == DeclaratorContext::FileContext); + + 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, + SourceLocation *DeclEnd, + ForRangeInit *FRI) { + // Parse the first declarator. + ParsingDeclarator D(*this, DS, Context); + ParseDeclarator(D); + + // Bail out if the first declarator didn't seem well-formed. + if (!D.hasName() && !D.mayOmitIdentifier()) { + SkipMalformedDecl(); + return nullptr; + } + + 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 (D.isFunctionDeclarator() && #ifndef noCbC !ProtoParsing && -#endif +#endif // 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. @@ -94,7 +2141,8 @@ #ifndef noCbC ExpectSemi = ExpectSemi && !ProtoParsing; #endif - + + // If we don't have a comma, it is either the end of the list (a ';') or an // error, bail out. SourceLocation CommaLoc; @@ -1748,6 +3796,7 @@ isInvalid = DS.SetTypeSpecType(DeclSpec::TST_void, Loc, PrevSpec, DiagID, Policy); break; + #ifndef noCbC case tok::kw___code: { LangOptions* LOP; @@ -1757,6 +3806,7 @@ break; } #endif + case tok::kw_char: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char, Loc, PrevSpec, DiagID, Policy);