blob: 08519410ddb0bde7452fd7b1d3be789abb58ea5e [file] [log] [blame]
//===--- ParseDecl.cpp - Declaration Parsing ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Declaration portions of the Parser interfaces.
//
//===----------------------------------------------------------------------===//
#include "clang/Parse/Parser.h"
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Basic/OpenCL.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/PrettyDeclStackTrace.h"
#include "RAIIObjectsForParser.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang;
//===----------------------------------------------------------------------===//
// C99 6.7: Declarations.
//===----------------------------------------------------------------------===//
/// ParseTypeName
/// type-name: [C99 6.7.6]
/// specifier-qualifier-list abstract-declarator[opt]
///
/// Called type-id in C++.
TypeResult Parser::ParseTypeName(SourceRange *Range,
Declarator::TheContext Context,
AccessSpecifier AS,
Decl **OwnedType) {
DeclSpecContext DSC = getDeclSpecContextFromDeclaratorContext(Context);
// Parse the common declaration-specifiers piece.
DeclSpec DS(AttrFactory);
ParseSpecifierQualifierList(DS, AS, DSC);
if (OwnedType)
*OwnedType = DS.isTypeSpecOwned() ? DS.getRepAsDecl() : 0;
// Parse the abstract-declarator, if present.
Declarator DeclaratorInfo(DS, Context);
ParseDeclarator(DeclaratorInfo);
if (Range)
*Range = DeclaratorInfo.getSourceRange();
if (DeclaratorInfo.isInvalidType())
return true;
return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
}
/// isAttributeLateParsed - Return true if the attribute has arguments that
/// require late parsing.
static bool isAttributeLateParsed(const IdentifierInfo &II) {
return llvm::StringSwitch<bool>(II.getName())
#include "clang/Parse/AttrLateParsed.inc"
.Default(false);
}
/// 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
///
/// FIXME: The GCC grammar/code for this construct implies we need two
/// token lookahead. Comment from gcc: "If they start with an identifier
/// which is followed by a comma or close parenthesis, then the arguments
/// start with that identifier; otherwise they are an expression list."
///
/// GCC does not require the ',' between attribs in an attribute-list.
///
/// At the moment, I am not doing 2 token lookahead. I am also unaware of
/// any attributes that don't work (based on my limited testing). Most
/// attributes are very simple in practice. Until we find a bug, I don't see
/// a pressing need to implement the 2 token lookahead.
void Parser::ParseGNUAttributes(ParsedAttributes &attrs,
SourceLocation *endLoc,
LateParsedAttrList *LateAttrs) {
assert(Tok.is(tok::kw___attribute) && "Not a GNU attribute list!");
while (Tok.is(tok::kw___attribute)) {
ConsumeToken();
if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
"attribute")) {
SkipUntil(tok::r_paren, true); // skip until ) or ;
return;
}
if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, "(")) {
SkipUntil(tok::r_paren, true); // skip until ) or ;
return;
}
// Parse the attribute-list. e.g. __attribute__(( weak, alias("__f") ))
while (Tok.is(tok::identifier) || isDeclarationSpecifier() ||
Tok.is(tok::comma)) {
if (Tok.is(tok::comma)) {
// allows for empty/non-empty attributes. ((__vector_size__(16),,,,))
ConsumeToken();
continue;
}
// we have an identifier or declaration specifier (const, int, etc.)
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
SourceLocation AttrNameLoc = ConsumeToken();
if (Tok.is(tok::l_paren)) {
// handle "parameterized" attributes
if (LateAttrs && isAttributeLateParsed(*AttrName)) {
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())
getCurrentClass().LateParsedDeclarations.push_back(LA);
// consume everything up to and including the matching right parens
ConsumeAndStoreUntil(tok::r_paren, LA->Toks, true, false);
Token Eof;
Eof.startToken();
Eof.setLocation(Tok.getLocation());
LA->Toks.push_back(Eof);
} else {
ParseGNUAttributeArgs(AttrName, AttrNameLoc, attrs, endLoc);
}
} else {
attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc,
0, SourceLocation(), 0, 0);
}
}
if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen))
SkipUntil(tok::r_paren, false);
SourceLocation Loc = Tok.getLocation();
if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) {
SkipUntil(tok::r_paren, false);
}
if (endLoc)
*endLoc = Loc;
}
}
/// Parse the arguments to a parameterized GNU attribute
void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
SourceLocation AttrNameLoc,
ParsedAttributes &Attrs,
SourceLocation *EndLoc) {
assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
// Availability attributes have their own grammar.
if (AttrName->isStr("availability")) {
ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
return;
}
// Thread safety attributes fit into the FIXME case above, so we
// just parse the arguments as a list of expressions
if (IsThreadSafetyAttribute(AttrName->getName())) {
ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
return;
}
ConsumeParen(); // ignore the left paren loc for now
IdentifierInfo *ParmName = 0;
SourceLocation ParmLoc;
bool BuiltinType = false;
switch (Tok.getKind()) {
case tok::kw_char:
case tok::kw_wchar_t:
case tok::kw_char16_t:
case tok::kw_char32_t:
case tok::kw_bool:
case tok::kw_short:
case tok::kw_int:
case tok::kw_long:
case tok::kw___int64:
case tok::kw___int128:
case tok::kw_signed:
case tok::kw_unsigned:
case tok::kw_float:
case tok::kw_double:
case tok::kw_void:
case tok::kw_typeof:
// __attribute__(( vec_type_hint(char) ))
// FIXME: Don't just discard the builtin type token.
ConsumeToken();
BuiltinType = true;
break;
case tok::identifier:
ParmName = Tok.getIdentifierInfo();
ParmLoc = ConsumeToken();
break;
default:
break;
}
ExprVector ArgExprs(Actions);
if (!BuiltinType &&
(ParmLoc.isValid() ? Tok.is(tok::comma) : Tok.isNot(tok::r_paren))) {
// Eat the comma.
if (ParmLoc.isValid())
ConsumeToken();
// Parse the non-empty comma-separated list of expressions.
while (1) {
ExprResult ArgExpr(ParseAssignmentExpression());
if (ArgExpr.isInvalid()) {
SkipUntil(tok::r_paren);
return;
}
ArgExprs.push_back(ArgExpr.release());
if (Tok.isNot(tok::comma))
break;
ConsumeToken(); // Eat the comma, move to the next argument
}
}
else if (Tok.is(tok::less) && AttrName->isStr("iboutletcollection")) {
if (!ExpectAndConsume(tok::less, diag::err_expected_less_after, "<",
tok::greater)) {
while (Tok.is(tok::identifier)) {
ConsumeToken();
if (Tok.is(tok::greater))
break;
if (Tok.is(tok::comma)) {
ConsumeToken();
continue;
}
}
if (Tok.isNot(tok::greater))
Diag(Tok, diag::err_iboutletcollection_with_protocol);
SkipUntil(tok::r_paren, false, true); // skip until ')'
}
}
SourceLocation RParen = Tok.getLocation();
if (!ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) {
AttributeList *attr =
Attrs.addNew(AttrName, SourceRange(AttrNameLoc, RParen), 0, AttrNameLoc,
ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size());
if (BuiltinType && attr->getKind() == AttributeList::AT_iboutletcollection)
Diag(Tok, diag::err_iboutletcollection_builtintype);
}
}
/// ParseMicrosoftDeclSpec - Parse an __declspec construct
///
/// [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::ParseMicrosoftDeclSpec(ParsedAttributes &attrs) {
assert(Tok.is(tok::kw___declspec) && "Not a declspec!");
ConsumeToken();
if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
"declspec")) {
SkipUntil(tok::r_paren, true); // skip until ) or ;
return;
}
while (Tok.getIdentifierInfo()) {
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
SourceLocation AttrNameLoc = ConsumeToken();
// FIXME: Remove this when we have proper __declspec(property()) support.
// Just skip everything inside property().
if (AttrName->getName() == "property") {
ConsumeParen();
SkipUntil(tok::r_paren);
}
if (Tok.is(tok::l_paren)) {
ConsumeParen();
// FIXME: This doesn't parse __declspec(property(get=get_func_name))
// correctly.
ExprResult ArgExpr(ParseAssignmentExpression());
if (!ArgExpr.isInvalid()) {
Expr *ExprList = ArgExpr.take();
attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0,
SourceLocation(), &ExprList, 1, true);
}
if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen))
SkipUntil(tok::r_paren, false);
} else {
attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc,
0, SourceLocation(), 0, 0, true);
}
}
if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen))
SkipUntil(tok::r_paren, false);
return;
}
void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) {
// Treat these like attributes
// FIXME: Allow Sema to distinguish between these and real attributes!
while (Tok.is(tok::kw___fastcall) || Tok.is(tok::kw___stdcall) ||
Tok.is(tok::kw___thiscall) || Tok.is(tok::kw___cdecl) ||
Tok.is(tok::kw___ptr64) || Tok.is(tok::kw___w64) ||
Tok.is(tok::kw___ptr32) ||
Tok.is(tok::kw___unaligned)) {
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
SourceLocation AttrNameLoc = ConsumeToken();
if (Tok.is(tok::kw___ptr64) || Tok.is(tok::kw___w64) ||
Tok.is(tok::kw___ptr32))
// FIXME: Support these properly!
continue;
attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0,
SourceLocation(), 0, 0, true);
}
}
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, 0, AttrNameLoc, 0,
SourceLocation(), 0, 0, true);
}
}
void Parser::ParseOpenCLAttributes(ParsedAttributes &attrs) {
// Treat these like attributes
while (Tok.is(tok::kw___kernel)) {
SourceLocation AttrNameLoc = ConsumeToken();
attrs.addNew(PP.getIdentifierInfo("opencl_kernel_function"),
AttrNameLoc, 0, AttrNameLoc, 0,
SourceLocation(), 0, 0, false);
}
}
void Parser::ParseOpenCLQualifiers(DeclSpec &DS) {
SourceLocation Loc = Tok.getLocation();
switch(Tok.getKind()) {
// OpenCL qualifiers:
case tok::kw___private:
case tok::kw_private:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("address_space"), Loc, 0);
break;
case tok::kw___global:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_global);
break;
case tok::kw___local:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_local);
break;
case tok::kw___constant:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_constant);
break;
case tok::kw___read_only:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("opencl_image_access"), Loc, CLIA_read_only);
break;
case tok::kw___write_only:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("opencl_image_access"), Loc, CLIA_write_only);
break;
case tok::kw___read_write:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("opencl_image_access"), Loc, CLIA_read_write);
break;
default: break;
}
}
/// \brief Parse a version number.
///
/// version:
/// simple-integer
/// simple-integer ',' simple-integer
/// simple-integer ',' simple-integer ',' simple-integer
VersionTuple Parser::ParseVersionTuple(SourceRange &Range) {
Range = Tok.getLocation();
if (!Tok.is(tok::numeric_constant)) {
Diag(Tok, diag::err_expected_version);
SkipUntil(tok::comma, tok::r_paren, true, true, true);
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, true, true, true);
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);
}
if (ThisTokBegin[AfterMajor] != '.' || (AfterMajor + 1 == ActualLength)) {
Diag(Tok, diag::err_expected_version);
SkipUntil(tok::comma, tok::r_paren, true, true, true);
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);
}
// If what follows is not a '.', we have a problem.
if (ThisTokBegin[AfterMinor] != '.') {
Diag(Tok, diag::err_expected_version);
SkipUntil(tok::comma, tok::r_paren, true, true, true);
return VersionTuple();
}
// 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, true, true, true);
return VersionTuple();
}
ConsumeToken();
return VersionTuple(Major, Minor, Subminor);
}
/// \brief Parse the contents of the "availability" attribute.
///
/// availability-attribute:
/// 'availability' '(' platform ',' version-arg-list, opt-message')'
///
/// platform:
/// identifier
///
/// version-arg-list:
/// version-arg
/// version-arg ',' version-arg-list
///
/// version-arg:
/// 'introduced' '=' version
/// 'deprecated' '=' version
/// 'obsoleted' = version
/// 'unavailable'
/// opt-message:
/// 'message' '=' <string>
void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
SourceLocation AvailabilityLoc,
ParsedAttributes &attrs,
SourceLocation *endLoc) {
SourceLocation PlatformLoc;
IdentifierInfo *Platform = 0;
enum { Introduced, Deprecated, Obsoleted, Unknown };
AvailabilityChange Changes[Unknown];
ExprResult MessageExpr;
// Opening '('.
BalancedDelimiterTracker T(*this, tok::l_paren);
if (T.consumeOpen()) {
Diag(Tok, diag::err_expected_lparen);
return;
}
// Parse the platform name,
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_availability_expected_platform);
SkipUntil(tok::r_paren);
return;
}
Platform = Tok.getIdentifierInfo();
PlatformLoc = ConsumeToken();
// Parse the ',' following the platform name.
if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "", tok::r_paren))
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");
}
// Parse the set of introductions/deprecations/removals.
SourceLocation UnavailableLoc;
do {
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_availability_expected_change);
SkipUntil(tok::r_paren);
return;
}
IdentifierInfo *Keyword = Tok.getIdentifierInfo();
SourceLocation KeywordLoc = ConsumeToken();
if (Keyword == Ident_unavailable) {
if (UnavailableLoc.isValid()) {
Diag(KeywordLoc, diag::err_availability_redundant)
<< Keyword << SourceRange(UnavailableLoc);
}
UnavailableLoc = KeywordLoc;
if (Tok.isNot(tok::comma))
break;
ConsumeToken();
continue;
}
if (Tok.isNot(tok::equal)) {
Diag(Tok, diag::err_expected_equal_after)
<< Keyword;
SkipUntil(tok::r_paren);
return;
}
ConsumeToken();
if (Keyword == Ident_message) {
if (!isTokenStringLiteral()) {
Diag(Tok, diag::err_expected_string_literal);
SkipUntil(tok::r_paren);
return;
}
MessageExpr = ParseStringLiteralExpression();
break;
}
SourceRange VersionRange;
VersionTuple Version = ParseVersionTuple(VersionRange);
if (Version.empty()) {
SkipUntil(tok::r_paren);
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;
}
if (Tok.isNot(tok::comma))
break;
ConsumeToken();
} while (true);
// 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()),
0, AvailabilityLoc,
Platform, PlatformLoc,
Changes[Introduced],
Changes[Deprecated],
Changes[Obsoleted],
UnavailableLoc, MessageExpr.take(),
false, false);
}
// Late Parsed Attributes:
// See other examples of late parsing in lib/Parse/ParseCXXInlineMethods
void Parser::LateParsedDeclaration::ParseLexedAttributes() {}
void Parser::LateParsedClass::ParseLexedAttributes() {
Self->ParseLexedAttributes(*Class);
}
void Parser::LateParsedAttribute::ParseLexedAttributes() {
Self->ParseLexedAttribute(*this, true, false);
}
/// Wrapper class which calls ParseLexedAttribute, after setting up the
/// scope appropriately.
void Parser::ParseLexedAttributes(ParsingClass &Class) {
// Deal with templates
// FIXME: Test cases to make sure this does the right thing for templates.
bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
ParseScope ClassTemplateScope(this, Scope::TemplateParamScope,
HasTemplateScope);
if (HasTemplateScope)
Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
// Set or update the scope flags to include Scope::ThisScope.
bool AlreadyHasClassScope = Class.TopLevelClass;
unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope|Scope::ThisScope;
ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope);
ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope);
// Enter the scope of nested classes
if (!AlreadyHasClassScope)
Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
Class.TagOrTemplate);
for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i) {
Class.LateParsedDeclarations[i]->ParseLexedAttributes();
}
if (!AlreadyHasClassScope)
Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(),
Class.TagOrTemplate);
}
/// \brief Parse all attributes in LAs, and attach them to Decl D.
void Parser::ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,
bool EnterScope, bool OnDefinition) {
for (unsigned i = 0, ni = LAs.size(); i < ni; ++i) {
LAs[i]->addDecl(D);
ParseLexedAttribute(*LAs[i], EnterScope, OnDefinition);
}
LAs.clear();
}
/// \brief Finish parsing an attribute for which parsing was delayed.
/// This will be called at the end of parsing a class declaration
/// for each LateParsedAttribute. We consume the saved tokens and
/// create an attribute with the arguments filled in. We add this
/// to the Attribute list for the decl.
void Parser::ParseLexedAttribute(LateParsedAttribute &LA,
bool EnterScope, bool OnDefinition) {
// Save the current token position.
SourceLocation OrigLoc = Tok.getLocation();
// Append the current token at the end of the new token stream so that it
// doesn't get lost.
LA.Toks.push_back(Tok);
PP.EnterTokenStream(LA.Toks.data(), LA.Toks.size(), true, false);
// Consume the previously pushed token.
ConsumeAnyToken();
if (OnDefinition && !IsThreadSafetyAttribute(LA.AttrName.getName())) {
Diag(Tok, diag::warn_attribute_on_function_definition)
<< LA.AttrName.getName();
}
ParsedAttributes Attrs(AttrFactory);
SourceLocation endLoc;
if (LA.Decls.size() == 1) {
Decl *D = LA.Decls[0];
// If the Decl is templatized, add template parameters to scope.
bool HasTemplateScope = EnterScope && D->isTemplateDecl();
ParseScope TempScope(this, Scope::TemplateParamScope, HasTemplateScope);
if (HasTemplateScope)
Actions.ActOnReenterTemplateScope(Actions.CurScope, D);
// If the Decl is on a function, add function parameters to the scope.
bool HasFunctionScope = EnterScope && D->isFunctionOrFunctionTemplate();
ParseScope FnScope(this, Scope::FnScope|Scope::DeclScope, HasFunctionScope);
if (HasFunctionScope)
Actions.ActOnReenterFunctionContext(Actions.CurScope, D);
ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc);
if (HasFunctionScope) {
Actions.ActOnExitFunctionContext();
FnScope.Exit(); // Pop scope, and remove Decls from IdResolver
}
if (HasTemplateScope) {
TempScope.Exit();
}
} else if (LA.Decls.size() > 0) {
// If there are multiple decls, then the decl cannot be within the
// function scope.
ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc);
} else {
Diag(Tok, diag::warn_attribute_no_decl) << LA.AttrName.getName();
}
for (unsigned i = 0, ni = LA.Decls.size(); i < ni; ++i) {
Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.Decls[i], Attrs);
}
if (Tok.getLocation() != OrigLoc) {
// Due to a parsing error, we either went over the cached tokens or
// there are still cached tokens left, so we skip the leftover tokens.
// Since this is an uncommon situation that should be avoided, use the
// expensive isBeforeInTranslationUnit call.
if (PP.getSourceManager().isBeforeInTranslationUnit(Tok.getLocation(),
OrigLoc))
while (Tok.getLocation() != OrigLoc && Tok.isNot(tok::eof))
ConsumeAnyToken();
}
}
/// \brief Wrapper around a case statement checking if AttrName is
/// one of the thread safety attributes
bool Parser::IsThreadSafetyAttribute(llvm::StringRef AttrName){
return llvm::StringSwitch<bool>(AttrName)
.Case("guarded_by", true)
.Case("guarded_var", true)
.Case("pt_guarded_by", true)
.Case("pt_guarded_var", true)
.Case("lockable", true)
.Case("scoped_lockable", true)
.Case("no_thread_safety_analysis", true)
.Case("acquired_after", true)
.Case("acquired_before", true)
.Case("exclusive_lock_function", true)
.Case("shared_lock_function", true)
.Case("exclusive_trylock_function", true)
.Case("shared_trylock_function", true)
.Case("unlock_function", true)
.Case("lock_returned", true)
.Case("locks_excluded", true)
.Case("exclusive_locks_required", true)
.Case("shared_locks_required", true)
.Default(false);
}
/// \brief Parse the contents of thread safety attributes. These
/// should always be parsed as an expression list.
///
/// We need to special case the parsing due to the fact that if the first token
/// of the first argument is an identifier, the main parse loop will store
/// that token as a "parameter" and the rest of
/// the arguments will be added to a list of "arguments". However,
/// subsequent tokens in the first argument are lost. We instead parse each
/// argument as an expression and add all arguments to the list of "arguments".
/// In future, we will take advantage of this special case to also
/// deal with some argument scoping issues here (for example, referring to a
/// function parameter in the attribute on that function).
void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName,
SourceLocation AttrNameLoc,
ParsedAttributes &Attrs,
SourceLocation *EndLoc) {
assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
ExprVector ArgExprs(Actions);
bool ArgExprsOk = true;
// now parse the list of expressions
while (Tok.isNot(tok::r_paren)) {
ExprResult ArgExpr(ParseAssignmentExpression());
if (ArgExpr.isInvalid()) {
ArgExprsOk = false;
T.consumeClose();
break;
} else {
ArgExprs.push_back(ArgExpr.release());
}
if (Tok.isNot(tok::comma))
break;
ConsumeToken(); // Eat the comma, move to the next argument
}
// Match the ')'.
if (ArgExprsOk && !T.consumeClose()) {
Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(),
ArgExprs.take(), ArgExprs.size());
}
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, /*StopAtSemi*/ false);
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.");
}
void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) {
Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed)
<< attrs.Range;
}
/// ParseDeclaration - Parse a full 'declaration', which consists of
/// declaration-specifiers, some number of declarators, and a semicolon.
/// 'Context' should be a Declarator::TheContext 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++0x/C11] static_assert-declaration
/// others... [FIXME]
///
Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts,
unsigned Context,
SourceLocation &DeclEnd,
ParsedAttributesWithRange &attrs) {
ParenBraceBracketBalancer BalancerRAIIObj(*this);
// Must temporarily exit the objective-c container scope for
// parsing c none objective-c decls.
ObjCDeclContextSwitch ObjCDC(*this);
Decl *SingleDecl = 0;
Decl *OwnedType = 0;
switch (Tok.getKind()) {
case tok::kw_template:
case tok::kw_export:
ProhibitAttributes(attrs);
SingleDecl = ParseDeclarationStartingWithTemplate(Context, DeclEnd);
break;
case tok::kw_inline:
// Could be the start of an inline namespace. Allowed as an ext in C++03.
if (getLangOpts().CPlusPlus && NextToken().is(tok::kw_namespace)) {
ProhibitAttributes(attrs);
SourceLocation InlineLoc = ConsumeToken();
SingleDecl = ParseNamespace(Context, DeclEnd, InlineLoc);
break;
}
return ParseSimpleDeclaration(Stmts, Context, DeclEnd, attrs,
true);
case tok::kw_namespace:
ProhibitAttributes(attrs);
SingleDecl = ParseNamespace(Context, DeclEnd);
break;
case tok::kw_using:
SingleDecl = ParseUsingDirectiveOrDeclaration(Context, ParsedTemplateInfo(),
DeclEnd, attrs, &OwnedType);
break;
case tok::kw_static_assert:
case tok::kw__Static_assert:
ProhibitAttributes(attrs);
SingleDecl = ParseStaticAssertDeclaration(DeclEnd);
break;
default:
return ParseSimpleDeclaration(Stmts, Context, DeclEnd, attrs, true);
}
// This routine returns a DeclGroup, if the thing we parsed only contains a
// single decl, convert it now. Alias declarations can also declare a type;
// include that too if it is present.
return Actions.ConvertDeclToDeclGroup(SingleDecl, OwnedType);
}
/// simple-declaration: [C99 6.7: declaration] [C++ 7p1: dcl.dcl]
/// declaration-specifiers init-declarator-list[opt] ';'
///[C90/C++]init-declarator-list ';' [TODO]
/// [OMP] threadprivate-directive [TODO]
///
/// for-range-declaration: [C++0x 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.
Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts,
unsigned Context,
SourceLocation &DeclEnd,
ParsedAttributes &attrs,
bool RequireSemi,
ForRangeInit *FRI) {
// Parse the common declaration-specifiers piece.
ParsingDeclSpec DS(*this);
DS.takeAttributesFrom(attrs);
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none,
getDeclSpecContextFromDeclaratorContext(Context));
// C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
// declaration-specifiers init-declarator-list[opt] ';'
if (Tok.is(tok::semi)) {
if (RequireSemi) ConsumeToken();
Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none,
DS);
DS.complete(TheDecl);
return Actions.ConvertDeclToDeclGroup(TheDecl);
}
return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI);
}
/// Returns true if this might be the start of a declarator, or a common typo
/// for a declarator.
bool Parser::MightBeDeclarator(unsigned 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 == Declarator::MemberContext && getLangOpts().CPlusPlus0x &&
NextToken().is(tok::l_square);
case tok::colon: // Might be a typo for '::' or an unnamed bit-field.
return Context == Declarator::MemberContext || getLangOpts().CPlusPlus;
case tok::identifier:
switch (NextToken().getKind()) {
case tok::code_completion:
case tok::coloncolon:
case tok::comma:
case tok::equal:
case tok::equalequal: // Might be a typo for '='.
case tok::kw_alignas:
case tok::kw_asm:
case tok::kw___attribute:
case tok::l_brace:
case tok::l_paren:
case tok::l_square:
case tok::less:
case tok::r_brace:
case tok::r_paren:
case tok::r_square:
case tok::semi:
return true;
case tok::colon:
// At namespace scope, 'identifier:' is probably a typo for 'identifier::'
// and in block scope it's probably a label. Inside a class definition,
// this is a bit-field.
return Context == Declarator::MemberContext ||
(getLangOpts().CPlusPlus && Context == Declarator::FileContext);
case tok::identifier: // Possible virt-specifier.
return getLangOpts().CPlusPlus0x && isCXX0XVirtSpecifier(NextToken());
default:
return false;
}
default:
return false;
}
}
/// 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,
unsigned Context,
bool AllowFunctionDefinitions,
SourceLocation *DeclEnd,
ForRangeInit *FRI) {
// Parse the first declarator.
ParsingDeclarator D(*this, DS, static_cast<Declarator::TheContext>(Context));
ParseDeclarator(D);
// Bail out if the first declarator didn't seem well-formed.
if (!D.hasName() && !D.mayOmitIdentifier()) {
// Skip until ; or }.
SkipUntil(tok::r_brace, true, true);
if (Tok.is(tok::semi))
ConsumeToken();
return DeclGroupPtrTy();
}
// Save late-parsed attributes for now; they need to be parsed in the
// appropriate function scope after the function Decl has been constructed.
LateParsedAttrList LateParsedAttrs;
if (D.isFunctionDeclarator())
MaybeParseGNUAttributes(D, &LateParsedAttrs);
// Check to see if we have a function *definition* which must have a body.
if (AllowFunctionDefinitions && D.isFunctionDeclarator() &&
// 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.
!isDeclarationAfterDeclarator()) {
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()) {
// If there is an invalid declaration specifier right after the function
// prototype, then we must be in a missing semicolon case where this isn't
// actually a body. Just fall through into the code that handles it as a
// prototype, and let the top-level code handle the erroneous declspec
// where it would otherwise expect a comma or semicolon.
} else {
Diag(Tok, diag::err_expected_fn_body);
SkipUntil(tok::semi);
return DeclGroupPtrTy();
}
}
if (ParseAsmAttributesAfterDeclarator(D))
return DeclGroupPtrTy();
// 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.
if (FRI && Tok.is(tok::colon)) {
FRI->ColonLoc = ConsumeToken();
if (Tok.is(tok::l_brace))
FRI->RangeExpr = ParseBraceInitializer();
else
FRI->RangeExpr = ParseExpression();
Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
Actions.ActOnCXXForRangeDecl(ThisDecl);
Actions.FinalizeDeclaration(ThisDecl);
D.complete(ThisDecl);
return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, &ThisDecl, 1);
}
SmallVector<Decl *, 8> DeclsInGroup;
Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(D);
if (LateParsedAttrs.size() > 0)
ParseLexedAttributeList(LateParsedAttrs, FirstDecl, true, false);
D.complete(FirstDecl);
if (FirstDecl)
DeclsInGroup.push_back(FirstDecl);
bool ExpectSemi = Context != Declarator::ForContext;
// If we don't have a comma, it is either the end of the list (a ';') or an
// error, bail out.
while (Tok.is(tok::comma)) {
SourceLocation CommaLoc = ConsumeToken();
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);
ParseDeclarator(D);
if (!D.isInvalidType()) {
Decl *ThisDecl = ParseDeclarationAfterDeclarator(D);
D.complete(ThisDecl);
if (ThisDecl)
DeclsInGroup.push_back(ThisDecl);
}
}
if (DeclEnd)
*DeclEnd = Tok.getLocation();
if (ExpectSemi &&
ExpectAndConsume(tok::semi,
Context == Declarator::FileContext
? 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()) {
SkipUntil(tok::r_brace, true, true);
if (Tok.is(tok::semi))
ConsumeToken();
}
}
return Actions.FinalizeDeclaratorGroup(getCurScope(), DS,
DeclsInGroup.data(),
DeclsInGroup.size());
}
/// 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(&Loc));
if (AsmLabel.isInvalid()) {
SkipUntil(tok::semi, true, true);
return true;
}
D.setAsmLabel(AsmLabel.release());
D.SetRangeEnd(Loc);
}
MaybeParseGNUAttributes(D);
return false;
}
/// \brief 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 0;
return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo);
}
Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
const ParsedTemplateInfo &TemplateInfo) {
// Inform the current actions module that we just parsed this declarator.
Decl *ThisDecl = 0;
switch (TemplateInfo.Kind) {
case ParsedTemplateInfo::NonTemplate:
ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
break;
case ParsedTemplateInfo::Template:
case ParsedTemplateInfo::ExplicitSpecialization:
ThisDecl = Actions.ActOnTemplateDeclarator(getCurScope(),
MultiTemplateParamsArg(Actions,
TemplateInfo.TemplateParams->data(),
TemplateInfo.TemplateParams->size()),
D);
break;
case ParsedTemplateInfo::ExplicitInstantiation: {
DeclResult ThisRes
= Actions.ActOnExplicitInstantiation(getCurScope(),
TemplateInfo.ExternLoc,
TemplateInfo.TemplateLoc,
D);
if (ThisRes.isInvalid()) {
SkipUntil(tok::semi, true, true);
return 0;
}
ThisDecl = ThisRes.get();
break;
}
}
bool TypeContainsAuto =
D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto;
// Parse declarator '=' initializer.
// If a '==' or '+=' is found, suggest a fixit to '='.
if (isTokenEqualOrEqualTypo()) {
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);
} else {
if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) {
EnterScope(0);
Actions.ActOnCXXEnterDeclInitializer(getCurScope(), ThisDecl);
}
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteInitializer(getCurScope(), ThisDecl);
cutOffParsing();
return 0;
}
ExprResult Init(ParseInitializer());
if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) {
Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl);
ExitScope();
}
if (Init.isInvalid()) {
SkipUntil(tok::comma, true, true);
Actions.ActOnInitializerError(ThisDecl);
} else
Actions.AddInitializerToDecl(ThisDecl, Init.take(),
/*DirectInit=*/false, TypeContainsAuto);
}
} else if (Tok.is(tok::l_paren)) {
// Parse C++ direct initializer: '(' expression-list ')'
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
ExprVector Exprs(Actions);
CommaLocsTy CommaLocs;
if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) {
EnterScope(0);
Actions.ActOnCXXEnterDeclInitializer(getCurScope(), ThisDecl);
}
if (ParseExpressionList(Exprs, CommaLocs)) {
SkipUntil(tok::r_paren);
if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) {
Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl);
ExitScope();
}
} else {
// Match the ')'.
T.consumeClose();
assert(!Exprs.empty() && Exprs.size()-1 == CommaLocs.size() &&
"Unexpected number of commas!");
if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) {
Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl);
ExitScope();
}
ExprResult Initializer = Actions.ActOnParenListExpr(T.getOpenLocation(),
T.getCloseLocation(),
move_arg(Exprs));
Actions.AddInitializerToDecl(ThisDecl, Initializer.take(),
/*DirectInit=*/true, TypeContainsAuto);
}
} else if (getLangOpts().CPlusPlus0x && Tok.is(tok::l_brace)) {
// Parse C++0x braced-init-list.
Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
if (D.getCXXScopeSpec().isSet()) {
EnterScope(0);
Actions.ActOnCXXEnterDeclInitializer(getCurScope(), ThisDecl);
}
ExprResult Init(ParseBraceInitializer());
if (D.getCXXScopeSpec().isSet()) {
Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl);
ExitScope();
}
if (Init.isInvalid()) {
Actions.ActOnInitializerError(ThisDecl);
} else
Actions.AddInitializerToDecl(ThisDecl, Init.take(),
/*DirectInit=*/true, TypeContainsAuto);
} else {
Actions.ActOnUninitializedDecl(ThisDecl, TypeContainsAuto);
}
Actions.FinalizeDeclaration(ThisDecl);
return 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, 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);
// Validate declspec for type-name.
unsigned Specs = DS.getParsedSpecifiers();
if (DSC == DSC_type_specifier && !DS.hasTypeSpecifier()) {
Diag(Tok, diag::err_expected_type);
DS.SetTypeSpecError();
} else if (Specs == DeclSpec::PQ_None && !DS.getNumProtocolQualifiers() &&
!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.getThreadSpecLoc(), diag::err_typename_invalid_storageclass);
DS.ClearStorageClassSpecs();
}
// Issue diagnostic and remove function specfier 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.isExplicitSpecified())
Diag(DS.getExplicitSpecLoc(), diag::err_typename_invalid_functionspec);
DS.ClearFunctionSpecs();
}
// Issue diagnostic and remove constexpr specfier if present.
if (DS.isConstexprSpecified()) {
Diag(DS.getConstexprSpecLoc(), diag::err_typename_invalid_constexpr);
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.is(tok::l_square) || T.is(tok::l_paren) || T.is(tok::r_paren) ||
T.is(tok::semi) || T.is(tok::comma) || T.is(tok::equal) ||
T.is(tok::kw_asm) || T.is(tok::l_brace) || T.is(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) {
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 identifer 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.
// FIXME: Don't bail out here in languages with no implicit int (like
// C++ with no -fms-extensions). This is much more likely to be an undeclared
// type or typo than a use of implicit int.
if (DSC != DSC_type_specifier &&
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;
}
// 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 == 0) {
const char *TagName = 0, *FixitTagName = 0;
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_class:
TagName="class" ; FixitTagName = "class " ;TagKind=tok::kw_class ;break;
}
if (TagName) {
Diag(Loc, diag::err_use_of_tag_name_without_tag)
<< Tok.getIdentifierInfo() << TagName << getLangOpts().CPlusPlus
<< FixItHint::CreateInsertion(Tok.getLocation(),FixitTagName);
// Parse this as a tag as if the missing tag were present.
if (TagKind == tok::kw_enum)
ParseEnumSpecifier(Loc, DS, TemplateInfo, AS, DSC_normal);
else
ParseClassSpecifier(TagKind, Loc, DS, TemplateInfo, AS,
/*EnteringContext*/ false, DSC_normal);
return true;
}
}
// This is almost certainly an invalid type name. Let the action emit a
// diagnostic and attempt to recover.
ParsedType T;
if (Actions.DiagnoseUnknownTypeName(*Tok.getIdentifierInfo(), Loc,
getCurScope(), SS, T)) {
// The action emitted a diagnostic, so we don't have to.
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);
DS.SetRangeEnd(Tok.getLocation());
ConsumeToken();
// There may be other declaration specifiers after this.
return true;
}
// Fall through; the action had no suggestion for us.
} else {
// The action did not emit a diagnostic, so emit one now.
SourceRange R;
if (SS) R = SS->getRange();
Diag(Loc, diag::err_unknown_typename) << Tok.getIdentifierInfo() << R;
}
// Mark this as an error.
DS.SetTypeSpecError();
DS.SetRangeEnd(Tok.getLocation());
ConsumeToken();
// 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 false;
}
/// \brief Determine the declaration specifier context from the declarator
/// context.
///
/// \param Context the declarator context, which is one of the
/// Declarator::TheContext enumerator values.
Parser::DeclSpecContext
Parser::getDeclSpecContextFromDeclaratorContext(unsigned Context) {
if (Context == Declarator::MemberContext)
return DSC_class;
if (Context == Declarator::FileContext)
return DSC_top_level;
if (Context == Declarator::TrailingReturnContext)
return DSC_trailing;
return DSC_normal;
}
/// ParseAlignArgument - Parse the argument to an alignment-specifier.
///
/// FIXME: Simply returns an alignof() expression if the argument is a
/// type. Ideally, the type should be propagated directly into Sema.
///
/// [C11] type-id
/// [C11] constant-expression
/// [C++0x] type-id ...[opt]
/// [C++0x] assignment-expression ...[opt]
ExprResult Parser::ParseAlignArgument(SourceLocation Start,
SourceLocation &EllipsisLoc) {
ExprResult ER;
if (isTypeIdInParens()) {
SourceLocation TypeLoc = Tok.getLocation();
ParsedType Ty = ParseTypeName().get();
SourceRange TypeRange(Start, Tok.getLocation());
ER = Actions.ActOnUnaryExprOrTypeTraitExpr(TypeLoc, UETT_AlignOf, true,
Ty.getAsOpaquePtr(), TypeRange);
} else
ER = ParseConstantExpression();
if (getLangOpts().CPlusPlus0x && Tok.is(tok::ellipsis))
EllipsisLoc = ConsumeToken();
return ER;
}
/// ParseAlignmentSpecifier - Parse an alignment-specifier, and add the
/// attribute to Attrs.
///
/// alignment-specifier:
/// [C11] '_Alignas' '(' type-id ')'
/// [C11] '_Alignas' '(' constant-expression ')'
/// [C++0x] 'alignas' '(' type-id ...[opt] ')'
/// [C++0x] 'alignas' '(' assignment-expression ...[opt] ')'
void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
SourceLocation *endLoc) {
assert((Tok.is(tok::kw_alignas) || Tok.is(tok::kw__Alignas)) &&
"Not an alignment-specifier!");
SourceLocation KWLoc = Tok.getLocation();
ConsumeToken();
BalancedDelimiterTracker T(*this, tok::l_paren);
if (T.expectAndConsume(diag::err_expected_lparen))
return;
SourceLocation EllipsisLoc;
ExprResult ArgExpr = ParseAlignArgument(T.getOpenLocation(), EllipsisLoc);
if (ArgExpr.isInvalid()) {
SkipUntil(tok::r_paren);
return;
}
T.consumeClose();
if (endLoc)
*endLoc = T.getCloseLocation();
// FIXME: Handle pack-expansions here.
if (EllipsisLoc.isValid()) {
Diag(EllipsisLoc, diag::err_alignas_pack_exp_unsupported);
return;
}
ExprVector ArgExprs(Actions);
ArgExprs.push_back(ArgExpr.release());
Attrs.addNew(PP.getIdentifierInfo("aligned"), KWLoc, 0, KWLoc,
0, T.getOpenLocation(), ArgExprs.take(), 1, false, 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]
///
/// storage-class-specifier: [C99 6.7.1]
/// 'typedef'
/// 'extern'
/// 'static'
/// 'auto'
/// 'register'
/// [C++] 'mutable'
/// [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) {
if (DS.getSourceRange().isInvalid()) {
DS.SetRangeStart(Tok.getLocation());
DS.SetRangeEnd(Tok.getLocation());
}
bool EnteringContext = (DSContext == DSC_class || DSContext == DSC_top_level);
while (1) {
bool isInvalid = false;
const char *PrevSpec = 0;
unsigned DiagID = 0;
SourceLocation Loc = Tok.getLocation();
switch (Tok.getKind()) {
default:
DoneWithDeclSpec:
// [C++0x] decl-specifier-seq: decl-specifier attribute-specifier-seq[opt]
MaybeParseCXX0XAttributes(DS.getAttributes());
// If this is not a declaration specifier token, we're done reading decl
// specifiers. First verify that DeclSpec's are consistent.
DS.Finish(Diags, PP);
return;
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 == DSC_top_level ||
(DSContext == DSC_class && DS.isFriendSpecified());
Actions.CodeCompleteDeclSpec(getCurScope(), DS,
AllowNonIdentifiers,
AllowNestedNameSpecifiers);
return cutOffParsing();
}
if (getCurScope()->getFnParent() || getCurScope()->getBlockParent())
CCC = Sema::PCC_LocalDeclarationSpecifiers;
else if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate)
CCC = DSContext == DSC_class? Sema::PCC_MemberTemplate
: Sema::PCC_Template;
else if (DSContext == DSC_class)
CCC = Sema::PCC_Class;
else if (CurParsedObjCImpl)
CCC = Sema::PCC_ObjCImplementation;
Actions.CodeCompleteOrdinaryName(getCurScope(), CCC);
return cutOffParsing();
}
case tok::coloncolon: // ::foo::bar
// C++ scope specifier. Annotate and loop, or bail out on error.
if (TryAnnotateCXXScopeToken(true)) {
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())
goto DoneWithDeclSpec;
CXXScopeSpec SS;
Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
Tok.getAnnotationRange(),
SS);
// We are looking for a qualified typename.
Token Next = NextToken();
if (Next.is(tok::annot_template_id) &&
static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
->Kind == TNK_Type_template) {
// We have a qualified template-id, e.g., N::A<int>
// C++ [class.qual]p2:
// In a lookup in which the constructor is an acceptable lookup
// result and the nested-name-specifier nominates a class C:
//
// - if the name specified after the
// nested-name-specifier, when looked up in C, is the
// injected-class-name of C (Clause 9), or
//
// - if the name specified after the nested-name-specifier
// is the same as the identifier or the
// simple-template-id's template-name in the last
// component of the nested-name-specifier,
//
// the name is instead considered to name the constructor of
// class C.
//
// Thus, if the template-name is actually the constructor
// name, then the code is ill-formed; this interpretation is
// reinforced by the NAD status of core issue 635.
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next);
if ((DSContext == DSC_top_level ||
(DSContext == DSC_class && DS.isFriendSpecified())) &&
TemplateId->Name &&
Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS)) {
if (isConstructorDeclarator()) {
// The user meant this to be an out-of-line constructor
// definition, but template arguments are not allowed
// there. Just allow this as a constructor; we'll
// complain about it later.
goto DoneWithDeclSpec;
}
// The user meant this to name a type, but it actually names
// a constructor with some extraneous template
// arguments. Complain, then parse it as a type as the user
// intended.
Diag(TemplateId->TemplateNameLoc,
diag::err_out_of_line_template_id_names_constructor)
<< TemplateId->Name;
}
DS.getTypeSpecScope() = SS;
ConsumeToken(); // The C++ scope.
assert(Tok.is(tok::annot_template_id) &&
"ParseOptionalCXXScopeSpecifier not working");
AnnotateTemplateIdTokenAsType();
continue;
}
if (Next.is(tok::annot_typename)) {
DS.getTypeSpecScope() = SS;
ConsumeToken(); // The C++ scope.
if (Tok.getAnnotationValue()) {
ParsedType T = getTypeAnnotation(Tok);
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename,
Tok.getAnnotationEndLoc(),
PrevSpec, DiagID, T);
}
else
DS.SetTypeSpecError();
DS.SetRangeEnd(Tok.getAnnotationEndLoc());
ConsumeToken(); // The typename
}
if (Next.isNot(tok::identifier))
goto DoneWithDeclSpec;
// If we're in a context where the identifier could be a class name,
// check whether this is a constructor declaration.
if ((DSContext == DSC_top_level ||
(DSContext == DSC_class && DS.isFriendSpecified())) &&
Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(),
&SS)) {
if (isConstructorDeclarator())
goto DoneWithDeclSpec;
// As noted in C++ [class.qual]p2 (cited above), when the name
// of the class is qualified in a context where it could name
// a constructor, its a constructor name. However, we've
// looked at the declarator, and the user probably meant this
// to be a type. Complain that it isn't supposed to be treated
// as a type, then proceed to parse it as a type.
Diag(Next.getLocation(), diag::err_out_of_line_type_names_constructor)
<< Next.getIdentifierInfo();
}
ParsedType TypeRep = Actions.getTypeName(*Next.getIdentifierInfo(),
Next.getLocation(),
getCurScope(), &SS,
false, false, ParsedType(),
/*IsCtorOrDtorName=*/false,
/*NonTrivialSourceInfo=*/true);
// If the referenced identifier is not a type, then this declspec is
// erroneous: We already checked about that it has no type specifier, and
// C++ doesn't have implicit int. Diagnose it as a typo w.r.t. to the
// typename.
if (TypeRep == 0) {
ConsumeToken(); // Eat the scope spec so the identifier is current.
if (ParseImplicitInt(DS, &SS, TemplateInfo, AS, DSContext)) continue;
goto DoneWithDeclSpec;
}
DS.getTypeSpecScope() = SS;
ConsumeToken(); // The C++ scope.
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
DiagID, TypeRep);
if (isInvalid)
break;
DS.SetRangeEnd(Tok.getLocation());
ConsumeToken(); // The typename.
continue;
}
case tok::annot_typename: {
if (Tok.getAnnotationValue()) {
ParsedType T = getTypeAnnotation(Tok);
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
DiagID, T);
} else
DS.SetTypeSpecError();
if (isInvalid)
break;
DS.SetRangeEnd(Tok.getAnnotationEndLoc());
ConsumeToken(); // The typename
// Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id'
// is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an
// Objective-C interface.
if (Tok.is(tok::less) && getLangOpts().ObjC1)
ParseObjCProtocolQualifiers(DS);
continue;
}
case tok::kw___is_signed:
// GNU libstdc++ 4.4 uses __is_signed as an identifier, but Clang
// typically treats it as a trait. If we see __is_signed as it appears
// in libstdc++, e.g.,
//
// static const bool __is_signed;
//
// then treat __is_signed as an identifier rather than as a keyword.
if (DS.getTypeSpecType() == TST_bool &&
DS.getTypeQualifiers() == DeclSpec::TQ_const &&
DS.getStorageClassSpec() == DeclSpec::SCS_static) {
Tok.getIdentifierInfo()->RevertTokenIDToIdentifier();
Tok.setKind(tok::identifier);
}
// We're done with the declaration-specifiers.
goto DoneWithDeclSpec;
// typedef-name
case tok::kw_decltype:
case tok::identifier: {
// In C++, check to see if this is a scope specifier like foo::bar::, if
// so handle it as such. This is important for ctor parsing.
if (getLangOpts().CPlusPlus) {
if (TryAnnotateCXXScopeToken(true)) {
if (!DS.hasTypeSpecifier())
DS.SetTypeSpecError();
goto DoneWithDeclSpec;
}
if (!Tok.is(tok::identifier))
continue;
}
// This identifier can only be a typedef name if we haven't already seen
// a type-specifier. Without this check we misparse:
// typedef int X; struct Y { short X; }; as 'short int'.
if (DS.hasTypeSpecifier())
goto DoneWithDeclSpec;
// Check for need to substitute AltiVec keyword tokens.
if (TryAltiVecToken(DS, Loc, PrevSpec, DiagID, isInvalid))
break;
ParsedType TypeRep =
Actions.getTypeName(*Tok.getIdentifierInfo(),
Tok.getLocation(), getCurScope());
// If this is not a typedef name, don't parse it as part of the declspec,
// it must be an implicit int or an error.
if (!TypeRep) {
if (ParseImplicitInt(DS, 0, TemplateInfo, AS, DSContext)) continue;
goto DoneWithDeclSpec;
}
// If we're in a context where the identifier could be a class name,
// check whether this is a constructor declaration.
if (getLangOpts().CPlusPlus && DSContext == DSC_class &&
Actions.isCurrentClassName(*Tok.getIdentifierInfo(), getCurScope()) &&
isConstructorDeclarator())
goto DoneWithDeclSpec;
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
DiagID, TypeRep);
if (isInvalid)
break;
DS.SetRangeEnd(Tok.getLocation());
ConsumeToken(); // The identifier
// Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id'
// is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an
// Objective-C interface.
if (Tok.is(tok::less) && getLangOpts().ObjC1)
ParseObjCProtocolQualifiers(DS);
// Need to support trailing type qualifiers (e.g. "id<p> const").
// If a type specifier follows, it will be diagnosed elsewhere.
continue;
}
// type-name
case tok::annot_template_id: {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
if (TemplateId->Kind != TNK_Type_template) {
// This template-id does not refer to a type name, so we're
// done with the type-specifiers.
goto DoneWithDeclSpec;
}
// If we're in a context where the template-id could be a
// constructor name or specialization, check whether this is a
// constructor declaration.
if (getLangOpts().CPlusPlus && DSContext == DSC_class &&
Actions.isCurrentClassName(*TemplateId->Name, getCurScope()) &&
isConstructorDeclarator())
goto DoneWithDeclSpec;
// Turn the template-id annotation token into a type annotation
// token, then try again to parse it as a type-specifier.
AnnotateTemplateIdTokenAsType();
continue;
}
// GNU attributes support.
case tok::kw___attribute:
ParseGNUAttributes(DS.getAttributes(), 0, LateAttrs);
continue;
// Microsoft declspec support.
case tok::kw___declspec:
ParseMicrosoftDeclSpec(DS.getAttributes());
continue;
// Microsoft single token adornments.
case tok::kw___forceinline:
// FIXME: Add handling here!
break;
case tok::kw___ptr64:
case tok::kw___ptr32:
case tok::kw___w64:
case tok::kw___cdecl:
case tok::kw___stdcall:
case tok::kw___fastcall:
case tok::kw___thiscall:
case tok::kw___unaligned:
ParseMicrosoftTypeAttributes(DS.getAttributes());
continue;
// Borland single token adornments.
case tok::kw___pascal:
ParseBorlandTypeAttributes(DS.getAttributes());
continue;
// OpenCL single token adornments.
case tok::kw___kernel:
ParseOpenCLAttributes(DS.getAttributes());
continue;
// storage-class-specifier
case tok::kw_typedef:
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
PrevSpec, DiagID);
break;
case tok::kw_extern:
if (DS.isThreadSpecified())
Diag(Tok, diag::ext_thread_before) << "extern";
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_extern, Loc,
PrevSpec, DiagID);
break;
case tok::kw___private_extern__:
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_private_extern,
Loc, PrevSpec, DiagID);
break;
case tok::kw_static:
if (DS.isThreadSpecified())
Diag(Tok, diag::ext_thread_before) << "static";
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_static, Loc,
PrevSpec, DiagID);
break;
case tok::kw_auto:
if (getLangOpts().CPlusPlus0x) {
if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) {
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
PrevSpec, DiagID);
if (!isInvalid)
Diag(Tok, diag::ext_auto_storage_class)
<< FixItHint::CreateRemoval(DS.getStorageClassSpecLoc());
} else
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec,
DiagID);
} else
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
PrevSpec, DiagID);
break;
case tok::kw_register:
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_register, Loc,
PrevSpec, DiagID);
break;
case tok::kw_mutable:
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_mutable, Loc,
PrevSpec, DiagID);
break;
case tok::kw___thread:
isInvalid = DS.SetStorageClassSpecThread(Loc, PrevSpec, DiagID);
break;
// function-specifier
case tok::kw_inline:
isInvalid = DS.SetFunctionSpecInline(Loc, PrevSpec, DiagID);
break;
case tok::kw_virtual:
isInvalid = DS.SetFunctionSpecVirtual(Loc, PrevSpec, DiagID);
break;
case tok::kw_explicit:
isInvalid = DS.SetFunctionSpecExplicit(Loc, PrevSpec, DiagID);
break;
// alignment-specifier
case tok::kw__Alignas:
if (!getLangOpts().C11)
Diag(Tok, diag::ext_c11_alignas);
ParseAlignmentSpecifier(DS.getAttributes());
continue;
// friend
case tok::kw_friend:
if (DSContext == DSC_class)
isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
else {
PrevSpec = ""; // not actually used by the diagnostic
DiagID = diag::err_friend_invalid_in_context;
isInvalid = true;
}
break;
// Modules
case tok::kw___module_private__:
isInvalid = DS.setModulePrivateSpec(Loc, PrevSpec, DiagID);
break;
// constexpr
case tok::kw_constexpr:
isInvalid = DS.SetConstexprSpec(Loc, PrevSpec, DiagID);
break;
// type-specifier
case tok::kw_short:
isInvalid = DS.SetTypeSpecWidth(DeclSpec::TSW_short, Loc, PrevSpec,
DiagID);
break;
case tok::kw_long:
if (DS.getTypeSpecWidth() != DeclSpec::TSW_long)
isInvalid = DS.SetTypeSpecWidth(DeclSpec::TSW_long, Loc, PrevSpec,
DiagID);
else
isInvalid = DS.SetTypeSpecWidth(DeclSpec::TSW_longlong, Loc, PrevSpec,
DiagID);
break;
case tok::kw___int64:
isInvalid = DS.SetTypeSpecWidth(DeclSpec::TSW_longlong, Loc, PrevSpec,
DiagID);
break;
case tok::kw_signed:
isInvalid = DS.SetTypeSpecSign(DeclSpec::TSS_signed, Loc, PrevSpec,
DiagID);
break;
case tok::kw_unsigned:
isInvalid = DS.SetTypeSpecSign(DeclSpec::TSS_unsigned, Loc, PrevSpec,
DiagID);
break;
case tok::kw__Complex:
isInvalid = DS.SetTypeSpecComplex(DeclSpec::TSC_complex, Loc, PrevSpec,
DiagID);
break;
case tok::kw__Imaginary:
isInvalid = DS.SetTypeSpecComplex(DeclSpec::TSC_imaginary, Loc, PrevSpec,
DiagID);
break;
case tok::kw_void:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_void, Loc, PrevSpec,
DiagID);
break;
case tok::kw_char:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char, Loc, PrevSpec,
DiagID);
break;
case tok::kw_int:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec,
DiagID);
break;
case tok::kw___int128:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec,
DiagID);
break;
case tok::kw_half:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec,
DiagID);
break;
case tok::kw_float:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float, Loc, PrevSpec,
DiagID);
break;
case tok::kw_double:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_double, Loc, PrevSpec,
DiagID);
break;
case tok::kw_wchar_t:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_wchar, Loc, PrevSpec,
DiagID);
break;
case tok::kw_char16_t:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char16, Loc, PrevSpec,
DiagID);
break;
case tok::kw_char32_t:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char32, Loc, PrevSpec,
DiagID);
break;
case tok::kw_bool:
case tok::kw__Bool:
if (Tok.is(tok::kw_bool) &&
DS.getTypeSpecType() != DeclSpec::TST_unspecified &&
DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
PrevSpec = ""; // Not used by the diagnostic.
DiagID = diag::err_bool_redeclaration;
// For better error recovery.
Tok.setKind(tok::identifier);
isInvalid = true;
} else {
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_bool, Loc, PrevSpec,
DiagID);
}
break;
case tok::kw__Decimal32:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal32, Loc, PrevSpec,
DiagID);
break;
case tok::kw__Decimal64:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal64, Loc, PrevSpec,
DiagID);
break;
case tok::kw__Decimal128:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal128, Loc, PrevSpec,
DiagID);
break;
case tok::kw___vector:
isInvalid = DS.SetTypeAltiVecVector(true, Loc, PrevSpec, DiagID);
break;
case tok::kw___pixel:
isInvalid = DS.SetTypeAltiVecPixel(true, Loc, PrevSpec, DiagID);
break;
case tok::kw___unknown_anytype:
isInvalid = DS.SetTypeSpecType(TST_unknown_anytype, Loc,
PrevSpec, DiagID);
break;
// class-specifier:
case tok::kw_class:
case tok::kw_struct:
case tok::kw_union: {
tok::TokenKind Kind = Tok.getKind();
ConsumeToken();
ParseClassSpecifier(Kind, Loc, DS, TemplateInfo, AS,
EnteringContext, DSContext);
continue;
}
// enum-specifier:
case tok::kw_enum:
ConsumeToken();
ParseEnumSpecifier(Loc, DS, TemplateInfo, AS, DSContext);
continue;
// cv-qualifier:
case tok::kw_const:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_const, Loc, PrevSpec, DiagID,
getLangOpts());
break;
case tok::kw_volatile:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_volatile, Loc, PrevSpec, DiagID,
getLangOpts());
break;
case tok::kw_restrict:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID,
getLangOpts());
break;
// C++ typename-specifier:
case tok::kw_typename:
if (TryAnnotateTypeOrScopeToken()) {
DS.SetTypeSpecError();
goto DoneWithDeclSpec;
}
if (!Tok.is(tok::kw_typename))
continue;
break;
// GNU typeof support.
case tok::kw_typeof:
ParseTypeofSpecifier(DS);
continue;
case tok::annot_decltype:
ParseDecltypeSpecifier(DS);
continue;
case tok::kw___underlying_type:
ParseUnderlyingTypeSpecifier(DS);
continue;
case tok::kw__Atomic:
ParseAtomicSpecifier(DS);
continue;
// OpenCL qualifiers:
case tok::kw_private:
if (!getLangOpts().OpenCL)
goto DoneWithDeclSpec;
case tok::kw___private:
case tok::kw___global:
case tok::kw___local:
case tok::kw___constant:
case tok::kw___read_only:
case tok::kw___write_only:
case tok::kw___read_write:
ParseOpenCLQualifiers(DS);
break;
case tok::less:
// GCC ObjC supports types like "<SomeProtocol>" as a synonym for
// "id<SomeProtocol>". This is hopelessly old fashioned and dangerous,
// but we support it.
if (DS.hasTypeSpecifier() || !getLangOpts().ObjC1)
goto DoneWithDeclSpec;
if (!ParseObjCProtocolQualifiers(DS))
Diag(Loc, diag::warn_objc_protocol_qualifier_missing_id)
<< FixItHint::CreateInsertion(Loc, "id")
<< SourceRange(Loc, DS.getSourceRange().getEnd());
// Need to support trailing type qualifiers (e.g. "id<p> const").
// If a type specifier follows, it will be diagnosed elsewhere.
continue;
}
// If the specifier wasn't legal, issue a diagnostic.
if (isInvalid) {
assert(PrevSpec && "Method did not return previous specifier!");
assert(DiagID);
if (DiagID == diag::ext_duplicate_declspec)
Diag(Tok, DiagID)
<< PrevSpec << FixItHint::CreateRemoval(Tok.getLocation());
else
Diag(Tok, DiagID) << PrevSpec;
}
DS.SetRangeEnd(Tok.getLocation());
if (DiagID != diag::err_bool_redeclaration)
ConsumeToken();
}
}
/// ParseStructDeclaration - Parse a struct declaration without the terminating
/// semicolon.
///
/// struct-declaration:
/// specifier-qualifier-list struct-declarator-list
/// [GNU] __extension__ struct-declaration
/// [GNU] specifier-qualifier-list
/// struct-declarator-list:
/// struct-declarator
/// struct-declarator-list ',' struct-declarator
/// [GNU] struct-declarator-list ',' attributes[opt] struct-declarator
/// struct-declarator:
/// declarator
/// [GNU] declarator attributes[opt]
/// declarator[opt] ':' constant-expression
/// [GNU] declarator[opt] ':' constant-expression attributes[opt]
///
void Parser::
ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) {
if (Tok.is(tok::kw___extension__)) {
// __extension__ silences extension warnings in the subexpression.
ExtensionRAIIObject O(Diags); // Use RAII to do this.
ConsumeToken();
return ParseStructDeclaration(DS, Fields);
}
// Parse the common specifier-qualifiers-list piece.
ParseSpecifierQualifierList(DS);
// If there are no declarators, this is a free-standing declaration
// specifier. Let the actions module cope with it.
if (Tok.is(tok::semi)) {
Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, DS);
return;
}
// Read struct-declarators until we find the semicolon.
bool FirstDeclarator = true;
SourceLocation CommaLoc;
while (1) {
ParsingDeclRAIIObject PD(*this);
FieldDeclarator DeclaratorInfo(DS);
DeclaratorInfo.D.setCommaLoc(CommaLoc);
// Attributes are only allowed here on successive declarators.
if (!FirstDeclarator)
MaybeParseGNUAttributes(DeclaratorInfo.D);
/// struct-declarator: declarator
/// struct-declarator: declarator[opt] ':' constant-expression
if (Tok.isNot(tok::colon)) {
// Don't parse FOO:BAR as if it were a typo for FOO::BAR.
ColonProtectionRAIIObject X(*this);
ParseDeclarator(DeclaratorInfo.D);
}
if (Tok.is(tok::colon)) {
ConsumeToken();
ExprResult Res(ParseConstantExpression());
if (Res.isInvalid())
SkipUntil(tok::semi, true, true);
else
DeclaratorInfo.BitfieldSize = Res.release();
}
// If attributes exist after the declarator, parse them.
MaybeParseGNUAttributes(DeclaratorInfo.D);
// We're done with this declarator; invoke the callback.
Decl *D = Fields.invoke(DeclaratorInfo);
PD.complete(D);
// If we don't have a comma, it is either the end of the list (a ';')
// or an error, bail out.
if (Tok.isNot(tok::comma))
return;
// Consume the comma.
CommaLoc = ConsumeToken();
FirstDeclarator = false;
}
}
/// ParseStructUnionBody
/// struct-contents:
/// struct-declaration-list
/// [EXT] empty
/// [GNU] "struct-declaration-list" without terminatoring ';'
/// struct-declaration-list:
/// struct-declaration
/// struct-declaration-list struct-declaration
/// [OBC] '@' 'defs' '(' class-name ')'
///
void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
unsigned TagType, Decl *TagDecl) {
PrettyDeclStackTraceEntry CrashInfo(Actions, TagDecl, RecordLoc,
"parsing struct/union body");
BalancedDelimiterTracker T(*this, tok::l_brace);
if (T.consumeOpen())
return;
ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope);
Actions.ActOnTagStartDefinition(getCurScope(), TagDecl);
// Empty structs are an extension in C (C99 6.7.2.1p7), but are allowed in
// C++.
if (Tok.is(tok::r_brace) && !getLangOpts().CPlusPlus) {
Diag(Tok, diag::ext_empty_struct_union) << (TagType == TST_union);
Diag(Tok, diag::warn_empty_struct_union_compat) << (TagType == TST_union);
}
SmallVector<Decl *, 32> FieldDecls;
// While we still have something to read, read the declarations in the struct.
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
// Each iteration of this loop reads one struct-declaration.
// Check for extraneous top-level semicolon.
if (Tok.is(tok::semi)) {
Diag(Tok, diag::ext_extra_struct_semi)
<< DeclSpec::getSpecifierName((DeclSpec::TST)TagType)
<< FixItHint::CreateRemoval(Tok.getLocation());
ConsumeToken();
continue;
}
// Parse all the comma separated declarators.
DeclSpec DS(AttrFactory);
if (!Tok.is(tok::at)) {
struct CFieldCallback : FieldCallback {
Parser &P;
Decl *TagDecl;
SmallVectorImpl<Decl *> &FieldDecls;
CFieldCallback(Parser &P, Decl *TagDecl,
SmallVectorImpl<Decl *> &FieldDecls) :
P(P), TagDecl(TagDecl), FieldDecls(FieldDecls) {}
virtual Decl *invoke(FieldDeclarator &FD) {
// Install the declarator into the current TagDecl.
Decl *Field = P.Actions.ActOnField(P.getCurScope(), TagDecl,
FD.D.getDeclSpec().getSourceRange().getBegin(),
FD.D, FD.BitfieldSize);
FieldDecls.push_back(Field);
return Field;
}
} Callback(*this, TagDecl, FieldDecls);
ParseStructDeclaration(DS, Callback);
} else { // Handle @defs
ConsumeToken();
if (!Tok.isObjCAtKeyword(tok::objc_defs)) {
Diag(Tok, diag::err_unexpected_at);
SkipUntil(tok::semi, true);
continue