Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
llvm-project/clang/lib/Parse/ParseDecl.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
7952 lines (7095 sloc)
292 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//===--- 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/EnterExpressionEvaluationContext.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; | |
// Move declspec attributes to ParsedAttributes | |
if (Attrs) { | |
llvm::SmallVector<ParsedAttr *, 1> ToBeMoved; | |
for (ParsedAttr &AL : DS.getAttributes()) { | |
if (AL.isDeclspecAttribute()) | |
ToBeMoved.push_back(&AL); | |
} | |
for (ParsedAttr *AL : ToBeMoved) | |
Attrs->takeOneFrom(DS.getAttributes(), AL); | |
} | |
// 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::Form::GNU()); | |
continue; | |
} | |
// Handle "parameterized" attributes | |
if (!LateAttrs || !isAttributeLateParsed(*AttrName)) { | |
ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, &EndLoc, nullptr, | |
SourceLocation(), ParsedAttr::Form::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::Form Form) { | |
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(), Form); | |
else | |
Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, Parens.getCloseLocation()), | |
ScopeName, ScopeLoc, nullptr, 0, Form); | |
} | |
unsigned Parser::ParseAttributeArgsCommon( | |
IdentifierInfo *AttrName, SourceLocation AttrNameLoc, | |
ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, | |
SourceLocation ScopeLoc, ParsedAttr::Form Form) { | |
// 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, Form.getSyntax()); | |
// 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, Form); | |
} else { | |
Attrs.addNew(AttrName, SourceRange(AttrLoc, RParen), ScopeName, ScopeLoc, | |
ArgExprs.data(), ArgExprs.size(), Form); | |
} | |
} | |
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::Form Form, Declarator *D) { | |
assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); | |
ParsedAttr::Kind AttrKind = | |
ParsedAttr::getParsedKind(AttrName, ScopeName, Form.getSyntax()); | |
if (AttrKind == ParsedAttr::AT_Availability) { | |
ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, | |
ScopeLoc, Form); | |
return; | |
} else if (AttrKind == ParsedAttr::AT_ExternalSourceSymbol) { | |
ParseExternalSourceSymbolAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, | |
ScopeName, ScopeLoc, Form); | |
return; | |
} else if (AttrKind == ParsedAttr::AT_ObjCBridgeRelated) { | |
ParseObjCBridgeRelatedAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, | |
ScopeName, ScopeLoc, Form); | |
return; | |
} else if (AttrKind == ParsedAttr::AT_SwiftNewType) { | |
ParseSwiftNewTypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, | |
ScopeLoc, Form); | |
return; | |
} else if (AttrKind == ParsedAttr::AT_TypeTagForDatatype) { | |
ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, | |
ScopeName, ScopeLoc, Form); | |
return; | |
} else if (attributeIsTypeArgAttr(*AttrName)) { | |
ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName, | |
ScopeLoc, Form); | |
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, Form); | |
} | |
unsigned Parser::ParseClangAttributeArgs( | |
IdentifierInfo *AttrName, SourceLocation AttrNameLoc, | |
ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, | |
SourceLocation ScopeLoc, ParsedAttr::Form Form) { | |
assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); | |
ParsedAttr::Kind AttrKind = | |
ParsedAttr::getParsedKind(AttrName, ScopeName, Form.getSyntax()); | |
switch (AttrKind) { | |
default: | |
return ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc, | |
ScopeName, ScopeLoc, Form); | |
case ParsedAttr::AT_ExternalSourceSymbol: | |
ParseExternalSourceSymbolAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, | |
ScopeName, ScopeLoc, Form); | |
break; | |
case ParsedAttr::AT_Availability: | |
ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, | |
ScopeLoc, Form); | |
break; | |
case ParsedAttr::AT_ObjCBridgeRelated: | |
ParseObjCBridgeRelatedAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, | |
ScopeName, ScopeLoc, Form); | |
break; | |
case ParsedAttr::AT_SwiftNewType: | |
ParseSwiftNewTypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, | |
ScopeLoc, Form); | |
break; | |
case ParsedAttr::AT_TypeTagForDatatype: | |
ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, | |
ScopeName, ScopeLoc, Form); | |
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::Form::Declspec()); | |
T.skipToEnd(); | |
return !HasInvalidAccessor; | |
} | |
unsigned NumArgs = | |
ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, nullptr, nullptr, | |
SourceLocation(), ParsedAttr::Form::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::Form::Declspec()); | |
} | |
T.consumeClose(); | |
EndLoc = T.getCloseLocation(); | |
} | |
Attrs.Range = SourceRange(StartLoc, EndLoc); | |
} | |
void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) { | |
// Treat these like attributes | |
while (true) { | |
auto Kind = Tok.getKind(); | |
switch (Kind) { | |
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, | |
Kind); | |
break; | |
} | |
default: | |
return; | |
} | |
} | |
} | |
void Parser::ParseWebAssemblyFuncrefTypeAttribute(ParsedAttributes &attrs) { | |
assert(Tok.is(tok::kw___funcref)); | |
SourceLocation StartLoc = Tok.getLocation(); | |
if (!getTargetInfo().getTriple().isWasm()) { | |
ConsumeToken(); | |
Diag(StartLoc, diag::err_wasm_funcref_not_wasm); | |
return; | |
} | |
IdentifierInfo *AttrName = Tok.getIdentifierInfo(); | |
SourceLocation AttrNameLoc = ConsumeToken(); | |
attrs.addNew(AttrName, AttrNameLoc, /*ScopeName=*/nullptr, | |
/*ScopeLoc=*/SourceLocation{}, /*Args=*/nullptr, /*numArgs=*/0, | |
tok::kw___funcref); | |
} | |
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, | |
tok::kw___pascal); | |
} | |
} | |
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, | |
tok::kw___kernel); | |
} | |
} | |
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, | |
tok::kw___noinline__); | |
} | |
} | |
void Parser::ParseOpenCLQualifiers(ParsedAttributes &Attrs) { | |
IdentifierInfo *AttrName = Tok.getIdentifierInfo(); | |
SourceLocation AttrNameLoc = Tok.getLocation(); | |
Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0, | |
Tok.getKind()); | |
} | |
bool Parser::isHLSLQualifier(const Token &Tok) const { | |
return Tok.is(tok::kw_groupshared); | |
} | |
void Parser::ParseHLSLQualifiers(ParsedAttributes &Attrs) { | |
IdentifierInfo *AttrName = Tok.getIdentifierInfo(); | |
auto Kind = Tok.getKind(); | |
SourceLocation AttrNameLoc = ConsumeToken(); | |
Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0, Kind); | |
} | |
void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) { | |
// Treat these like attributes, even though they're type specifiers. | |
while (true) { | |
auto Kind = Tok.getKind(); | |
switch (Kind) { | |
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, | |
Kind); | |
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::Form Form) { | |
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(), Form, | |
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> | |
/// 'USR' '=' <string> | |
/// 'generated_declaration' | |
void Parser::ParseExternalSourceSymbolAttribute( | |
IdentifierInfo &ExternalSourceSymbol, SourceLocation Loc, | |
ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, | |
SourceLocation ScopeLoc, ParsedAttr::Form Form) { | |
// 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"); | |
Ident_USR = PP.getIdentifierInfo("USR"); | |
} | |
ExprResult Language; | |
bool HasLanguage = false; | |
ExprResult DefinedInExpr; | |
bool HasDefinedIn = false; | |
IdentifierLoc *GeneratedDeclaration = nullptr; | |
ExprResult USR; | |
bool HasUSR = false; | |
// 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 && | |
Keyword != Ident_USR) { | |
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, | |
HadUSR = HasUSR; | |
if (Keyword == Ident_language) | |
HasLanguage = true; | |
else if (Keyword == Ident_USR) | |
HasUSR = 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 | USR*/ ( | |
Keyword == Ident_language | |
? 0 | |
: (Keyword == Ident_defined_in ? 1 : 2)); | |
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 if (Keyword == Ident_USR) { | |
if (HadUSR) { | |
Diag(KeywordLoc, diag::err_external_source_symbol_duplicate_clause) | |
<< Keyword; | |
ParseStringLiteralExpression(); | |
continue; | |
} | |
USR = 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, | |
USR.get()}; | |
Attrs.addNew(&ExternalSourceSymbol, SourceRange(Loc, T.getCloseLocation()), | |
ScopeName, ScopeLoc, Args, std::size(Args), Form); | |
} | |
/// 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::Form Form) { | |
// 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, | |
Form); | |
} | |
void Parser::ParseSwiftNewTypeAttribute( | |
IdentifierInfo &AttrName, SourceLocation AttrNameLoc, | |
ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, | |
SourceLocation ScopeLoc, ParsedAttr::Form Form) { | |
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), Form); | |
} | |
void Parser::ParseTypeTagForDatatypeAttribute( | |
IdentifierInfo &AttrName, SourceLocation AttrNameLoc, | |
ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, | |
SourceLocation ScopeLoc, ParsedAttr::Form Form) { | |
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, Form); | |
} | |
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) || Tok.isRegularKeywordAttribute()); | |
// Consume the attributes. | |
auto Keyword = | |
Tok.isRegularKeywordAttribute() ? Tok.getIdentifierInfo() : nullptr; | |
SourceLocation Loc = Tok.getLocation(); | |
ParseCXX11Attributes(Attrs); | |
CharSourceRange AttrRange(SourceRange(Loc, Attrs.Range.getEnd()), true); | |
// FIXME: use err_attributes_misplaced | |
(Keyword ? Diag(Loc, diag::err_keyword_not_allowed) << Keyword | |
: Diag(Loc, diag::err_attributes_not_allowed)) | |
<< FixItHint::CreateInsertionFromRange(CorrectLocation, AttrRange) | |
<< FixItHint::CreateRemoval(AttrRange); | |
} | |
void Parser::DiagnoseProhibitedAttributes( | |
const ParsedAttributesView &Attrs, const SourceLocation CorrectLocation) { | |
auto *FirstAttr = Attrs.empty() ? nullptr : &Attrs.front(); | |
if (CorrectLocation.isValid()) { | |
CharSourceRange AttrRange(Attrs.Range, true); | |
(FirstAttr && FirstAttr->isRegularKeywordAttribute() | |
? Diag(CorrectLocation, diag::err_keyword_misplaced) << FirstAttr | |
: Diag(CorrectLocation, diag::err_attributes_misplaced)) | |
<< FixItHint::CreateInsertionFromRange(CorrectLocation, AttrRange) | |
<< FixItHint::CreateRemoval(AttrRange); | |
} else { | |
const SourceRange &Range = Attrs.Range; | |
(FirstAttr && FirstAttr->isRegularKeywordAttribute() | |
? Diag(Range.getBegin(), diag::err_keyword_not_allowed) << FirstAttr | |
: Diag(Range.getBegin(), diag::err_attributes_not_allowed)) | |
<< Range; | |
} | |
} | |
void Parser::ProhibitCXX11Attributes(ParsedAttributes &Attrs, | |
unsigned AttrDiagID, | |
unsigned KeywordDiagID, | |
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(), AttrDiagID) << Attrs.Range; | |
return; | |
} | |
} | |
} | |
for (const ParsedAttr &AL : Attrs) { | |
if (AL.isRegularKeywordAttribute()) { | |
Diag(AL.getLoc(), KeywordDiagID) << AL; | |
AL.setInvalid(); | |
continue; | |
} | |
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(), AttrDiagID) << AL; | |
AL.setInvalid(); | |
} | |
} | |
} | |
void Parser::DiagnoseCXX11AttributeExtension(ParsedAttributes &Attrs) { | |
for (const ParsedAttr &PA : Attrs) { | |
if (PA.isCXX11Attribute() || PA.isC2xAttribute() || | |
PA.isRegularKeywordAttribute()) | |
Diag(PA.getLoc(), diag::ext_cxx11_attr_placement) | |
<< PA << PA.isRegularKeywordAttribute() << 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 Tok.isRegularKeywordAttribute(); | |
} | |
default: | |
return Tok.isRegularKeywordAttribute(); | |
} | |
} | |
/// 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: | |
case tok::annot_repl_input_end: | |
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) || | |
Tok.is(tok::kw_namespace)) { | |
// If there is an invalid declaration specifier or a namespace | |
// definition 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. Note that | |
// isDeclarationSpecifier already covers 'inline namespace', since | |
// 'inline' can be a declaration specifier. | |
} 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)) | |
SkipMalformedDecl(); | |
} | |
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. | |
/// | |
/// [C11] type-id | |
/// [C11] constant-expression | |
/// [C++0x] type-id ...[opt] | |
/// [C++0x] assignment-expression ...[opt] | |
ExprResult Parser::ParseAlignArgument(StringRef KWName, SourceLocation Start, | |
SourceLocation &EllipsisLoc, bool &IsType, | |
ParsedType &TypeResult) { | |
ExprResult ER; | |
if (isTypeIdInParens()) { | |
SourceLocation TypeLoc = Tok.getLocation(); | |
ParsedType Ty = ParseTypeName().get(); | |
SourceRange TypeRange(Start, Tok.getLocation()); | |
if (Actions.ActOnAlignasTypeArgument(KWName, Ty, TypeLoc, TypeRange)) | |
return ExprError(); | |
TypeResult = Ty; | |
IsType = true; | |
} else { | |
ER = ParseConstantExpression(); | |
IsType = false; | |
} | |
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!"); | |
Token KWTok = Tok; | |
IdentifierInfo *KWName = KWTok.getIdentifierInfo(); | |
auto Kind = KWTok.getKind(); | |
SourceLocation KWLoc = ConsumeToken(); | |
BalancedDelimiterTracker T(*this, tok::l_paren); | |
if (T.expectAndConsume()) | |
return; | |
bool IsType; | |
ParsedType TypeResult; | |
SourceLocation EllipsisLoc; | |
ExprResult ArgExpr = | |
ParseAlignArgument(PP.getSpelling(KWTok), T.getOpenLocation(), | |
EllipsisLoc, IsType, TypeResult); | |
if (ArgExpr.isInvalid()) { | |
T.skipToEnd(); | |
return; | |
} | |
T.consumeClose(); | |
if (EndLoc) | |
*EndLoc = T.getCloseLocation(); | |
if (IsType) { | |
Attrs.addNewTypeAttr(KWName, KWLoc, nullptr, KWLoc, TypeResult, Kind, | |
EllipsisLoc); | |
} else { | |
ArgsVector ArgExprs; | |
ArgExprs.push_back(ArgExpr.get()); | |
Attrs.addNew(KWName, KWLoc, nullptr, KWLoc, ArgExprs.data(), 1, Kind, | |
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: | |
if (Tok.isRegularKeywordAttribute()) | |
goto Attribute; | |
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() && | |
!PA.isRegularKeywordAttribute()) | |
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.isRegularKeywordAttribute(); | |
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 (!isAllowedCXX11AttributeSpecifier()) | |
goto DoneWithDeclSpec; | |
Attribute: | |
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; | |
if (TemplateInfo.TemplateParams) | |
SS.setTemplateParamLists(*TemplateInfo.TemplateParams); | |
Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(), | |
Tok.getAnnotationRange(), | |
SS); | |
// We are looking for a qualified typename. | |
Token Next = NextToken(); | |