diff options
Diffstat (limited to 'clang/lib/Parse')
-rw-r--r-- | clang/lib/Parse/AttributeList.cpp | 98 | ||||
-rw-r--r-- | clang/lib/Parse/DeclSpec.cpp | 287 | ||||
-rw-r--r-- | clang/lib/Parse/Makefile | 22 | ||||
-rw-r--r-- | clang/lib/Parse/MinimalAction.cpp | 136 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 1540 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 119 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExpr.cpp | 1081 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 99 | ||||
-rw-r--r-- | clang/lib/Parse/ParseInit.cpp | 227 | ||||
-rw-r--r-- | clang/lib/Parse/ParseObjc.cpp | 1578 | ||||
-rw-r--r-- | clang/lib/Parse/ParseStmt.cpp | 1159 | ||||
-rw-r--r-- | clang/lib/Parse/Parser.cpp | 647 |
12 files changed, 6993 insertions, 0 deletions
diff --git a/clang/lib/Parse/AttributeList.cpp b/clang/lib/Parse/AttributeList.cpp new file mode 100644 index 00000000000..0ff9447d2e6 --- /dev/null +++ b/clang/lib/Parse/AttributeList.cpp @@ -0,0 +1,98 @@ +//===--- AttributeList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the AttributeList class implementation +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/AttributeList.h" +using namespace clang; + +AttributeList::AttributeList(IdentifierInfo *aName, SourceLocation aLoc, + IdentifierInfo *pName, SourceLocation pLoc, + Action::ExprTy **elist, unsigned numargs, + AttributeList *n) + : AttrName(aName), AttrLoc(aLoc), ParmName(pName), ParmLoc(pLoc), + NumArgs(numargs), Next(n) { + Args = new Action::ExprTy*[numargs]; + for (unsigned i = 0; i != numargs; ++i) + Args[i] = elist[i]; +} + +AttributeList::~AttributeList() { + if (Args) { + // FIXME: before we delete the vector, we need to make sure the Expr's + // have been deleted. Since Action::ExprTy is "void", we are dependent + // on the actions module for actually freeing the memory. The specific + // hooks are ActOnDeclarator, ActOnTypeName, ActOnParamDeclaratorType, + // ParseField, ParseTag. Once these routines have freed the expression, + // they should zero out the Args slot (to indicate the memory has been + // freed). If any element of the vector is non-null, we should assert. + delete [] Args; + } + delete Next; +} + +AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { + const char *Str = Name->getName(); + unsigned Len = Name->getLength(); + + // Normalize the attribute name, __foo__ becomes foo. + if (Len > 4 && Str[0] == '_' && Str[1] == '_' && + Str[Len - 2] == '_' && Str[Len - 1] == '_') { + Str += 2; + Len -= 4; + } + + switch (Len) { + case 4: + if (!memcmp(Str, "weak", 4)) return AT_weak; + if (!memcmp(Str, "pure", 4)) return AT_pure; + break; + case 6: + if (!memcmp(Str, "packed", 6)) return AT_packed; + if (!memcmp(Str, "malloc", 6)) return AT_malloc; + if (!memcmp(Str, "format", 6)) return AT_format; + if (!memcmp(Str, "unused", 6)) return AT_unused; + break; + case 7: + if (!memcmp(Str, "aligned", 7)) return AT_aligned; + if (!memcmp(Str, "nothrow", 7)) return AT_nothrow; + if (!memcmp(Str, "nonnull", 7)) return AT_nonnull; + if (!memcmp(Str, "stdcall", 7)) return AT_stdcall; + break; + case 8: + if (!memcmp(Str, "annotate", 8)) return AT_annotate; + if (!memcmp(Str, "noreturn", 8)) return AT_noreturn; + if (!memcmp(Str, "noinline", 8)) return AT_noinline; + if (!memcmp(Str, "fastcall", 8)) return AT_fastcall; + break; + case 9: + if (!memcmp(Str, "dllimport", 9)) return AT_dllimport; + if (!memcmp(Str, "dllexport", 9)) return AT_dllexport; + break; + case 10: + if (!memcmp(Str, "deprecated", 10)) return AT_deprecated; + if (!memcmp(Str, "visibility", 10)) return AT_visibility; + break; + case 11: + if (!memcmp(Str, "vector_size", 11)) return AT_vector_size; + break; + case 13: + if (!memcmp(Str, "address_space", 13)) return AT_address_space; + break; + case 15: + if (!memcmp(Str, "ocu_vector_type", 15)) return AT_ocu_vector_type; + break; + case 18: + if (!memcmp(Str, "warn_unused_result", 18)) return AT_warn_unused_result; + break; + } + return UnknownAttribute; +} diff --git a/clang/lib/Parse/DeclSpec.cpp b/clang/lib/Parse/DeclSpec.cpp new file mode 100644 index 00000000000..1cd350893f4 --- /dev/null +++ b/clang/lib/Parse/DeclSpec.cpp @@ -0,0 +1,287 @@ +//===--- SemaDeclSpec.cpp - Declaration Specifier Semantic Analysis -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for declaration specifiers. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/DeclSpec.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +using namespace clang; + +/// getParsedSpecifiers - Return a bitmask of which flavors of specifiers this +/// +unsigned DeclSpec::getParsedSpecifiers() const { + unsigned Res = 0; + if (StorageClassSpec != SCS_unspecified || + SCS_thread_specified) + Res |= PQ_StorageClassSpecifier; + + if (TypeQualifiers != TQ_unspecified) + Res |= PQ_TypeQualifier; + + if (hasTypeSpecifier()) + Res |= PQ_TypeSpecifier; + + if (FS_inline_specified) + Res |= PQ_FunctionSpecifier; + return Res; +} + +const char *DeclSpec::getSpecifierName(DeclSpec::SCS S) { + switch (S) { + default: assert(0 && "Unknown typespec!"); + case DeclSpec::SCS_unspecified: return "unspecified"; + case DeclSpec::SCS_typedef: return "typedef"; + case DeclSpec::SCS_extern: return "extern"; + case DeclSpec::SCS_static: return "static"; + case DeclSpec::SCS_auto: return "auto"; + case DeclSpec::SCS_register: return "register"; + } +} + +bool DeclSpec::BadSpecifier(SCS S, const char *&PrevSpec) { + PrevSpec = getSpecifierName(S); + return true; +} + +bool DeclSpec::BadSpecifier(TSW W, const char *&PrevSpec) { + switch (W) { + case TSW_unspecified: PrevSpec = "unspecified"; break; + case TSW_short: PrevSpec = "short"; break; + case TSW_long: PrevSpec = "long"; break; + case TSW_longlong: PrevSpec = "long long"; break; + } + return true; +} + +bool DeclSpec::BadSpecifier(TSC C, const char *&PrevSpec) { + switch (C) { + case TSC_unspecified: PrevSpec = "unspecified"; break; + case TSC_imaginary: PrevSpec = "imaginary"; break; + case TSC_complex: PrevSpec = "complex"; break; + } + return true; +} + + +bool DeclSpec::BadSpecifier(TSS S, const char *&PrevSpec) { + switch (S) { + case TSS_unspecified: PrevSpec = "unspecified"; break; + case TSS_signed: PrevSpec = "signed"; break; + case TSS_unsigned: PrevSpec = "unsigned"; break; + } + return true; +} + +const char *DeclSpec::getSpecifierName(DeclSpec::TST T) { + switch (T) { + default: assert(0 && "Unknown typespec!"); + case DeclSpec::TST_unspecified: return "unspecified"; + case DeclSpec::TST_void: return "void"; + case DeclSpec::TST_char: return "char"; + case DeclSpec::TST_int: return "int"; + case DeclSpec::TST_float: return "float"; + case DeclSpec::TST_double: return "double"; + case DeclSpec::TST_bool: return "_Bool"; + case DeclSpec::TST_decimal32: return "_Decimal32"; + case DeclSpec::TST_decimal64: return "_Decimal64"; + case DeclSpec::TST_decimal128: return "_Decimal128"; + case DeclSpec::TST_enum: return "enum"; + case DeclSpec::TST_union: return "union"; + case DeclSpec::TST_struct: return "struct"; + case DeclSpec::TST_typedef: return "typedef"; + case DeclSpec::TST_typeofType: + case DeclSpec::TST_typeofExpr: return "typeof"; + } +} + +bool DeclSpec::BadSpecifier(TST T, const char *&PrevSpec) { + PrevSpec = getSpecifierName(T); + return true; +} + +bool DeclSpec::BadSpecifier(TQ T, const char *&PrevSpec) { + switch (T) { + case DeclSpec::TQ_unspecified: PrevSpec = "unspecified"; break; + case DeclSpec::TQ_const: PrevSpec = "const"; break; + case DeclSpec::TQ_restrict: PrevSpec = "restrict"; break; + case DeclSpec::TQ_volatile: PrevSpec = "volatile"; break; + } + return true; +} + +bool DeclSpec::SetStorageClassSpec(SCS S, SourceLocation Loc, + const char *&PrevSpec) { + if (StorageClassSpec != SCS_unspecified) + return BadSpecifier( (SCS)StorageClassSpec, PrevSpec); + StorageClassSpec = S; + StorageClassSpecLoc = Loc; + return false; +} + +bool DeclSpec::SetStorageClassSpecThread(SourceLocation Loc, + const char *&PrevSpec) { + if (SCS_thread_specified) { + PrevSpec = "__thread"; + return true; + } + SCS_thread_specified = true; + SCS_threadLoc = Loc; + return false; +} + + +/// These methods set the specified attribute of the DeclSpec, but return true +/// and ignore the request if invalid (e.g. "extern" then "auto" is +/// specified). +bool DeclSpec::SetTypeSpecWidth(TSW W, SourceLocation Loc, + const char *&PrevSpec) { + if (TypeSpecWidth != TSW_unspecified && + // Allow turning long -> long long. + (W != TSW_longlong || TypeSpecWidth != TSW_long)) + return BadSpecifier( (TSW)TypeSpecWidth, PrevSpec); + TypeSpecWidth = W; + TSWLoc = Loc; + return false; +} + +bool DeclSpec::SetTypeSpecComplex(TSC C, SourceLocation Loc, + const char *&PrevSpec) { + if (TypeSpecComplex != TSC_unspecified) + return BadSpecifier( (TSC)TypeSpecComplex, PrevSpec); + TypeSpecComplex = C; + TSCLoc = Loc; + return false; +} + +bool DeclSpec::SetTypeSpecSign(TSS S, SourceLocation Loc, + const char *&PrevSpec) { + if (TypeSpecSign != TSS_unspecified) + return BadSpecifier( (TSS)TypeSpecSign, PrevSpec); + TypeSpecSign = S; + TSSLoc = Loc; + return false; +} + +bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, + const char *&PrevSpec, void *Rep) { + if (TypeSpecType != TST_unspecified) + return BadSpecifier( (TST)TypeSpecType, PrevSpec); + TypeSpecType = T; + TypeRep = Rep; + TSTLoc = Loc; + return false; +} + +bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec, + const LangOptions &Lang) { + // Duplicates turn into warnings pre-C99. + if ((TypeQualifiers & T) && !Lang.C99) + return BadSpecifier(T, PrevSpec); + TypeQualifiers |= T; + + switch (T) { + default: assert(0 && "Unknown type qualifier!"); + case TQ_const: TQ_constLoc = Loc; break; + case TQ_restrict: TQ_restrictLoc = Loc; break; + case TQ_volatile: TQ_volatileLoc = Loc; break; + } + return false; +} + +bool DeclSpec::SetFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec){ + // 'inline inline' is ok. + FS_inline_specified = true; + FS_inlineLoc = Loc; + return false; +} + + +/// Finish - This does final analysis of the declspec, rejecting things like +/// "_Imaginary" (lacking an FP type). This returns a diagnostic to issue or +/// diag::NUM_DIAGNOSTICS if there is no error. After calling this method, +/// DeclSpec is guaranteed self-consistent, even if an error occurred. +void DeclSpec::Finish(Diagnostic &D, SourceManager& SrcMgr, + const LangOptions &Lang) { + // Check the type specifier components first. + + // signed/unsigned are only valid with int/char. + if (TypeSpecSign != TSS_unspecified) { + if (TypeSpecType == TST_unspecified) + TypeSpecType = TST_int; // unsigned -> unsigned int, signed -> signed int. + else if (TypeSpecType != TST_int && TypeSpecType != TST_char) { + Diag(D, TSSLoc, SrcMgr, diag::err_invalid_sign_spec, + getSpecifierName( (TST)TypeSpecType)); + // signed double -> double. + TypeSpecSign = TSS_unspecified; + } + } + + // Validate the width of the type. + switch (TypeSpecWidth) { + case TSW_unspecified: break; + case TSW_short: // short int + case TSW_longlong: // long long int + if (TypeSpecType == TST_unspecified) + TypeSpecType = TST_int; // short -> short int, long long -> long long int. + else if (TypeSpecType != TST_int) { + Diag(D, TSWLoc, SrcMgr, + TypeSpecWidth == TSW_short ? diag::err_invalid_short_spec + : diag::err_invalid_longlong_spec, + getSpecifierName( (TST)TypeSpecType)); + TypeSpecType = TST_int; + } + break; + case TSW_long: // long double, long int + if (TypeSpecType == TST_unspecified) + TypeSpecType = TST_int; // long -> long int. + else if (TypeSpecType != TST_int && TypeSpecType != TST_double) { + Diag(D, TSWLoc, SrcMgr, diag::err_invalid_long_spec, + getSpecifierName( (TST)TypeSpecType)); + TypeSpecType = TST_int; + } + break; + } + + // TODO: if the implementation does not implement _Complex or _Imaginary, + // disallow their use. Need information about the backend. + if (TypeSpecComplex != TSC_unspecified) { + if (TypeSpecType == TST_unspecified) { + Diag(D, TSCLoc, SrcMgr, diag::ext_plain_complex); + TypeSpecType = TST_double; // _Complex -> _Complex double. + } else if (TypeSpecType == TST_int || TypeSpecType == TST_char) { + // Note that this intentionally doesn't include _Complex _Bool. + Diag(D, TSTLoc, SrcMgr, diag::ext_integer_complex); + } else if (TypeSpecType != TST_float && TypeSpecType != TST_double) { + Diag(D, TSCLoc, SrcMgr, diag::err_invalid_complex_spec, + getSpecifierName( (TST)TypeSpecType)); + TypeSpecComplex = TSC_unspecified; + } + } + + // Verify __thread. + if (SCS_thread_specified) { + if (StorageClassSpec == SCS_unspecified) { + StorageClassSpec = SCS_extern; // '__thread int' -> 'extern __thread int' + } else if (StorageClassSpec != SCS_extern && + StorageClassSpec != SCS_static) { + Diag(D, getStorageClassSpecLoc(), SrcMgr, diag::err_invalid_thread_spec, + getSpecifierName( (SCS)StorageClassSpec)); + SCS_thread_specified = false; + } + } + + // Okay, now we can infer the real type. + + // TODO: return "auto function" and other bad things based on the real type. + + // 'data definition has no type or storage class'? +} diff --git a/clang/lib/Parse/Makefile b/clang/lib/Parse/Makefile new file mode 100644 index 00000000000..b5d2653bb09 --- /dev/null +++ b/clang/lib/Parse/Makefile @@ -0,0 +1,22 @@ +##===- clang/lib/Parse/Makefile ----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements the Parser library for the C-Language front-end. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. +LIBRARYNAME := clangParse +BUILD_ARCHIVE = 1 +CXXFLAGS = -fno-rtti + +CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include + +include $(LEVEL)/Makefile.common + diff --git a/clang/lib/Parse/MinimalAction.cpp b/clang/lib/Parse/MinimalAction.cpp new file mode 100644 index 00000000000..250fa76ccc4 --- /dev/null +++ b/clang/lib/Parse/MinimalAction.cpp @@ -0,0 +1,136 @@ +//===--- MinimalAction.cpp - Implement the MinimalAction class ------------===// +// +// 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 MinimalAction interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Parse/DeclSpec.h" +#include "clang/Parse/Scope.h" +using namespace clang; + +/// TypeNameInfo - A link exists here for each scope that an identifier is +/// defined. +struct TypeNameInfo { + TypeNameInfo *Prev; + bool isTypeName; + + TypeNameInfo(bool istypename, TypeNameInfo *prev) { + isTypeName = istypename; + Prev = prev; + } +}; + +void MinimalAction:: ActOnTranslationUnitScope(SourceLocation Loc, Scope *S) { + TUScope = S; + IdentifierInfo *II; + TypeNameInfo *TI; + + // recognize the ObjC built-in type identifiers. + II = &Idents.get("id"); + TI = new TypeNameInfo(1, II->getFETokenInfo<TypeNameInfo>()); + II->setFETokenInfo(TI); + II = &Idents.get("SEL"); + TI = new TypeNameInfo(1, II->getFETokenInfo<TypeNameInfo>()); + II->setFETokenInfo(TI); + II = &Idents.get("Class"); + TI = new TypeNameInfo(1, II->getFETokenInfo<TypeNameInfo>()); + II->setFETokenInfo(TI); + II = &Idents.get("Protocol"); + TI = new TypeNameInfo(1, II->getFETokenInfo<TypeNameInfo>()); + II->setFETokenInfo(TI); +} + +/// isTypeName - This looks at the IdentifierInfo::FETokenInfo field to +/// determine whether the name is a type name (objc class name or typedef) or +/// not in this scope. +Action::DeclTy * +MinimalAction::isTypeName(const IdentifierInfo &II, Scope *S) const { + if (TypeNameInfo *TI = II.getFETokenInfo<TypeNameInfo>()) + if (TI->isTypeName) + return TI; + return 0; +} + +/// ActOnDeclarator - If this is a typedef declarator, we modify the +/// IdentifierInfo::FETokenInfo field to keep track of this fact, until S is +/// popped. +Action::DeclTy * +MinimalAction::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *LastInGroup) { + IdentifierInfo *II = D.getIdentifier(); + + // If there is no identifier associated with this declarator, bail out. + if (II == 0) return 0; + + TypeNameInfo *weCurrentlyHaveTypeInfo = II->getFETokenInfo<TypeNameInfo>(); + bool isTypeName = + D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef; + + // this check avoids creating TypeNameInfo objects for the common case. + // It does need to handle the uncommon case of shadowing a typedef name with a + // non-typedef name. e.g. { typedef int a; a xx; { int a; } } + if (weCurrentlyHaveTypeInfo || isTypeName) { + TypeNameInfo *TI = new TypeNameInfo(isTypeName, weCurrentlyHaveTypeInfo); + + II->setFETokenInfo(TI); + + // Remember that this needs to be removed when the scope is popped. + S->AddDecl(II); + } + return 0; +} + +Action::DeclTy * +MinimalAction::ActOnStartClassInterface(SourceLocation AtInterafceLoc, + IdentifierInfo *ClassName, SourceLocation ClassLoc, + IdentifierInfo *SuperName, SourceLocation SuperLoc, + IdentifierInfo **ProtocolNames, unsigned NumProtocols, + SourceLocation EndProtoLoc, AttributeList *AttrList) { + TypeNameInfo *TI = + new TypeNameInfo(1, ClassName->getFETokenInfo<TypeNameInfo>()); + + ClassName->setFETokenInfo(TI); + return 0; +} + +/// ActOnForwardClassDeclaration - +/// Scope will always be top level file scope. +Action::DeclTy * +MinimalAction::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, + IdentifierInfo **IdentList, unsigned NumElts) { + for (unsigned i = 0; i != NumElts; ++i) { + TypeNameInfo *TI = + new TypeNameInfo(1, IdentList[i]->getFETokenInfo<TypeNameInfo>()); + + IdentList[i]->setFETokenInfo(TI); + + // Remember that this needs to be removed when the scope is popped. + TUScope->AddDecl(IdentList[i]); + } + return 0; +} + +/// ActOnPopScope - When a scope is popped, if any typedefs are now out-of-scope, +/// they are removed from the IdentifierInfo::FETokenInfo field. +void MinimalAction::ActOnPopScope(SourceLocation Loc, Scope *S) { + for (Scope::decl_iterator I = S->decl_begin(), E = S->decl_end(); + I != E; ++I) { + IdentifierInfo &II = *static_cast<IdentifierInfo*>(*I); + TypeNameInfo *TI = II.getFETokenInfo<TypeNameInfo>(); + assert(TI && "This decl didn't get pushed??"); + + if (TI) { + TypeNameInfo *Next = TI->Prev; + delete TI; + + II.setFETokenInfo(Next); + } + } +} diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp new file mode 100644 index 00000000000..7d15e984ee6 --- /dev/null +++ b/clang/lib/Parse/ParseDecl.cpp @@ -0,0 +1,1540 @@ +//===--- 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/DeclSpec.h" +#include "clang/Parse/Scope.h" +#include "llvm/ADT/SmallSet.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// C99 6.7: Declarations. +//===----------------------------------------------------------------------===// + +/// ParseTypeName +/// type-name: [C99 6.7.6] +/// specifier-qualifier-list abstract-declarator[opt] +Parser::TypeTy *Parser::ParseTypeName() { + // Parse the common declaration-specifiers piece. + DeclSpec DS; + ParseSpecifierQualifierList(DS); + + // Parse the abstract-declarator, if present. + Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + ParseDeclarator(DeclaratorInfo); + + return Actions.ActOnTypeName(CurScope, DeclaratorInfo).Val; +} + +/// ParseAttributes - 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." +/// +/// 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. + +AttributeList *Parser::ParseAttributes() { + assert(Tok.is(tok::kw___attribute) && "Not an attribute list!"); + + AttributeList *CurrAttr = 0; + + 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 CurrAttr; + } + if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, "(")) { + SkipUntil(tok::r_paren, true); // skip until ) or ; + return CurrAttr; + } + // 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(); + + // check if we have a "paramterized" attribute + if (Tok.is(tok::l_paren)) { + ConsumeParen(); // ignore the left paren loc for now + + if (Tok.is(tok::identifier)) { + IdentifierInfo *ParmName = Tok.getIdentifierInfo(); + SourceLocation ParmLoc = ConsumeToken(); + + if (Tok.is(tok::r_paren)) { + // __attribute__(( mode(byte) )) + ConsumeParen(); // ignore the right paren loc for now + CurrAttr = new AttributeList(AttrName, AttrNameLoc, + ParmName, ParmLoc, 0, 0, CurrAttr); + } else if (Tok.is(tok::comma)) { + ConsumeToken(); + // __attribute__(( format(printf, 1, 2) )) + llvm::SmallVector<ExprTy*, 8> ArgExprs; + bool ArgExprsOk = true; + + // now parse the non-empty comma separated list of expressions + while (1) { + ExprResult ArgExpr = ParseAssignmentExpression(); + if (ArgExpr.isInvalid) { + ArgExprsOk = false; + SkipUntil(tok::r_paren); + break; + } else { + ArgExprs.push_back(ArgExpr.Val); + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + if (ArgExprsOk && Tok.is(tok::r_paren)) { + ConsumeParen(); // ignore the right paren loc for now + CurrAttr = new AttributeList(AttrName, AttrNameLoc, ParmName, + ParmLoc, &ArgExprs[0], ArgExprs.size(), CurrAttr); + } + } + } else { // not an identifier + // parse a possibly empty comma separated list of expressions + if (Tok.is(tok::r_paren)) { + // __attribute__(( nonnull() )) + ConsumeParen(); // ignore the right paren loc for now + CurrAttr = new AttributeList(AttrName, AttrNameLoc, + 0, SourceLocation(), 0, 0, CurrAttr); + } else { + // __attribute__(( aligned(16) )) + llvm::SmallVector<ExprTy*, 8> ArgExprs; + bool ArgExprsOk = true; + + // now parse the list of expressions + while (1) { + ExprResult ArgExpr = ParseAssignmentExpression(); + if (ArgExpr.isInvalid) { + ArgExprsOk = false; + SkipUntil(tok::r_paren); + break; + } else { + ArgExprs.push_back(ArgExpr.Val); + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + // Match the ')'. + if (ArgExprsOk && Tok.is(tok::r_paren)) { + ConsumeParen(); // ignore the right paren loc for now + CurrAttr = new AttributeList(AttrName, AttrNameLoc, 0, + SourceLocation(), &ArgExprs[0], ArgExprs.size(), + CurrAttr); + } + } + } + } else { + CurrAttr = new AttributeList(AttrName, AttrNameLoc, + 0, SourceLocation(), 0, 0, CurrAttr); + } + } + if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) + SkipUntil(tok::r_paren, false); + if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) + SkipUntil(tok::r_paren, false); + } + return CurrAttr; +} + +/// ParseDeclaration - Parse a full 'declaration', which consists of +/// declaration-specifiers, some number of declarators, and a semicolon. +/// 'Context' should be a Declarator::TheContext value. +/// +/// declaration: [C99 6.7] +/// block-declaration -> +/// simple-declaration +/// others [FIXME] +/// [C++] namespace-definition +/// others... [FIXME] +/// +Parser::DeclTy *Parser::ParseDeclaration(unsigned Context) { + switch (Tok.getKind()) { + case tok::kw_namespace: + return ParseNamespace(Context); + default: + return ParseSimpleDeclaration(Context); + } +} + +/// 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] +Parser::DeclTy *Parser::ParseSimpleDeclaration(unsigned Context) { + // Parse the common declaration-specifiers piece. + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + + // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" + // declaration-specifiers init-declarator-list[opt] ';' + if (Tok.is(tok::semi)) { + ConsumeToken(); + return Actions.ParsedFreeStandingDeclSpec(CurScope, DS); + } + + Declarator DeclaratorInfo(DS, (Declarator::TheContext)Context); + ParseDeclarator(DeclaratorInfo); + + return ParseInitDeclaratorListAfterFirstDeclarator(DeclaratorInfo); +} + + +/// ParseInitDeclaratorListAfterFirstDeclarator - Parse 'declaration' after +/// parsing 'declaration-specifiers declarator'. This method is split out this +/// way to handle the ambiguity between top-level function-definitions and +/// declarations. +/// +/// init-declarator-list: [C99 6.7] +/// init-declarator +/// init-declarator-list ',' init-declarator +/// 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 +/// +Parser::DeclTy *Parser:: +ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) { + + // Declarators may be grouped together ("int X, *Y, Z();"). Provide info so + // that they can be chained properly if the actions want this. + Parser::DeclTy *LastDeclInGroup = 0; + + // At this point, we know that it is not a function definition. Parse the + // rest of the init-declarator-list. + while (1) { + // If a simple-asm-expr is present, parse it. + if (Tok.is(tok::kw_asm)) + ParseSimpleAsm(); + + // If attributes are present, parse them. + if (Tok.is(tok::kw___attribute)) + D.AddAttributes(ParseAttributes()); + + // Inform the current actions module that we just parsed this declarator. + // FIXME: pass asm & attributes. + LastDeclInGroup = Actions.ActOnDeclarator(CurScope, D, LastDeclInGroup); + + // Parse declarator '=' initializer. + ExprResult Init; + if (Tok.is(tok::equal)) { + ConsumeToken(); + Init = ParseInitializer(); + if (Init.isInvalid) { + SkipUntil(tok::semi); + return 0; + } + Actions.AddInitializerToDecl(LastDeclInGroup, Init.Val); + } + + // If we don't have a comma, it is either the end of the list (a ';') or an + // error, bail out. + if (Tok.isNot(tok::comma)) + break; + + // Consume the comma. + ConsumeToken(); + + // Parse the next declarator. + D.clear(); + ParseDeclarator(D); + } + + if (Tok.is(tok::semi)) { + ConsumeToken(); + return Actions.FinalizeDeclaratorGroup(CurScope, LastDeclInGroup); + } + // If this is an ObjC2 for-each loop, this is a successful declarator + // parse. The syntax for these looks like: + // 'for' '(' declaration 'in' expr ')' statement + if (D.getContext() == Declarator::ForContext && isTokIdentifier_in()) { + return Actions.FinalizeDeclaratorGroup(CurScope, LastDeclInGroup); + } + Diag(Tok, diag::err_parse_error); + // Skip to end of block or statement + SkipUntil(tok::r_brace, true, true); + if (Tok.is(tok::semi)) + ConsumeToken(); + return 0; +} + +/// 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) { + /// specifier-qualifier-list is a subset of declaration-specifiers. Just + /// parse declaration-specifiers and complain about extra stuff. + ParseDeclarationSpecifiers(DS); + + // Validate declspec for type-name. + unsigned Specs = DS.getParsedSpecifiers(); + if (Specs == DeclSpec::PQ_None) + Diag(Tok, diag::err_typename_requires_specqual); + + // 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) { + Diag(DS.getInlineSpecLoc(), diag::err_typename_invalid_functionspec); + DS.ClearFunctionSpecs(); + } +} + +/// ParseDeclarationSpecifiers +/// declaration-specifiers: [C99 6.7] +/// storage-class-specifier declaration-specifiers[opt] +/// type-specifier declaration-specifiers[opt] +/// type-qualifier declaration-specifiers[opt] +/// [C99] function-specifier declaration-specifiers[opt] +/// [GNU] attributes declaration-specifiers[opt] +/// +/// storage-class-specifier: [C99 6.7.1] +/// 'typedef' +/// 'extern' +/// 'static' +/// 'auto' +/// 'register' +/// [GNU] '__thread' +/// type-specifier: [C99 6.7.2] +/// 'void' +/// 'char' +/// 'short' +/// 'int' +/// 'long' +/// 'float' +/// 'double' +/// 'signed' +/// 'unsigned' +/// struct-or-union-specifier +/// enum-specifier +/// typedef-name +/// [C++] 'bool' +/// [C99] '_Bool' +/// [C99] '_Complex' +/// [C99] '_Imaginary' // Removed in TC2? +/// [GNU] '_Decimal32' +/// [GNU] '_Decimal64' +/// [GNU] '_Decimal128' +/// [GNU] typeof-specifier +/// [OBJC] class-name objc-protocol-refs[opt] [TODO] +/// [OBJC] typedef-name objc-protocol-refs[opt] [TODO] +/// type-qualifier: +/// 'const' +/// 'volatile' +/// [C99] 'restrict' +/// function-specifier: [C99 6.7.4] +/// [C99] 'inline' +/// +void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) { + DS.SetRangeStart(Tok.getLocation()); + while (1) { + int isInvalid = false; + const char *PrevSpec = 0; + SourceLocation Loc = Tok.getLocation(); + + switch (Tok.getKind()) { + // typedef-name + case tok::identifier: + // 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()) { + // It has to be available as a typedef too! + if (void *TypeRep = Actions.isTypeName(*Tok.getIdentifierInfo(), + CurScope)) { + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typedef, Loc, PrevSpec, + TypeRep); + if (isInvalid) + break; + // FIXME: restrict this to "id" and ObjC classnames. + DS.SetRangeEnd(Tok.getLocation()); + ConsumeToken(); // The identifier + if (Tok.is(tok::less)) { + SourceLocation endProtoLoc; + llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs; + ParseObjCProtocolReferences(ProtocolRefs, endProtoLoc); + llvm::SmallVector<DeclTy *, 8> *ProtocolDecl = + new llvm::SmallVector<DeclTy *, 8>; + DS.setProtocolQualifiers(ProtocolDecl); + Actions.FindProtocolDeclaration(Loc, + &ProtocolRefs[0], ProtocolRefs.size(), + *ProtocolDecl); + } + continue; + } + } + // FALL THROUGH. + default: + // 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.getSourceManager(), getLang()); + return; + + // GNU attributes support. + case tok::kw___attribute: + DS.AddAttributes(ParseAttributes()); + continue; + + // storage-class-specifier + case tok::kw_typedef: + isInvalid = DS.SetStorageClassSpec(DeclSpec::SCS_typedef, Loc, PrevSpec); + break; + case tok::kw_extern: + if (DS.isThreadSpecified()) + Diag(Tok, diag::ext_thread_before, "extern"); + isInvalid = DS.SetStorageClassSpec(DeclSpec::SCS_extern, Loc, PrevSpec); + break; + case tok::kw___private_extern__: + isInvalid = DS.SetStorageClassSpec(DeclSpec::SCS_private_extern, Loc, PrevSpec); + break; + case tok::kw_static: + if (DS.isThreadSpecified()) + Diag(Tok, diag::ext_thread_before, "static"); + isInvalid = DS.SetStorageClassSpec(DeclSpec::SCS_static, Loc, PrevSpec); + break; + case tok::kw_auto: + isInvalid = DS.SetStorageClassSpec(DeclSpec::SCS_auto, Loc, PrevSpec); + break; + case tok::kw_register: + isInvalid = DS.SetStorageClassSpec(DeclSpec::SCS_register, Loc, PrevSpec); + break; + case tok::kw___thread: + isInvalid = DS.SetStorageClassSpecThread(Loc, PrevSpec)*2; + break; + + // type-specifiers + case tok::kw_short: + isInvalid = DS.SetTypeSpecWidth(DeclSpec::TSW_short, Loc, PrevSpec); + break; + case tok::kw_long: + if (DS.getTypeSpecWidth() != DeclSpec::TSW_long) + isInvalid = DS.SetTypeSpecWidth(DeclSpec::TSW_long, Loc, PrevSpec); + else + isInvalid = DS.SetTypeSpecWidth(DeclSpec::TSW_longlong, Loc, PrevSpec); + break; + case tok::kw_signed: + isInvalid = DS.SetTypeSpecSign(DeclSpec::TSS_signed, Loc, PrevSpec); + break; + case tok::kw_unsigned: + isInvalid = DS.SetTypeSpecSign(DeclSpec::TSS_unsigned, Loc, PrevSpec); + break; + case tok::kw__Complex: + isInvalid = DS.SetTypeSpecComplex(DeclSpec::TSC_complex, Loc, PrevSpec); + break; + case tok::kw__Imaginary: + isInvalid = DS.SetTypeSpecComplex(DeclSpec::TSC_imaginary, Loc, PrevSpec); + break; + case tok::kw_void: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_void, Loc, PrevSpec); + break; + case tok::kw_char: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char, Loc, PrevSpec); + break; + case tok::kw_int: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec); + break; + case tok::kw_float: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float, Loc, PrevSpec); + break; + case tok::kw_double: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_double, Loc, PrevSpec); + break; + case tok::kw_bool: // [C++ 2.11p1] + case tok::kw__Bool: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_bool, Loc, PrevSpec); + break; + case tok::kw__Decimal32: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal32, Loc, PrevSpec); + break; + case tok::kw__Decimal64: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal64, Loc, PrevSpec); + break; + case tok::kw__Decimal128: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_decimal128, Loc, PrevSpec); + break; + + case tok::kw_struct: + case tok::kw_union: + ParseStructUnionSpecifier(DS); + continue; + case tok::kw_enum: + ParseEnumSpecifier(DS); + continue; + + // GNU typeof support. + case tok::kw_typeof: + ParseTypeofSpecifier(DS); + continue; + + // type-qualifier + case tok::kw_const: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_const , Loc, PrevSpec, + getLang())*2; + break; + case tok::kw_volatile: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_volatile, Loc, PrevSpec, + getLang())*2; + break; + case tok::kw_restrict: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, + getLang())*2; + break; + + // function-specifier + case tok::kw_inline: + isInvalid = DS.SetFunctionSpecInline(Loc, PrevSpec); + break; + } + // If the specifier combination wasn't legal, issue a diagnostic. + if (isInvalid) { + assert(PrevSpec && "Method did not return previous specifier!"); + if (isInvalid == 1) // Error. + Diag(Tok, diag::err_invalid_decl_spec_combination, PrevSpec); + else // extwarn. + Diag(Tok, diag::ext_duplicate_declspec, PrevSpec); + } + DS.SetRangeEnd(Tok.getLocation()); + ConsumeToken(); + } +} + +/// ParseTag - Parse "struct-or-union-or-class-or-enum identifier[opt]", where +/// the first token has already been read and has been turned into an instance +/// of DeclSpec::TST (TagType). This returns true if there is an error parsing, +/// otherwise it returns false and fills in Decl. +bool Parser::ParseTag(DeclTy *&Decl, unsigned TagType, SourceLocation StartLoc){ + AttributeList *Attr = 0; + // If attributes exist after tag, parse them. + if (Tok.is(tok::kw___attribute)) + Attr = ParseAttributes(); + + // Must have either 'struct name' or 'struct {...}'. + if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace)) { + Diag(Tok, diag::err_expected_ident_lbrace); + + // Skip the rest of this declarator, up until the comma or semicolon. + SkipUntil(tok::comma, true); + return true; + } + + // If an identifier is present, consume and remember it. + IdentifierInfo *Name = 0; + SourceLocation NameLoc; + if (Tok.is(tok::identifier)) { + Name = Tok.getIdentifierInfo(); + NameLoc = ConsumeToken(); + } + + // There are three options here. If we have 'struct foo;', then this is a + // forward declaration. If we have 'struct foo {...' then this is a + // definition. Otherwise we have something like 'struct foo xyz', a reference. + // + // This is needed to handle stuff like this right (C99 6.7.2.3p11): + // struct foo {..}; void bar() { struct foo; } <- new foo in bar. + // struct foo {..}; void bar() { struct foo x; } <- use of old foo. + // + Action::TagKind TK; + if (Tok.is(tok::l_brace)) + TK = Action::TK_Definition; + else if (Tok.is(tok::semi)) + TK = Action::TK_Declaration; + else + TK = Action::TK_Reference; + Decl = Actions.ActOnTag(CurScope, TagType, TK, StartLoc, Name, NameLoc, Attr); + return false; +} + + +/// ParseStructUnionSpecifier +/// struct-or-union-specifier: [C99 6.7.2.1] +/// struct-or-union identifier[opt] '{' struct-contents '}' +/// struct-or-union identifier +/// [GNU] struct-or-union attributes[opt] identifier[opt] '{' struct-contents +/// '}' attributes[opt] +/// [GNU] struct-or-union attributes[opt] identifier +/// struct-or-union: +/// 'struct' +/// 'union' +/// +void Parser::ParseStructUnionSpecifier(DeclSpec &DS) { + assert((Tok.is(tok::kw_struct) || Tok.is(tok::kw_union)) && + "Not a struct/union specifier"); + DeclSpec::TST TagType = + Tok.is(tok::kw_union) ? DeclSpec::TST_union : DeclSpec::TST_struct; + SourceLocation StartLoc = ConsumeToken(); + + // Parse the tag portion of this. + DeclTy *TagDecl; + if (ParseTag(TagDecl, TagType, StartLoc)) + return; + + // If there is a body, parse it and inform the actions module. + if (Tok.is(tok::l_brace)) + ParseStructUnionBody(StartLoc, TagType, TagDecl); + + const char *PrevSpec = 0; + if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, TagDecl)) + Diag(StartLoc, diag::err_invalid_decl_spec_combination, PrevSpec); +} + +/// 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(DeclTy *TagDecl, + llvm::SmallVectorImpl<DeclTy*> &FieldDecls) { + // FIXME: When __extension__ is specified, disable extension diagnostics. + if (Tok.is(tok::kw___extension__)) + ConsumeToken(); + + // Parse the common specifier-qualifiers-list piece. + DeclSpec DS; + SourceLocation SpecQualLoc = Tok.getLocation(); + ParseSpecifierQualifierList(DS); + // TODO: Does specifier-qualifier list correctly check that *something* is + // specified? + + // If there are no declarators, issue a warning. + if (Tok.is(tok::semi)) { + Diag(SpecQualLoc, diag::w_no_declarators); + return; + } + + // Read struct-declarators until we find the semicolon. + Declarator DeclaratorInfo(DS, Declarator::MemberContext); + + while (1) { + /// struct-declarator: declarator + /// struct-declarator: declarator[opt] ':' constant-expression + if (Tok.isNot(tok::colon)) + ParseDeclarator(DeclaratorInfo); + + ExprTy *BitfieldSize = 0; + if (Tok.is(tok::colon)) { + ConsumeToken(); + ExprResult Res = ParseConstantExpression(); + if (Res.isInvalid) { + SkipUntil(tok::semi, true, true); + } else { + BitfieldSize = Res.Val; + } + } + + // If attributes exist after the declarator, parse them. + if (Tok.is(tok::kw___attribute)) + DeclaratorInfo.AddAttributes(ParseAttributes()); + + // Install the declarator into the current TagDecl. + DeclTy *Field = Actions.ActOnField(CurScope, TagDecl, SpecQualLoc, + DeclaratorInfo, BitfieldSize); + FieldDecls.push_back(Field); + + // 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. + ConsumeToken(); + + // Parse the next declarator. + DeclaratorInfo.clear(); + + // Attributes are only allowed on the second declarator. + if (Tok.is(tok::kw___attribute)) + DeclaratorInfo.AddAttributes(ParseAttributes()); + } +} + +/// 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 ')' [TODO] +/// +void Parser::ParseStructUnionBody(SourceLocation RecordLoc, + unsigned TagType, DeclTy *TagDecl) { + SourceLocation LBraceLoc = ConsumeBrace(); + + // Empty structs are an extension in C (C99 6.7.2.1p7), but are allowed in + // C++. + if (Tok.is(tok::r_brace)) + Diag(Tok, diag::ext_empty_struct_union_enum, + DeclSpec::getSpecifierName((DeclSpec::TST)TagType)); + + llvm::SmallVector<DeclTy*, 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); + ConsumeToken(); + continue; + } + ParseStructDeclaration(TagDecl, FieldDecls); + + if (Tok.is(tok::semi)) { + ConsumeToken(); + } else if (Tok.is(tok::r_brace)) { + Diag(Tok.getLocation(), diag::ext_expected_semi_decl_list); + break; + } else { + Diag(Tok, diag::err_expected_semi_decl_list); + // Skip to end of block or statement + SkipUntil(tok::r_brace, true, true); + } + } + + SourceLocation RBraceLoc = MatchRHSPunctuation(tok::r_brace, LBraceLoc); + + Actions.ActOnFields(CurScope, + RecordLoc,TagDecl,&FieldDecls[0],FieldDecls.size(), + LBraceLoc, RBraceLoc); + + AttributeList *AttrList = 0; + // If attributes exist after struct contents, parse them. + if (Tok.is(tok::kw___attribute)) + AttrList = ParseAttributes(); // FIXME: where should I put them? +} + + +/// ParseEnumSpecifier +/// enum-specifier: [C99 6.7.2.2] +/// 'enum' identifier[opt] '{' enumerator-list '}' +/// [C99] 'enum' identifier[opt] '{' enumerator-list ',' '}' +/// [GNU] 'enum' attributes[opt] identifier[opt] '{' enumerator-list ',' [opt] +/// '}' attributes[opt] +/// 'enum' identifier +/// [GNU] 'enum' attributes[opt] identifier +void Parser::ParseEnumSpecifier(DeclSpec &DS) { + assert(Tok.is(tok::kw_enum) && "Not an enum specifier"); + SourceLocation StartLoc = ConsumeToken(); + + // Parse the tag portion of this. + DeclTy *TagDecl; + if (ParseTag(TagDecl, DeclSpec::TST_enum, StartLoc)) + return; + + if (Tok.is(tok::l_brace)) + ParseEnumBody(StartLoc, TagDecl); + + // TODO: semantic analysis on the declspec for enums. + const char *PrevSpec = 0; + if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, PrevSpec, TagDecl)) + Diag(StartLoc, diag::err_invalid_decl_spec_combination, PrevSpec); +} + +/// ParseEnumBody - Parse a {} enclosed enumerator-list. +/// enumerator-list: +/// enumerator +/// enumerator-list ',' enumerator +/// enumerator: +/// enumeration-constant +/// enumeration-constant '=' constant-expression +/// enumeration-constant: +/// identifier +/// +void Parser::ParseEnumBody(SourceLocation StartLoc, DeclTy *EnumDecl) { + SourceLocation LBraceLoc = ConsumeBrace(); + + // C does not allow an empty enumerator-list, C++ does [dcl.enum]. + if (Tok.is(tok::r_brace) && !getLang().CPlusPlus) + Diag(Tok, diag::ext_empty_struct_union_enum, "enum"); + + llvm::SmallVector<DeclTy*, 32> EnumConstantDecls; + + DeclTy *LastEnumConstDecl = 0; + + // Parse the enumerator-list. + while (Tok.is(tok::identifier)) { + IdentifierInfo *Ident = Tok.getIdentifierInfo(); + SourceLocation IdentLoc = ConsumeToken(); + + SourceLocation EqualLoc; + ExprTy *AssignedVal = 0; + if (Tok.is(tok::equal)) { + EqualLoc = ConsumeToken(); + ExprResult Res = ParseConstantExpression(); + if (Res.isInvalid) + SkipUntil(tok::comma, tok::r_brace, true, true); + else + AssignedVal = Res.Val; + } + + // Install the enumerator constant into EnumDecl. + DeclTy *EnumConstDecl = Actions.ActOnEnumConstant(CurScope, EnumDecl, + LastEnumConstDecl, + IdentLoc, Ident, + EqualLoc, AssignedVal); + EnumConstantDecls.push_back(EnumConstDecl); + LastEnumConstDecl = EnumConstDecl; + + if (Tok.isNot(tok::comma)) + break; + SourceLocation CommaLoc = ConsumeToken(); + + if (Tok.isNot(tok::identifier) && !getLang().C99) + Diag(CommaLoc, diag::ext_c99_enumerator_list_comma); + } + + // Eat the }. + MatchRHSPunctuation(tok::r_brace, LBraceLoc); + + Actions.ActOnEnumBody(StartLoc, EnumDecl, &EnumConstantDecls[0], + EnumConstantDecls.size()); + + DeclTy *AttrList = 0; + // If attributes exist after the identifier list, parse them. + if (Tok.is(tok::kw___attribute)) + AttrList = ParseAttributes(); // FIXME: where do they do? +} + +/// isTypeSpecifierQualifier - Return true if the current token could be the +/// start of a type-qualifier-list. +bool Parser::isTypeQualifier() const { + switch (Tok.getKind()) { + default: return false; + // type-qualifier + case tok::kw_const: + case tok::kw_volatile: + case tok::kw_restrict: + return true; + } +} + +/// isTypeSpecifierQualifier - Return true if the current token could be the +/// start of a specifier-qualifier-list. +bool Parser::isTypeSpecifierQualifier() const { + switch (Tok.getKind()) { + default: return false; + // GNU attributes support. + case tok::kw___attribute: + // GNU typeof support. + case tok::kw_typeof: + + // type-specifiers + case tok::kw_short: + case tok::kw_long: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw__Complex: + case tok::kw__Imaginary: + case tok::kw_void: + case tok::kw_char: + case tok::kw_int: + case tok::kw_float: + case tok::kw_double: + case tok::kw_bool: + case tok::kw__Bool: + case tok::kw__Decimal32: + case tok::kw__Decimal64: + case tok::kw__Decimal128: + + // struct-or-union-specifier + case tok::kw_struct: + case tok::kw_union: + // enum-specifier + case tok::kw_enum: + + // type-qualifier + case tok::kw_const: + case tok::kw_volatile: + case tok::kw_restrict: + return true; + + // typedef-name + case tok::identifier: + return Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope) != 0; + } +} + +/// isDeclarationSpecifier() - Return true if the current token is part of a +/// declaration specifier. +bool Parser::isDeclarationSpecifier() const { + switch (Tok.getKind()) { + default: return false; + // storage-class-specifier + case tok::kw_typedef: + case tok::kw_extern: + case tok::kw___private_extern__: + case tok::kw_static: + case tok::kw_auto: + case tok::kw_register: + case tok::kw___thread: + + // type-specifiers + case tok::kw_short: + case tok::kw_long: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw__Complex: + case tok::kw__Imaginary: + case tok::kw_void: + case tok::kw_char: + case tok::kw_int: + case tok::kw_float: + case tok::kw_double: + case tok::kw_bool: + case tok::kw__Bool: + case tok::kw__Decimal32: + case tok::kw__Decimal64: + case tok::kw__Decimal128: + + // struct-or-union-specifier + case tok::kw_struct: + case tok::kw_union: + // enum-specifier + case tok::kw_enum: + + // type-qualifier + case tok::kw_const: + case tok::kw_volatile: + case tok::kw_restrict: + + // function-specifier + case tok::kw_inline: + + // GNU typeof support. + case tok::kw_typeof: + + // GNU attributes. + case tok::kw___attribute: + return true; + + // typedef-name + case tok::identifier: + return Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope) != 0; + } +} + + +/// ParseTypeQualifierListOpt +/// type-qualifier-list: [C99 6.7.5] +/// type-qualifier +/// [GNU] attributes +/// type-qualifier-list type-qualifier +/// [GNU] type-qualifier-list attributes +/// +void Parser::ParseTypeQualifierListOpt(DeclSpec &DS) { + while (1) { + int isInvalid = false; + const char *PrevSpec = 0; + SourceLocation Loc = Tok.getLocation(); + + switch (Tok.getKind()) { + default: + // If this is not a type-qualifier token, we're done reading type + // qualifiers. First verify that DeclSpec's are consistent. + DS.Finish(Diags, PP.getSourceManager(), getLang()); + return; + case tok::kw_const: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_const , Loc, PrevSpec, + getLang())*2; + break; + case tok::kw_volatile: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_volatile, Loc, PrevSpec, + getLang())*2; + break; + case tok::kw_restrict: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, + getLang())*2; + break; + case tok::kw___attribute: + DS.AddAttributes(ParseAttributes()); + continue; // do *not* consume the next token! + } + + // If the specifier combination wasn't legal, issue a diagnostic. + if (isInvalid) { + assert(PrevSpec && "Method did not return previous specifier!"); + if (isInvalid == 1) // Error. + Diag(Tok, diag::err_invalid_decl_spec_combination, PrevSpec); + else // extwarn. + Diag(Tok, diag::ext_duplicate_declspec, PrevSpec); + } + ConsumeToken(); + } +} + + +/// ParseDeclarator - Parse and verify a newly-initialized declarator. +/// +void Parser::ParseDeclarator(Declarator &D) { + /// This implements the 'declarator' production in the C grammar, then checks + /// for well-formedness and issues diagnostics. + ParseDeclaratorInternal(D); + + // TODO: validate D. + +} + +/// ParseDeclaratorInternal +/// declarator: [C99 6.7.5] +/// pointer[opt] direct-declarator +/// [C++] '&' declarator [C++ 8p4, dcl.decl] +/// [GNU] '&' restrict[opt] attributes[opt] declarator +/// +/// pointer: [C99 6.7.5] +/// '*' type-qualifier-list[opt] +/// '*' type-qualifier-list[opt] pointer +/// +void Parser::ParseDeclaratorInternal(Declarator &D) { + tok::TokenKind Kind = Tok.getKind(); + + // Not a pointer or C++ reference. + if (Kind != tok::star && (Kind != tok::amp || !getLang().CPlusPlus)) + return ParseDirectDeclarator(D); + + // Otherwise, '*' -> pointer or '&' -> reference. + SourceLocation Loc = ConsumeToken(); // Eat the * or &. + + if (Kind == tok::star) { + // Is a pointer. + DeclSpec DS; + + ParseTypeQualifierListOpt(DS); + + // Recursively parse the declarator. + ParseDeclaratorInternal(D); + + // Remember that we parsed a pointer type, and remember the type-quals. + D.AddTypeInfo(DeclaratorChunk::getPointer(DS.getTypeQualifiers(), Loc, + DS.TakeAttributes())); + } else { + // Is a reference + DeclSpec DS; + + // C++ 8.3.2p1: cv-qualified references are ill-formed except when the + // cv-qualifiers are introduced through the use of a typedef or of a + // template type argument, in which case the cv-qualifiers are ignored. + // + // [GNU] Retricted references are allowed. + // [GNU] Attributes on references are allowed. + ParseTypeQualifierListOpt(DS); + + if (DS.getTypeQualifiers() != DeclSpec::TQ_unspecified) { + if (DS.getTypeQualifiers() & DeclSpec::TQ_const) + Diag(DS.getConstSpecLoc(), + diag::err_invalid_reference_qualifier_application, + "const"); + if (DS.getTypeQualifiers() & DeclSpec::TQ_volatile) + Diag(DS.getVolatileSpecLoc(), + diag::err_invalid_reference_qualifier_application, + "volatile"); + } + + // Recursively parse the declarator. + ParseDeclaratorInternal(D); + + // Remember that we parsed a reference type. It doesn't have type-quals. + D.AddTypeInfo(DeclaratorChunk::getReference(DS.getTypeQualifiers(), Loc, + DS.TakeAttributes())); + } +} + +/// ParseDirectDeclarator +/// direct-declarator: [C99 6.7.5] +/// identifier +/// '(' declarator ')' +/// [GNU] '(' attributes declarator ')' +/// [C90] direct-declarator '[' constant-expression[opt] ']' +/// [C99] direct-declarator '[' type-qual-list[opt] assignment-expr[opt] ']' +/// [C99] direct-declarator '[' 'static' type-qual-list[opt] assign-expr ']' +/// [C99] direct-declarator '[' type-qual-list 'static' assignment-expr ']' +/// [C99] direct-declarator '[' type-qual-list[opt] '*' ']' +/// direct-declarator '(' parameter-type-list ')' +/// direct-declarator '(' identifier-list[opt] ')' +/// [GNU] direct-declarator '(' parameter-forward-declarations +/// parameter-type-list[opt] ')' +/// +void Parser::ParseDirectDeclarator(Declarator &D) { + // Parse the first direct-declarator seen. + if (Tok.is(tok::identifier) && D.mayHaveIdentifier()) { + assert(Tok.getIdentifierInfo() && "Not an identifier?"); + D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); + ConsumeToken(); + } else if (Tok.is(tok::l_paren)) { + // direct-declarator: '(' declarator ')' + // direct-declarator: '(' attributes declarator ')' + // Example: 'char (*X)' or 'int (*XX)(void)' + ParseParenDeclarator(D); + } else if (D.mayOmitIdentifier()) { + // This could be something simple like "int" (in which case the declarator + // portion is empty), if an abstract-declarator is allowed. + D.SetIdentifier(0, Tok.getLocation()); + } else { + // Expected identifier or '('. + Diag(Tok, diag::err_expected_ident_lparen); + D.SetIdentifier(0, Tok.getLocation()); + } + + assert(D.isPastIdentifier() && + "Haven't past the location of the identifier yet?"); + + while (1) { + if (Tok.is(tok::l_paren)) { + ParseParenDeclarator(D); + } else if (Tok.is(tok::l_square)) { + ParseBracketDeclarator(D); + } else { + break; + } + } +} + +/// ParseParenDeclarator - We parsed the declarator D up to a paren. This may +/// either be before the identifier (in which case these are just grouping +/// parens for precedence) or it may be after the identifier, in which case +/// these are function arguments. +/// +/// This method also handles this portion of the grammar: +/// parameter-type-list: [C99 6.7.5] +/// parameter-list +/// parameter-list ',' '...' +/// +/// parameter-list: [C99 6.7.5] +/// parameter-declaration +/// parameter-list ',' parameter-declaration +/// +/// parameter-declaration: [C99 6.7.5] +/// declaration-specifiers declarator +/// [GNU] declaration-specifiers declarator attributes +/// declaration-specifiers abstract-declarator[opt] +/// [GNU] declaration-specifiers abstract-declarator[opt] attributes +/// +/// identifier-list: [C99 6.7.5] +/// identifier +/// identifier-list ',' identifier +/// +void Parser::ParseParenDeclarator(Declarator &D) { + SourceLocation StartLoc = ConsumeParen(); + + // If we haven't past the identifier yet (or where the identifier would be + // stored, if this is an abstract declarator), then this is probably just + // grouping parens. + if (!D.isPastIdentifier()) { + // Okay, this is probably a grouping paren. However, if this could be an + // abstract-declarator, then this could also be the start of function + // arguments (consider 'void()'). + bool isGrouping; + + if (!D.mayOmitIdentifier()) { + // If this can't be an abstract-declarator, this *must* be a grouping + // paren, because we haven't seen the identifier yet. + isGrouping = true; + } else if (Tok.is(tok::r_paren) || // 'int()' is a function. + isDeclarationSpecifier()) { // 'int(int)' is a function. + // This handles C99 6.7.5.3p11: in "typedef int X; void foo(X)", X is + // considered to be a type, not a K&R identifier-list. + isGrouping = false; + } else { + // Otherwise, this is a grouping paren, e.g. 'int (*X)' or 'int(X)'. + isGrouping = true; + } + + // If this is a grouping paren, handle: + // direct-declarator: '(' declarator ')' + // direct-declarator: '(' attributes declarator ')' + if (isGrouping) { + if (Tok.is(tok::kw___attribute)) + D.AddAttributes(ParseAttributes()); + + ParseDeclaratorInternal(D); + // Match the ')'. + MatchRHSPunctuation(tok::r_paren, StartLoc); + return; + } + + // Okay, if this wasn't a grouping paren, it must be the start of a function + // argument list. Recognize that this declarator will never have an + // identifier (and remember where it would have been), then fall through to + // the handling of argument lists. + D.SetIdentifier(0, Tok.getLocation()); + } + + // Okay, this is the parameter list of a function definition, or it is an + // identifier list of a K&R-style function. + bool IsVariadic; + bool HasPrototype; + bool ErrorEmitted = false; + + // Build up an array of information about the parsed arguments. + llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo; + llvm::SmallSet<const IdentifierInfo*, 16> ParamsSoFar; + + if (Tok.is(tok::r_paren)) { + // int() -> no prototype, no '...'. + IsVariadic = false; + HasPrototype = false; + } else if (Tok.is(tok::identifier) && + // K&R identifier lists can't have typedefs as identifiers, per + // C99 6.7.5.3p11. + !Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope)) { + // Identifier list. Note that '(' identifier-list ')' is only allowed for + // normal declarators, not for abstract-declarators. + assert(D.isPastIdentifier() && "Identifier (if present) must be passed!"); + + // If there was no identifier specified, either we are in an + // abstract-declarator, or we are in a parameter declarator which was found + // to be abstract. In abstract-declarators, identifier lists are not valid, + // diagnose this. + if (!D.getIdentifier()) + Diag(Tok, diag::ext_ident_list_in_param); + + // Remember this identifier in ParamInfo. + ParamInfo.push_back(DeclaratorChunk::ParamInfo(Tok.getIdentifierInfo(), + Tok.getLocation(), 0)); + + ConsumeToken(); + while (Tok.is(tok::comma)) { + // Eat the comma. + ConsumeToken(); + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + ErrorEmitted = true; + break; + } + + IdentifierInfo *ParmII = Tok.getIdentifierInfo(); + + // Verify that the argument identifier has not already been mentioned. + if (!ParamsSoFar.insert(ParmII)) { + Diag(Tok.getLocation(), diag::err_param_redefinition,ParmII->getName()); + ParmII = 0; + } + + // Remember this identifier in ParamInfo. + if (ParmII) + ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, + Tok.getLocation(), 0)); + + // Eat the identifier. + ConsumeToken(); + } + + // K&R 'prototype'. + IsVariadic = false; + HasPrototype = false; + } else { + // Finally, a normal, non-empty parameter type list. + + // Enter function-declaration scope, limiting any declarators for struct + // tags to the function prototype scope. + // FIXME: is this needed? + EnterScope(Scope::DeclScope); + + IsVariadic = false; + while (1) { + if (Tok.is(tok::ellipsis)) { + IsVariadic = true; + + // Check to see if this is "void(...)" which is not allowed. + if (ParamInfo.empty()) { + // Otherwise, parse parameter type list. If it starts with an + // ellipsis, diagnose the malformed function. + Diag(Tok, diag::err_ellipsis_first_arg); + IsVariadic = false; // Treat this like 'void()'. + } + + // Consume the ellipsis. + ConsumeToken(); + break; + } + + SourceLocation DSStart = Tok.getLocation(); + + // Parse the declaration-specifiers. + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + + // Parse the declarator. This is "PrototypeContext", because we must + // accept either 'declarator' or 'abstract-declarator' here. + Declarator ParmDecl(DS, Declarator::PrototypeContext); + ParseDeclarator(ParmDecl); + + // Parse GNU attributes, if present. + if (Tok.is(tok::kw___attribute)) + ParmDecl.AddAttributes(ParseAttributes()); + + // Verify C99 6.7.5.3p2: The only SCS allowed is 'register'. + // NOTE: we could trivially allow 'int foo(auto int X)' if we wanted. + if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified && + DS.getStorageClassSpec() != DeclSpec::SCS_register) { + Diag(DS.getStorageClassSpecLoc(), + diag::err_invalid_storage_class_in_func_decl); + DS.ClearStorageClassSpecs(); + } + if (DS.isThreadSpecified()) { + Diag(DS.getThreadSpecLoc(), + diag::err_invalid_storage_class_in_func_decl); + DS.ClearStorageClassSpecs(); + } + + // Inform the actions module about the parameter declarator, so it gets + // added to the current scope. + Action::TypeResult ParamTy = + Actions.ActOnParamDeclaratorType(CurScope, ParmDecl); + + // Remember this parsed parameter in ParamInfo. + IdentifierInfo *ParmII = ParmDecl.getIdentifier(); + + // Verify that the argument identifier has not already been mentioned. + if (ParmII && !ParamsSoFar.insert(ParmII)) { + Diag(ParmDecl.getIdentifierLoc(), diag::err_param_redefinition, + ParmII->getName()); + ParmII = 0; + } + + // If no parameter was specified, verify that *something* was specified, + // otherwise we have a missing type and identifier. + if (DS.getParsedSpecifiers() == DeclSpec::PQ_None && + ParmDecl.getIdentifier() == 0 && ParmDecl.getNumTypeObjects() == 0) { + Diag(DSStart, diag::err_missing_param); + } else if (!DS.hasTypeSpecifier() && + (getLang().C99 || getLang().CPlusPlus)) { + // Otherwise, if something was specified but a type specifier wasn't, + // (e.g. "x" or "restrict x" or "restrict"), this is a use of implicit + // int. This is valid in C90, but not in C99 or C++. + if (ParmII) + Diag(ParmDecl.getIdentifierLoc(), + diag::ext_param_requires_type_specifier, ParmII->getName()); + else + Diag(DSStart, diag::ext_anon_param_requires_type_specifier); + } + + ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, + ParmDecl.getIdentifierLoc(), ParamTy.Val, ParmDecl.getInvalidType(), + ParmDecl.getDeclSpec().TakeAttributes())); + + // If the next token is a comma, consume it and keep reading arguments. + if (Tok.isNot(tok::comma)) break; + + // Consume the comma. + ConsumeToken(); + } + + HasPrototype = true; + + // Leave prototype scope. + ExitScope(); + } + + // Remember that we parsed a function type, and remember the attributes. + if (!ErrorEmitted) + D.AddTypeInfo(DeclaratorChunk::getFunction(HasPrototype, IsVariadic, + &ParamInfo[0], ParamInfo.size(), + StartLoc)); + + // If we have the closing ')', eat it and we're done. + if (Tok.is(tok::r_paren)) { + ConsumeParen(); + } else { + // If an error happened earlier parsing something else in the proto, don't + // issue another error. + if (!ErrorEmitted) + Diag(Tok, diag::err_expected_rparen); + SkipUntil(tok::r_paren); + } +} + + +/// [C90] direct-declarator '[' constant-expression[opt] ']' +/// [C99] direct-declarator '[' type-qual-list[opt] assignment-expr[opt] ']' +/// [C99] direct-declarator '[' 'static' type-qual-list[opt] assign-expr ']' +/// [C99] direct-declarator '[' type-qual-list 'static' assignment-expr ']' +/// [C99] direct-declarator '[' type-qual-list[opt] '*' ']' +void Parser::ParseBracketDeclarator(Declarator &D) { + SourceLocation StartLoc = ConsumeBracket(); + + // If valid, this location is the position where we read the 'static' keyword. + SourceLocation StaticLoc; + if (Tok.is(tok::kw_static)) + StaticLoc = ConsumeToken(); + + // If there is a type-qualifier-list, read it now. + DeclSpec DS; + ParseTypeQualifierListOpt(DS); + + // If we haven't already read 'static', check to see if there is one after the + // type-qualifier-list. + if (!StaticLoc.isValid() && Tok.is(tok::kw_static)) + StaticLoc = ConsumeToken(); + + // Handle "direct-declarator [ type-qual-list[opt] * ]". + bool isStar = false; + ExprResult NumElements(false); + if (Tok.is(tok::star)) { + // Remember the '*' token, in case we have to un-get it. + Token StarTok = Tok; + ConsumeToken(); + + // Check that the ']' token is present to avoid incorrectly parsing + // expressions starting with '*' as [*]. + if (Tok.is(tok::r_square)) { + if (StaticLoc.isValid()) + Diag(StaticLoc, diag::err_unspecified_vla_size_with_static); + StaticLoc = SourceLocation(); // Drop the static. + isStar = true; + } else { + // Otherwise, the * must have been some expression (such as '*ptr') that + // started an assignment-expr. We already consumed the token, but now we + // need to reparse it. This handles cases like 'X[*p + 4]' + NumElements = ParseAssignmentExpressionWithLeadingStar(StarTok); + } + } else if (Tok.isNot(tok::r_square)) { + // Parse the assignment-expression now. + NumElements = ParseAssignmentExpression(); + } + + // If there was an error parsing the assignment-expression, recover. + if (NumElements.isInvalid) { + // If the expression was invalid, skip it. + SkipUntil(tok::r_square); + return; + } + + MatchRHSPunctuation(tok::r_square, StartLoc); + + // If C99 isn't enabled, emit an ext-warn if the arg list wasn't empty and if + // it was not a constant expression. + if (!getLang().C99) { + // TODO: check C90 array constant exprness. + if (isStar || StaticLoc.isValid() || + 0/*TODO: NumElts is not a C90 constantexpr */) + Diag(StartLoc, diag::ext_c99_array_usage); + } + + // Remember that we parsed a pointer type, and remember the type-quals. + D.AddTypeInfo(DeclaratorChunk::getArray(DS.getTypeQualifiers(), + StaticLoc.isValid(), isStar, + NumElements.Val, StartLoc)); +} + +/// [GNU] typeof-specifier: +/// typeof ( expressions ) +/// typeof ( type-name ) +/// +void Parser::ParseTypeofSpecifier(DeclSpec &DS) { + assert(Tok.is(tok::kw_typeof) && "Not a typeof specifier"); + const IdentifierInfo *BuiltinII = Tok.getIdentifierInfo(); + SourceLocation StartLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, BuiltinII->getName()); + return; + } + SourceLocation LParenLoc = ConsumeParen(), RParenLoc; + + if (isTypeSpecifierQualifier()) { + TypeTy *Ty = ParseTypeName(); + + assert(Ty && "Parser::ParseTypeofSpecifier(): missing type"); + + if (Tok.isNot(tok::r_paren)) { + MatchRHSPunctuation(tok::r_paren, LParenLoc); + return; + } + RParenLoc = ConsumeParen(); + const char *PrevSpec = 0; + // Check for duplicate type specifiers (e.g. "int typeof(int)"). + if (DS.SetTypeSpecType(DeclSpec::TST_typeofType, StartLoc, PrevSpec, Ty)) + Diag(StartLoc, diag::err_invalid_decl_spec_combination, PrevSpec); + } else { // we have an expression. + ExprResult Result = ParseExpression(); + + if (Result.isInvalid || Tok.isNot(tok::r_paren)) { + MatchRHSPunctuation(tok::r_paren, LParenLoc); + return; + } + RParenLoc = ConsumeParen(); + const char *PrevSpec = 0; + // Check for duplicate type specifiers (e.g. "int typeof(int)"). + if (DS.SetTypeSpecType(DeclSpec::TST_typeofExpr, StartLoc, PrevSpec, + Result.Val)) + Diag(StartLoc, diag::err_invalid_decl_spec_combination, PrevSpec); + } +} + diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp new file mode 100644 index 00000000000..46dcb574819 --- /dev/null +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -0,0 +1,119 @@ +//===--- ParseDeclCXX.cpp - C++ 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 C++ Declaration portions of the Parser interfaces. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Parse/Scope.h" +#include "clang/Basic/Diagnostic.h" +using namespace clang; + +/// ParseNamespace - We know that the current token is a namespace keyword. This +/// may either be a top level namespace or a block-level namespace alias. +/// +/// namespace-definition: [C++ 7.3: basic.namespace] +/// named-namespace-definition +/// unnamed-namespace-definition +/// +/// unnamed-namespace-definition: +/// 'namespace' attributes[opt] '{' namespace-body '}' +/// +/// named-namespace-definition: +/// original-namespace-definition +/// extension-namespace-definition +/// +/// original-namespace-definition: +/// 'namespace' identifier attributes[opt] '{' namespace-body '}' +/// +/// extension-namespace-definition: +/// 'namespace' original-namespace-name '{' namespace-body '}' +/// +/// namespace-alias-definition: [C++ 7.3.2: namespace.alias] +/// 'namespace' identifier '=' qualified-namespace-specifier ';' +/// +Parser::DeclTy *Parser::ParseNamespace(unsigned Context) { + assert(Tok.is(tok::kw_namespace) && "Not a namespace!"); + SourceLocation NamespaceLoc = ConsumeToken(); // eat the 'namespace'. + + SourceLocation IdentLoc; + IdentifierInfo *Ident = 0; + + if (Tok.is(tok::identifier)) { + Ident = Tok.getIdentifierInfo(); + IdentLoc = ConsumeToken(); // eat the identifier. + } + + // Read label attributes, if present. + DeclTy *AttrList = 0; + if (Tok.is(tok::kw___attribute)) + // FIXME: save these somewhere. + AttrList = ParseAttributes(); + + if (Tok.is(tok::equal)) { + // FIXME: Verify no attributes were present. + // FIXME: parse this. + } else if (Tok.is(tok::l_brace)) { + SourceLocation LBrace = ConsumeBrace(); + // FIXME: push a scope, push a namespace decl. + + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + // FIXME capture the decls. + ParseExternalDeclaration(); + } + + SourceLocation RBrace = MatchRHSPunctuation(tok::r_brace, LBrace); + + // FIXME: act on this. + } else { + unsigned D = Ident ? diag::err_expected_lbrace : + diag::err_expected_ident_lbrace; + Diag(Tok.getLocation(), D); + } + + return 0; +} + +/// ParseLinkage - We know that the current token is a string_literal +/// and just before that, that extern was seen. +/// +/// linkage-specification: [C++ 7.5p2: dcl.link] +/// 'extern' string-literal '{' declaration-seq[opt] '}' +/// 'extern' string-literal declaration +/// +Parser::DeclTy *Parser::ParseLinkage(unsigned Context) { + assert(Tok.is(tok::string_literal) && "Not a stringliteral!"); + llvm::SmallVector<char, 8> LangBuffer; + // LangBuffer is guaranteed to be big enough. + LangBuffer.resize(Tok.getLength()); + const char *LangBufPtr = &LangBuffer[0]; + unsigned StrSize = PP.getSpelling(Tok, LangBufPtr); + + SourceLocation Loc = ConsumeStringToken(); + DeclTy *D = 0; + SourceLocation LBrace, RBrace; + + if (Tok.isNot(tok::l_brace)) { + D = ParseDeclaration(Context); + } else { + LBrace = ConsumeBrace(); + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + // FIXME capture the decls. + D = ParseExternalDeclaration(); + } + + RBrace = MatchRHSPunctuation(tok::r_brace, LBrace); + } + + if (!D) + return 0; + + return Actions.ActOnLinkageSpec(Loc, LBrace, RBrace, LangBufPtr, StrSize, D); +} diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp new file mode 100644 index 00000000000..46714b73ea7 --- /dev/null +++ b/clang/lib/Parse/ParseExpr.cpp @@ -0,0 +1,1081 @@ +//===--- ParseExpr.cpp - Expression 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 Expression parsing implementation. Expressions in +// C99 basically consist of a bunch of binary operators with unary operators and +// other random stuff at the leaves. +// +// In the C99 grammar, these unary operators bind tightest and are represented +// as the 'cast-expression' production. Everything else is either a binary +// operator (e.g. '/') or a ternary operator ("?:"). The unary leaves are +// handled by ParseCastExpression, the higher level pieces are handled by +// ParseBinaryExpression. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SmallString.h" +using namespace clang; + +/// PrecedenceLevels - These are precedences for the binary/ternary operators in +/// the C99 grammar. These have been named to relate with the C99 grammar +/// productions. Low precedences numbers bind more weakly than high numbers. +namespace prec { + enum Level { + Unknown = 0, // Not binary operator. + Comma = 1, // , + Assignment = 2, // =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= + Conditional = 3, // ? + LogicalOr = 4, // || + LogicalAnd = 5, // && + InclusiveOr = 6, // | + ExclusiveOr = 7, // ^ + And = 8, // & + Equality = 9, // ==, != + Relational = 10, // >=, <=, >, < + Shift = 11, // <<, >> + Additive = 12, // -, + + Multiplicative = 13 // *, /, % + }; +} + + +/// getBinOpPrecedence - Return the precedence of the specified binary operator +/// token. This returns: +/// +static prec::Level getBinOpPrecedence(tok::TokenKind Kind) { + switch (Kind) { + default: return prec::Unknown; + case tok::comma: return prec::Comma; + case tok::equal: + case tok::starequal: + case tok::slashequal: + case tok::percentequal: + case tok::plusequal: + case tok::minusequal: + case tok::lesslessequal: + case tok::greatergreaterequal: + case tok::ampequal: + case tok::caretequal: + case tok::pipeequal: return prec::Assignment; + case tok::question: return prec::Conditional; + case tok::pipepipe: return prec::LogicalOr; + case tok::ampamp: return prec::LogicalAnd; + case tok::pipe: return prec::InclusiveOr; + case tok::caret: return prec::ExclusiveOr; + case tok::amp: return prec::And; + case tok::exclaimequal: + case tok::equalequal: return prec::Equality; + case tok::lessequal: + case tok::less: + case tok::greaterequal: + case tok::greater: return prec::Relational; + case tok::lessless: + case tok::greatergreater: return prec::Shift; + case tok::plus: + case tok::minus: return prec::Additive; + case tok::percent: + case tok::slash: + case tok::star: return prec::Multiplicative; + } +} + + +/// ParseExpression - Simple precedence-based parser for binary/ternary +/// operators. +/// +/// Note: we diverge from the C99 grammar when parsing the assignment-expression +/// production. C99 specifies that the LHS of an assignment operator should be +/// parsed as a unary-expression, but consistency dictates that it be a +/// conditional-expession. In practice, the important thing here is that the +/// LHS of an assignment has to be an l-value, which productions between +/// unary-expression and conditional-expression don't produce. Because we want +/// consistency, we parse the LHS as a conditional-expression, then check for +/// l-value-ness in semantic analysis stages. +/// +/// multiplicative-expression: [C99 6.5.5] +/// cast-expression +/// multiplicative-expression '*' cast-expression +/// multiplicative-expression '/' cast-expression +/// multiplicative-expression '%' cast-expression +/// +/// additive-expression: [C99 6.5.6] +/// multiplicative-expression +/// additive-expression '+' multiplicative-expression +/// additive-expression '-' multiplicative-expression +/// +/// shift-expression: [C99 6.5.7] +/// additive-expression +/// shift-expression '<<' additive-expression +/// shift-expression '>>' additive-expression +/// +/// relational-expression: [C99 6.5.8] +/// shift-expression +/// relational-expression '<' shift-expression +/// relational-expression '>' shift-expression +/// relational-expression '<=' shift-expression +/// relational-expression '>=' shift-expression +/// +/// equality-expression: [C99 6.5.9] +/// relational-expression +/// equality-expression '==' relational-expression +/// equality-expression '!=' relational-expression +/// +/// AND-expression: [C99 6.5.10] +/// equality-expression +/// AND-expression '&' equality-expression +/// +/// exclusive-OR-expression: [C99 6.5.11] +/// AND-expression +/// exclusive-OR-expression '^' AND-expression +/// +/// inclusive-OR-expression: [C99 6.5.12] +/// exclusive-OR-expression +/// inclusive-OR-expression '|' exclusive-OR-expression +/// +/// logical-AND-expression: [C99 6.5.13] +/// inclusive-OR-expression +/// logical-AND-expression '&&' inclusive-OR-expression +/// +/// logical-OR-expression: [C99 6.5.14] +/// logical-AND-expression +/// logical-OR-expression '||' logical-AND-expression +/// +/// conditional-expression: [C99 6.5.15] +/// logical-OR-expression +/// logical-OR-expression '?' expression ':' conditional-expression +/// [GNU] logical-OR-expression '?' ':' conditional-expression +/// +/// assignment-expression: [C99 6.5.16] +/// conditional-expression +/// unary-expression assignment-operator assignment-expression +/// [C++] throw-expression [C++ 15] +/// +/// assignment-operator: one of +/// = *= /= %= += -= <<= >>= &= ^= |= +/// +/// expression: [C99 6.5.17] +/// assignment-expression +/// expression ',' assignment-expression +/// +Parser::ExprResult Parser::ParseExpression() { + if (Tok.is(tok::kw_throw)) + return ParseThrowExpression(); + + ExprResult LHS = ParseCastExpression(false); + if (LHS.isInvalid) return LHS; + + return ParseRHSOfBinaryExpression(LHS, prec::Comma); +} + +/// This routine is called when the '@' is seen and consumed. +/// Current token is an Identifier and is not a 'try'. This +/// routine is necessary to disambiguate @try-statement from, +/// for example, @encode-expression. +/// +Parser::ExprResult Parser::ParseExpressionWithLeadingAt(SourceLocation AtLoc) { + ExprResult LHS = ParseObjCAtExpression(AtLoc); + if (LHS.isInvalid) return LHS; + + return ParseRHSOfBinaryExpression(LHS, prec::Comma); +} + +/// ParseAssignmentExpression - Parse an expr that doesn't include commas. +/// +Parser::ExprResult Parser::ParseAssignmentExpression() { + if (Tok.is(tok::kw_throw)) + return ParseThrowExpression(); + + ExprResult LHS = ParseCastExpression(false); + if (LHS.isInvalid) return LHS; + + return ParseRHSOfBinaryExpression(LHS, prec::Assignment); +} + +Parser::ExprResult Parser::ParseConstantExpression() { + ExprResult LHS = ParseCastExpression(false); + if (LHS.isInvalid) return LHS; + + return ParseRHSOfBinaryExpression(LHS, prec::Conditional); +} + +/// ParseExpressionWithLeadingIdentifier - This special purpose method is used +/// in contexts where we have already consumed an identifier (which we saved in +/// 'IdTok'), then discovered that the identifier was really the leading token +/// of part of an expression. For example, in "A[1]+B", we consumed "A" (which +/// is now in 'IdTok') and the current token is "[". +Parser::ExprResult Parser:: +ParseExpressionWithLeadingIdentifier(const Token &IdTok) { + // We know that 'IdTok' must correspond to this production: + // primary-expression: identifier + + // Let the actions module handle the identifier. + ExprResult Res = Actions.ActOnIdentifierExpr(CurScope, IdTok.getLocation(), + *IdTok.getIdentifierInfo(), + Tok.is(tok::l_paren)); + + // Because we have to parse an entire cast-expression before starting the + // ParseRHSOfBinaryExpression method (which parses any trailing binops), we + // need to handle the 'postfix-expression' rules. We do this by invoking + // ParsePostfixExpressionSuffix to consume any postfix-expression suffixes: + Res = ParsePostfixExpressionSuffix(Res); + if (Res.isInvalid) return Res; + + // At this point, the "A[1]" part of "A[1]+B" has been consumed. Once this is + // done, we know we don't have to do anything for cast-expression, because the + // only non-postfix-expression production starts with a '(' token, and we know + // we have an identifier. As such, we can invoke ParseRHSOfBinaryExpression + // to consume any trailing operators (e.g. "+" in this example) and connected + // chunks of the expression. + return ParseRHSOfBinaryExpression(Res, prec::Comma); +} + +/// ParseExpressionWithLeadingIdentifier - This special purpose method is used +/// in contexts where we have already consumed an identifier (which we saved in +/// 'IdTok'), then discovered that the identifier was really the leading token +/// of part of an assignment-expression. For example, in "A[1]+B", we consumed +/// "A" (which is now in 'IdTok') and the current token is "[". +Parser::ExprResult Parser:: +ParseAssignmentExprWithLeadingIdentifier(const Token &IdTok) { + // We know that 'IdTok' must correspond to this production: + // primary-expression: identifier + + // Let the actions module handle the identifier. + ExprResult Res = Actions.ActOnIdentifierExpr(CurScope, IdTok.getLocation(), + *IdTok.getIdentifierInfo(), + Tok.is(tok::l_paren)); + + // Because we have to parse an entire cast-expression before starting the + // ParseRHSOfBinaryExpression method (which parses any trailing binops), we + // need to handle the 'postfix-expression' rules. We do this by invoking + // ParsePostfixExpressionSuffix to consume any postfix-expression suffixes: + Res = ParsePostfixExpressionSuffix(Res); + if (Res.isInvalid) return Res; + + // At this point, the "A[1]" part of "A[1]+B" has been consumed. Once this is + // done, we know we don't have to do anything for cast-expression, because the + // only non-postfix-expression production starts with a '(' token, and we know + // we have an identifier. As such, we can invoke ParseRHSOfBinaryExpression + // to consume any trailing operators (e.g. "+" in this example) and connected + // chunks of the expression. + return ParseRHSOfBinaryExpression(Res, prec::Assignment); +} + + +/// ParseAssignmentExpressionWithLeadingStar - This special purpose method is +/// used in contexts where we have already consumed a '*' (which we saved in +/// 'StarTok'), then discovered that the '*' was really the leading token of an +/// expression. For example, in "*(int*)P+B", we consumed "*" (which is +/// now in 'StarTok') and the current token is "(". +Parser::ExprResult Parser:: +ParseAssignmentExpressionWithLeadingStar(const Token &StarTok) { + // We know that 'StarTok' must correspond to this production: + // unary-expression: unary-operator cast-expression + // where 'unary-operator' is '*'. + + // Parse the cast-expression that follows the '*'. This will parse the + // "*(int*)P" part of "*(int*)P+B". + ExprResult Res = ParseCastExpression(false); + if (Res.isInvalid) return Res; + + // Combine StarTok + Res to get the new AST for the combined expression.. + Res = Actions.ActOnUnaryOp(StarTok.getLocation(), tok::star, Res.Val); + if (Res.isInvalid) return Res; + + + // We have to parse an entire cast-expression before starting the + // ParseRHSOfBinaryExpression method (which parses any trailing binops). Since + // we know that the only production above us is the cast-expression + // production, and because the only alternative productions start with a '(' + // token (we know we had a '*'), there is no work to do to get a whole + // cast-expression. + + // At this point, the "*(int*)P" part of "*(int*)P+B" has been consumed. Once + // this is done, we can invoke ParseRHSOfBinaryExpression to consume any + // trailing operators (e.g. "+" in this example) and connected chunks of the + // assignment-expression. + return ParseRHSOfBinaryExpression(Res, prec::Assignment); +} + + +/// ParseRHSOfBinaryExpression - Parse a binary expression that starts with +/// LHS and has a precedence of at least MinPrec. +Parser::ExprResult +Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { + unsigned NextTokPrec = getBinOpPrecedence(Tok.getKind()); + SourceLocation ColonLoc; + + while (1) { + // If this token has a lower precedence than we are allowed to parse (e.g. + // because we are called recursively, or because the token is not a binop), + // then we are done! + if (NextTokPrec < MinPrec) + return LHS; + + // Consume the operator, saving the operator token for error reporting. + Token OpToken = Tok; + ConsumeToken(); + + // Special case handling for the ternary operator. + ExprResult TernaryMiddle(true); + if (NextTokPrec == prec::Conditional) { + if (Tok.isNot(tok::colon)) { + // Handle this production specially: + // logical-OR-expression '?' expression ':' conditional-expression + // In particular, the RHS of the '?' is 'expression', not + // 'logical-OR-expression' as we might expect. + TernaryMiddle = ParseExpression(); + if (TernaryMiddle.isInvalid) { + Actions.DeleteExpr(LHS.Val); + return TernaryMiddle; + } + } else { + // Special case handling of "X ? Y : Z" where Y is empty: + // logical-OR-expression '?' ':' conditional-expression [GNU] + TernaryMiddle = ExprResult(false); + Diag(Tok, diag::ext_gnu_conditional_expr); + } + + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon); + Diag(OpToken, diag::err_matching, "?"); + Actions.DeleteExpr(LHS.Val); + Actions.DeleteExpr(TernaryMiddle.Val); + return ExprResult(true); + } + + // Eat the colon. + ColonLoc = ConsumeToken(); + } + + // Parse another leaf here for the RHS of the operator. + ExprResult RHS = ParseCastExpression(false); + if (RHS.isInvalid) { + Actions.DeleteExpr(LHS.Val); + Actions.DeleteExpr(TernaryMiddle.Val); + return RHS; + } + + // Remember the precedence of this operator and get the precedence of the + // operator immediately to the right of the RHS. + unsigned ThisPrec = NextTokPrec; + NextTokPrec = getBinOpPrecedence(Tok.getKind()); + + // Assignment and conditional expressions are right-associative. + bool isRightAssoc = ThisPrec == prec::Conditional || + ThisPrec == prec::Assignment; + + // Get the precedence of the operator to the right of the RHS. If it binds + // more tightly with RHS than we do, evaluate it completely first. + if (ThisPrec < NextTokPrec || + (ThisPrec == NextTokPrec && isRightAssoc)) { + // If this is left-associative, only parse things on the RHS that bind + // more tightly than the current operator. If it is left-associative, it + // is okay, to bind exactly as tightly. For example, compile A=B=C=D as + // A=(B=(C=D)), where each paren is a level of recursion here. + RHS = ParseRHSOfBinaryExpression(RHS, ThisPrec + !isRightAssoc); + if (RHS.isInvalid) { + Actions.DeleteExpr(LHS.Val); + Actions.DeleteExpr(TernaryMiddle.Val); + return RHS; + } + + NextTokPrec = getBinOpPrecedence(Tok.getKind()); + } + assert(NextTokPrec <= ThisPrec && "Recursion didn't work!"); + + if (!LHS.isInvalid) { + // Combine the LHS and RHS into the LHS (e.g. build AST). + if (TernaryMiddle.isInvalid) + LHS = Actions.ActOnBinOp(OpToken.getLocation(), OpToken.getKind(), + LHS.Val, RHS.Val); + else + LHS = Actions.ActOnConditionalOp(OpToken.getLocation(), ColonLoc, + LHS.Val, TernaryMiddle.Val, RHS.Val); + } else { + // We had a semantic error on the LHS. Just free the RHS and continue. + Actions.DeleteExpr(TernaryMiddle.Val); + Actions.DeleteExpr(RHS.Val); + } + } +} + +/// ParseCastExpression - Parse a cast-expression, or, if isUnaryExpression is +/// true, parse a unary-expression. +/// +/// cast-expression: [C99 6.5.4] +/// unary-expression +/// '(' type-name ')' cast-expression +/// +/// unary-expression: [C99 6.5.3] +/// postfix-expression +/// '++' unary-expression +/// '--' unary-expression +/// unary-operator cast-expression +/// 'sizeof' unary-expression +/// 'sizeof' '(' type-name ')' +/// [GNU] '__alignof' unary-expression +/// [GNU] '__alignof' '(' type-name ')' +/// [GNU] '&&' identifier +/// +/// unary-operator: one of +/// '&' '*' '+' '-' '~' '!' +/// [GNU] '__extension__' '__real' '__imag' +/// +/// primary-expression: [C99 6.5.1] +/// identifier +/// constant +/// string-literal +/// [C++] boolean-literal [C++ 2.13.5] +/// '(' expression ')' +/// '__func__' [C99 6.4.2.2] +/// [GNU] '__FUNCTION__' +/// [GNU] '__PRETTY_FUNCTION__' +/// [GNU] '(' compound-statement ')' +/// [GNU] '__builtin_va_arg' '(' assignment-expression ',' type-name ')' +/// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')' +/// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' +/// assign-expr ')' +/// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' +/// [OBJC] '[' objc-message-expr ']' +/// [OBJC] '@selector' '(' objc-selector-arg ')' +/// [OBJC] '@protocol' '(' identifier ')' +/// [OBJC] '@encode' '(' type-name ')' +/// [OBJC] objc-string-literal +/// [C++] 'const_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] +/// [C++] 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] +/// [C++] 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] +/// [C++] 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] +/// +/// constant: [C99 6.4.4] +/// integer-constant +/// floating-constant +/// enumeration-constant -> identifier +/// character-constant +/// +Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { + ExprResult Res; + tok::TokenKind SavedKind = Tok.getKind(); + + // This handles all of cast-expression, unary-expression, postfix-expression, + // and primary-expression. We handle them together like this for efficiency + // and to simplify handling of an expression starting with a '(' token: which + // may be one of a parenthesized expression, cast-expression, compound literal + // expression, or statement expression. + // + // If the parsed tokens consist of a primary-expression, the cases below + // call ParsePostfixExpressionSuffix to handle the postfix expression + // suffixes. Cases that cannot be followed by postfix exprs should + // return without invoking ParsePostfixExpressionSuffix. + switch (SavedKind) { + case tok::l_paren: { + // If this expression is limited to being a unary-expression, the parent can + // not start a cast expression. + ParenParseOption ParenExprType = + isUnaryExpression ? CompoundLiteral : CastExpr; + TypeTy *CastTy; + SourceLocation LParenLoc = Tok.getLocation(); + SourceLocation RParenLoc; + Res = ParseParenExpression(ParenExprType, CastTy, RParenLoc); + if (Res.isInvalid) return Res; + + switch (ParenExprType) { + case SimpleExpr: break; // Nothing else to do. + case CompoundStmt: break; // Nothing else to do. + case CompoundLiteral: + // We parsed '(' type-name ')' '{' ... '}'. If any suffixes of + // postfix-expression exist, parse them now. + break; + case CastExpr: + // We parsed '(' type-name ')' and the thing after it wasn't a '{'. Parse + // the cast-expression that follows it next. + // TODO: For cast expression with CastTy. + Res = ParseCastExpression(false); + if (!Res.isInvalid) + Res = Actions.ActOnCastExpr(LParenLoc, CastTy, RParenLoc, Res.Val); + return Res; + } + + // These can be followed by postfix-expr pieces. + return ParsePostfixExpressionSuffix(Res); + } + + // primary-expression + case tok::numeric_constant: + // constant: integer-constant + // constant: floating-constant + + Res = Actions.ActOnNumericConstant(Tok); + ConsumeToken(); + + // These can be followed by postfix-expr pieces. + return ParsePostfixExpressionSuffix(Res); + + case tok::kw_true: + case tok::kw_false: + return ParseCXXBoolLiteral(); + + case tok::identifier: { // primary-expression: identifier + // constant: enumeration-constant + // Consume the identifier so that we can see if it is followed by a '('. + // Function designators are allowed to be undeclared (C99 6.5.1p2), so we + // need to know whether or not this identifier is a function designator or + // not. + IdentifierInfo &II = *Tok.getIdentifierInfo(); + SourceLocation L = ConsumeToken(); + Res = Actions.ActOnIdentifierExpr(CurScope, L, II, Tok.is(tok::l_paren)); + // These can be followed by postfix-expr pieces. + return ParsePostfixExpressionSuffix(Res); + } + case tok::char_constant: // constant: character-constant + Res = Actions.ActOnCharacterConstant(Tok); + ConsumeToken(); + // These can be followed by postfix-expr pieces. + return ParsePostfixExpressionSuffix(Res); + case tok::kw___func__: // primary-expression: __func__ [C99 6.4.2.2] + case tok::kw___FUNCTION__: // primary-expression: __FUNCTION__ [GNU] + case tok::kw___PRETTY_FUNCTION__: // primary-expression: __P..Y_F..N__ [GNU] + Res = Actions.ActOnPreDefinedExpr(Tok.getLocation(), SavedKind); + ConsumeToken(); + // These can be followed by postfix-expr pieces. + return ParsePostfixExpressionSuffix(Res); + case tok::string_literal: // primary-expression: string-literal + case tok::wide_string_literal: + Res = ParseStringLiteralExpression(); + if (Res.isInvalid) return Res; + // This can be followed by postfix-expr pieces (e.g. "foo"[1]). + return ParsePostfixExpressionSuffix(Res); + case tok::kw___builtin_va_arg: + case tok::kw___builtin_offsetof: + case tok::kw___builtin_choose_expr: + case tok::kw___builtin_overload: + case tok::kw___builtin_types_compatible_p: + return ParseBuiltinPrimaryExpression(); + case tok::plusplus: // unary-expression: '++' unary-expression + case tok::minusminus: { // unary-expression: '--' unary-expression + SourceLocation SavedLoc = ConsumeToken(); + Res = ParseCastExpression(true); + if (!Res.isInvalid) + Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val); + return Res; + } + case tok::amp: // unary-expression: '&' cast-expression + case tok::star: // unary-expression: '*' cast-expression + case tok::plus: // unary-expression: '+' cast-expression + case tok::minus: // unary-expression: '-' cast-expression + case tok::tilde: // unary-expression: '~' cast-expression + case tok::exclaim: // unary-expression: '!' cast-expression + case tok::kw___real: // unary-expression: '__real' cast-expression [GNU] + case tok::kw___imag: { // unary-expression: '__imag' cast-expression [GNU] + SourceLocation SavedLoc = ConsumeToken(); + Res = ParseCastExpression(false); + if (!Res.isInvalid) + Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val); + return Res; + } + + case tok::kw___extension__:{//unary-expression:'__extension__' cast-expr [GNU] + // __extension__ silences extension warnings in the subexpression. + bool SavedExtWarn = Diags.getWarnOnExtensions(); + Diags.setWarnOnExtensions(false); + SourceLocation SavedLoc = ConsumeToken(); + Res = ParseCastExpression(false); + if (!Res.isInvalid) + Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val); + Diags.setWarnOnExtensions(SavedExtWarn); + return Res; + } + case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression + // unary-expression: 'sizeof' '(' type-name ')' + case tok::kw___alignof: // unary-expression: '__alignof' unary-expression + // unary-expression: '__alignof' '(' type-name ')' + return ParseSizeofAlignofExpression(); + case tok::ampamp: { // unary-expression: '&&' identifier + SourceLocation AmpAmpLoc = ConsumeToken(); + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return ExprResult(true); + } + + Diag(AmpAmpLoc, diag::ext_gnu_address_of_label); + Res = Actions.ActOnAddrLabel(AmpAmpLoc, Tok.getLocation(), + Tok.getIdentifierInfo()); + ConsumeToken(); + return Res; + } + case tok::kw_const_cast: + case tok::kw_dynamic_cast: + case tok::kw_reinterpret_cast: + case tok::kw_static_cast: + return ParseCXXCasts(); + case tok::at: { + SourceLocation AtLoc = ConsumeToken(); + return ParseObjCAtExpression(AtLoc); + } + case tok::l_square: + // These can be followed by postfix-expr pieces. + return ParsePostfixExpressionSuffix(ParseObjCMessageExpression()); + default: + Diag(Tok, diag::err_expected_expression); + return ExprResult(true); + } + + // unreachable. + abort(); +} + +/// ParsePostfixExpressionSuffix - Once the leading part of a postfix-expression +/// is parsed, this method parses any suffixes that apply. +/// +/// postfix-expression: [C99 6.5.2] +/// primary-expression +/// postfix-expression '[' expression ']' +/// postfix-expression '(' argument-expression-list[opt] ')' +/// postfix-expression '.' identifier +/// postfix-expression '->' identifier +/// postfix-expression '++' +/// postfix-expression '--' +/// '(' type-name ')' '{' initializer-list '}' +/// '(' type-name ')' '{' initializer-list ',' '}' +/// +/// argument-expression-list: [C99 6.5.2] +/// argument-expression +/// argument-expression-list ',' assignment-expression +/// +Parser::ExprResult Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { + + // Now that the primary-expression piece of the postfix-expression has been + // parsed, see if there are any postfix-expression pieces here. + SourceLocation Loc; + while (1) { + switch (Tok.getKind()) { + default: // Not a postfix-expression suffix. + return LHS; + case tok::l_square: { // postfix-expression: p-e '[' expression ']' + Loc = ConsumeBracket(); + ExprResult Idx = ParseExpression(); + + SourceLocation RLoc = Tok.getLocation(); + + if (!LHS.isInvalid && !Idx.isInvalid && Tok.is(tok::r_square)) + LHS = Actions.ActOnArraySubscriptExpr(LHS.Val, Loc, Idx.Val, RLoc); + else + LHS = ExprResult(true); + + // Match the ']'. + MatchRHSPunctuation(tok::r_square, Loc); + break; + } + + case tok::l_paren: { // p-e: p-e '(' argument-expression-list[opt] ')' + llvm::SmallVector<ExprTy*, 8> ArgExprs; + llvm::SmallVector<SourceLocation, 8> CommaLocs; + + Loc = ConsumeParen(); + + if (Tok.isNot(tok::r_paren)) { + while (1) { + ExprResult ArgExpr = ParseAssignmentExpression(); + if (ArgExpr.isInvalid) { + SkipUntil(tok::r_paren); + return ExprResult(true); + } else + ArgExprs.push_back(ArgExpr.Val); + + if (Tok.isNot(tok::comma)) + break; + // Move to the next argument, remember where the comma was. + CommaLocs.push_back(ConsumeToken()); + } + } + + // Match the ')'. + if (!LHS.isInvalid && Tok.is(tok::r_paren)) { + assert((ArgExprs.size() == 0 || ArgExprs.size()-1 == CommaLocs.size())&& + "Unexpected number of commas!"); + LHS = Actions.ActOnCallExpr(LHS.Val, Loc, &ArgExprs[0], ArgExprs.size(), + &CommaLocs[0], Tok.getLocation()); + } + + MatchRHSPunctuation(tok::r_paren, Loc); + break; + } + case tok::arrow: // postfix-expression: p-e '->' identifier + case tok::period: { // postfix-expression: p-e '.' identifier + tok::TokenKind OpKind = Tok.getKind(); + SourceLocation OpLoc = ConsumeToken(); // Eat the "." or "->" token. + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return ExprResult(true); + } + + if (!LHS.isInvalid) + LHS = Actions.ActOnMemberReferenceExpr(LHS.Val, OpLoc, OpKind, + Tok.getLocation(), + *Tok.getIdentifierInfo()); + ConsumeToken(); + break; + } + case tok::plusplus: // postfix-expression: postfix-expression '++' + case tok::minusminus: // postfix-expression: postfix-expression '--' + if (!LHS.isInvalid) + LHS = Actions.ActOnPostfixUnaryOp(Tok.getLocation(), Tok.getKind(), + LHS.Val); + ConsumeToken(); + break; + } + } +} + + +/// ParseSizeofAlignofExpression - Parse a sizeof or alignof expression. +/// unary-expression: [C99 6.5.3] +/// 'sizeof' unary-expression +/// 'sizeof' '(' type-name ')' +/// [GNU] '__alignof' unary-expression +/// [GNU] '__alignof' '(' type-name ')' +Parser::ExprResult Parser::ParseSizeofAlignofExpression() { + assert((Tok.is(tok::kw_sizeof) || Tok.is(tok::kw___alignof)) && + "Not a sizeof/alignof expression!"); + Token OpTok = Tok; + ConsumeToken(); + + // If the operand doesn't start with an '(', it must be an expression. + ExprResult Operand; + if (Tok.isNot(tok::l_paren)) { + Operand = ParseCastExpression(true); + } else { + // If it starts with a '(', we know that it is either a parenthesized + // type-name, or it is a unary-expression that starts with a compound + // literal, or starts with a primary-expression that is a parenthesized + // expression. + ParenParseOption ExprType = CastExpr; + TypeTy *CastTy; + SourceLocation LParenLoc = Tok.getLocation(), RParenLoc; + Operand = ParseParenExpression(ExprType, CastTy, RParenLoc); + + // If ParseParenExpression parsed a '(typename)' sequence only, the this is + // sizeof/alignof a type. Otherwise, it is sizeof/alignof an expression. + if (ExprType == CastExpr) + return Actions.ActOnSizeOfAlignOfTypeExpr(OpTok.getLocation(), + OpTok.is(tok::kw_sizeof), + LParenLoc, CastTy, RParenLoc); + + // If this is a parenthesized expression, it is the start of a + // unary-expression, but doesn't include any postfix pieces. Parse these + // now if present. + Operand = ParsePostfixExpressionSuffix(Operand); + } + + // If we get here, the operand to the sizeof/alignof was an expresion. + if (!Operand.isInvalid) + Operand = Actions.ActOnUnaryOp(OpTok.getLocation(), OpTok.getKind(), + Operand.Val); + return Operand; +} + +/// ParseBuiltinPrimaryExpression +/// +/// primary-expression: [C99 6.5.1] +/// [GNU] '__builtin_va_arg' '(' assignment-expression ',' type-name ')' +/// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')' +/// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' +/// assign-expr ')' +/// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' +/// [CLANG] '__builtin_overload' '(' expr (',' expr)* ')' +/// +/// [GNU] offsetof-member-designator: +/// [GNU] identifier +/// [GNU] offsetof-member-designator '.' identifier +/// [GNU] offsetof-member-designator '[' expression ']' +/// +Parser::ExprResult Parser::ParseBuiltinPrimaryExpression() { + ExprResult Res(false); + const IdentifierInfo *BuiltinII = Tok.getIdentifierInfo(); + + tok::TokenKind T = Tok.getKind(); + SourceLocation StartLoc = ConsumeToken(); // Eat the builtin identifier. + + // All of these start with an open paren. + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, BuiltinII->getName()); + return ExprResult(true); + } + + SourceLocation LParenLoc = ConsumeParen(); + // TODO: Build AST. + + switch (T) { + default: assert(0 && "Not a builtin primary expression!"); + case tok::kw___builtin_va_arg: { + ExprResult Expr = ParseAssignmentExpression(); + if (Expr.isInvalid) { + SkipUntil(tok::r_paren); + return Res; + } + + if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "",tok::r_paren)) + return ExprResult(true); + + TypeTy *Ty = ParseTypeName(); + + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + return ExprResult(true); + } + Res = Actions.ActOnVAArg(StartLoc, Expr.Val, Ty, ConsumeParen()); + break; + } + case tok::kw___builtin_offsetof: { + SourceLocation TypeLoc = Tok.getLocation(); + TypeTy *Ty = ParseTypeName(); + + if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "",tok::r_paren)) + return ExprResult(true); + + // We must have at least one identifier here. + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::r_paren); + return true; + } + + // Keep track of the various subcomponents we see. + llvm::SmallVector<Action::OffsetOfComponent, 4> Comps; + + Comps.push_back(Action::OffsetOfComponent()); + Comps.back().isBrackets = false; + Comps.back().U.IdentInfo = Tok.getIdentifierInfo(); + Comps.back().LocStart = Comps.back().LocEnd = ConsumeToken(); + + while (1) { + if (Tok.is(tok::period)) { + // offsetof-member-designator: offsetof-member-designator '.' identifier + Comps.push_back(Action::OffsetOfComponent()); + Comps.back().isBrackets = false; + Comps.back().LocStart = ConsumeToken(); + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::r_paren); + return true; + } + Comps.back().U.IdentInfo = Tok.getIdentifierInfo(); + Comps.back().LocEnd = ConsumeToken(); + + } else if (Tok.is(tok::l_square)) { + // offsetof-member-designator: offsetof-member-design '[' expression ']' + Comps.push_back(Action::OffsetOfComponent()); + Comps.back().isBrackets = true; + Comps.back().LocStart = ConsumeBracket(); + Res = ParseExpression(); + if (Res.isInvalid) { + SkipUntil(tok::r_paren); + return Res; + } + Comps.back().U.E = Res.Val; + + Comps.back().LocEnd = + MatchRHSPunctuation(tok::r_square, Comps.back().LocStart); + } else if (Tok.is(tok::r_paren)) { + Res = Actions.ActOnBuiltinOffsetOf(StartLoc, TypeLoc, Ty, &Comps[0], + Comps.size(), ConsumeParen()); + break; + } else { + // Error occurred. + return ExprResult(true); + } + } + break; + } + case tok::kw___builtin_choose_expr: { + ExprResult Cond = ParseAssignmentExpression(); + if (Cond.isInvalid) { + SkipUntil(tok::r_paren); + return Cond; + } + if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "",tok::r_paren)) + return ExprResult(true); + + ExprResult Expr1 = ParseAssignmentExpression(); + if (Expr1.isInvalid) { + SkipUntil(tok::r_paren); + return Expr1; + } + if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "",tok::r_paren)) + return ExprResult(true); + + ExprResult Expr2 = ParseAssignmentExpression(); + if (Expr2.isInvalid) { + SkipUntil(tok::r_paren); + return Expr2; + } + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + return ExprResult(true); + } + Res = Actions.ActOnChooseExpr(StartLoc, Cond.Val, Expr1.Val, Expr2.Val, + ConsumeParen()); + break; + } + case tok::kw___builtin_overload: { + llvm::SmallVector<ExprTy*, 8> ArgExprs; + llvm::SmallVector<SourceLocation, 8> CommaLocs; + + // For each iteration through the loop look for assign-expr followed by a + // comma. If there is no comma, break and attempt to match r-paren. + if (Tok.isNot(tok::r_paren)) { + while (1) { + ExprResult ArgExpr = ParseAssignmentExpression(); + if (ArgExpr.isInvalid) { + SkipUntil(tok::r_paren); + return ExprResult(true); + } else + ArgExprs.push_back(ArgExpr.Val); + + if (Tok.isNot(tok::comma)) + break; + // Move to the next argument, remember where the comma was. + CommaLocs.push_back(ConsumeToken()); + } + } + + // Attempt to consume the r-paren + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + SkipUntil(tok::r_paren); + return ExprResult(true); + } + Res = Actions.ActOnOverloadExpr(&ArgExprs[0], ArgExprs.size(), + &CommaLocs[0], StartLoc, ConsumeParen()); + break; + } + case tok::kw___builtin_types_compatible_p: + TypeTy *Ty1 = ParseTypeName(); + + if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "",tok::r_paren)) + return ExprResult(true); + + TypeTy *Ty2 = ParseTypeName(); + + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + return ExprResult(true); + } + Res = Actions.ActOnTypesCompatibleExpr(StartLoc, Ty1, Ty2, ConsumeParen()); + break; + } + + // These can be followed by postfix-expr pieces because they are + // primary-expressions. + return ParsePostfixExpressionSuffix(Res); +} + +/// ParseParenExpression - This parses the unit that starts with a '(' token, +/// based on what is allowed by ExprType. The actual thing parsed is returned +/// in ExprType. +/// +/// primary-expression: [C99 6.5.1] +/// '(' expression ')' +/// [GNU] '(' compound-statement ')' (if !ParenExprOnly) +/// postfix-expression: [C99 6.5.2] +/// '(' type-name ')' '{' initializer-list '}' +/// '(' type-name ')' '{' initializer-list ',' '}' +/// cast-expression: [C99 6.5.4] +/// '(' type-name ')' cast-expression +/// +Parser::ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, + TypeTy *&CastTy, + SourceLocation &RParenLoc) { + assert(Tok.is(tok::l_paren) && "Not a paren expr!"); + SourceLocation OpenLoc = ConsumeParen(); + ExprResult Result(true); + CastTy = 0; + + if (ExprType >= CompoundStmt && Tok.is(tok::l_brace)) { + Diag(Tok, diag::ext_gnu_statement_expr); + Parser::StmtResult Stmt = ParseCompoundStatement(true); + ExprType = CompoundStmt; + + // If the substmt parsed correctly, build the AST node. + if (!Stmt.isInvalid && Tok.is(tok::r_paren)) + Result = Actions.ActOnStmtExpr(OpenLoc, Stmt.Val, Tok.getLocation()); + + } else if (ExprType >= CompoundLiteral && isTypeSpecifierQualifier()) { + // Otherwise, this is a compound literal expression or cast expression. + TypeTy *Ty = ParseTypeName(); + + // Match the ')'. + if (Tok.is(tok::r_paren)) + RParenLoc = ConsumeParen(); + else + MatchRHSPunctuation(tok::r_paren, OpenLoc); + + if (Tok.is(tok::l_brace)) { + if (!getLang().C99) // Compound literals don't exist in C90. + Diag(OpenLoc, diag::ext_c99_compound_literal); + Result = ParseInitializer(); + ExprType = CompoundLiteral; + if (!Result.isInvalid) + return Actions.ActOnCompoundLiteral(OpenLoc, Ty, RParenLoc, Result.Val); + } else if (ExprType == CastExpr) { + // Note that this doesn't parse the subsequence cast-expression, it just + // returns the parsed type to the callee. + ExprType = CastExpr; + CastTy = Ty; + return ExprResult(false); + } else { + Diag(Tok, diag::err_expected_lbrace_in_compound_literal); + return ExprResult(true); + } + return Result; + } else { + Result = ParseExpression(); + ExprType = SimpleExpr; + if (!Result.isInvalid && Tok.is(tok::r_paren)) + Result = Actions.ActOnParenExpr(OpenLoc, Tok.getLocation(), Result.Val); + } + + // Match the ')'. + if (Result.isInvalid) + SkipUntil(tok::r_paren); + else { + if (Tok.is(tok::r_paren)) + RParenLoc = ConsumeParen(); + else + MatchRHSPunctuation(tok::r_paren, OpenLoc); + } + + return Result; +} + +/// ParseStringLiteralExpression - This handles the various token types that +/// form string literals, and also handles string concatenation [C99 5.1.1.2, +/// translation phase #6]. +/// +/// primary-expression: [C99 6.5.1] +/// string-literal +Parser::ExprResult Parser::ParseStringLiteralExpression() { + assert(isTokenStringLiteral() && "Not a string literal!"); + + // String concat. Note that keywords like __func__ and __FUNCTION__ are not + // considered to be strings for concatenation purposes. + llvm::SmallVector<Token, 4> StringToks; + + do { + StringToks.push_back(Tok); + ConsumeStringToken(); + } while (isTokenStringLiteral()); + + // Pass the set of string tokens, ready for concatenation, to the actions. + return Actions.ActOnStringLiteral(&StringToks[0], StringToks.size()); +} diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp new file mode 100644 index 00000000000..6b42fb5b089 --- /dev/null +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -0,0 +1,99 @@ +//===--- ParseExprCXX.cpp - C++ Expression 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 Expression parsing implementation for C++. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Parse/Parser.h" +using namespace clang; + +/// ParseCXXCasts - This handles the various ways to cast expressions to another +/// type. +/// +/// postfix-expression: [C++ 5.2p1] +/// 'dynamic_cast' '<' type-name '>' '(' expression ')' +/// 'static_cast' '<' type-name '>' '(' expression ')' +/// 'reinterpret_cast' '<' type-name '>' '(' expression ')' +/// 'const_cast' '<' type-name '>' '(' expression ')' +/// +Parser::ExprResult Parser::ParseCXXCasts() { + tok::TokenKind Kind = Tok.getKind(); + const char *CastName = 0; // For error messages + + switch (Kind) { + default: assert(0 && "Unknown C++ cast!"); abort(); + case tok::kw_const_cast: CastName = "const_cast"; break; + case tok::kw_dynamic_cast: CastName = "dynamic_cast"; break; + case tok::kw_reinterpret_cast: CastName = "reinterpret_cast"; break; + case tok::kw_static_cast: CastName = "static_cast"; break; + } + + SourceLocation OpLoc = ConsumeToken(); + SourceLocation LAngleBracketLoc = Tok.getLocation(); + + if (ExpectAndConsume(tok::less, diag::err_expected_less_after, CastName)) + return ExprResult(true); + + TypeTy *CastTy = ParseTypeName(); + SourceLocation RAngleBracketLoc = Tok.getLocation(); + + if (ExpectAndConsume(tok::greater, diag::err_expected_greater)) { + Diag(LAngleBracketLoc, diag::err_matching, "<"); + return ExprResult(true); + } + + SourceLocation LParenLoc = Tok.getLocation(), RParenLoc; + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, CastName); + return ExprResult(true); + } + + ExprResult Result = ParseSimpleParenExpression(RParenLoc); + + if (!Result.isInvalid) + Result = Actions.ActOnCXXCasts(OpLoc, Kind, + LAngleBracketLoc, CastTy, RAngleBracketLoc, + LParenLoc, Result.Val, RParenLoc); + + return Result; +} + +/// ParseCXXBoolLiteral - This handles the C++ Boolean literals. +/// +/// boolean-literal: [C++ 2.13.5] +/// 'true' +/// 'false' +Parser::ExprResult Parser::ParseCXXBoolLiteral() { + tok::TokenKind Kind = Tok.getKind(); + return Actions.ActOnCXXBoolLiteral(ConsumeToken(), Kind); +} + +/// ParseThrowExpression - This handles the C++ throw expression. +/// +/// throw-expression: [C++ 15] +/// 'throw' assignment-expression[opt] +Parser::ExprResult Parser::ParseThrowExpression() { + assert(Tok.is(tok::kw_throw) && "Not throw!"); + + ExprResult Expr; + + SourceLocation ThrowLoc = ConsumeToken(); // Eat the throw token. + // FIXME: Anything that isn't an assignment-expression should bail out now. + if (Tok.is(tok::semi) || Tok.is(tok::r_paren) || Tok.is(tok::colon) || + Tok.is(tok::comma)) + return Actions.ActOnCXXThrow(ThrowLoc); + + Expr = ParseAssignmentExpression(); + if (!Expr.isInvalid) + Expr = Actions.ActOnCXXThrow(ThrowLoc, Expr.Val); + return Expr; +} diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp new file mode 100644 index 00000000000..45cf86e5b44 --- /dev/null +++ b/clang/lib/Parse/ParseInit.cpp @@ -0,0 +1,227 @@ +//===--- ParseInit.cpp - Initializer 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 initializer parsing as specified by C99 6.7.8. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/SmallString.h" +using namespace clang; + + +/// MayBeDesignationStart - Return true if this token might be the start of a +/// designator. +static bool MayBeDesignationStart(tok::TokenKind K) { + switch (K) { + default: return false; + case tok::period: // designator: '.' identifier + case tok::l_square: // designator: array-designator + case tok::identifier: // designation: identifier ':' + return true; + } +} + +/// ParseInitializerWithPotentialDesignator - Parse the 'initializer' production +/// checking to see if the token stream starts with a designator. +/// +/// designation: +/// designator-list '=' +/// [GNU] array-designator +/// [GNU] identifier ':' +/// +/// designator-list: +/// designator +/// designator-list designator +/// +/// designator: +/// array-designator +/// '.' identifier +/// +/// array-designator: +/// '[' constant-expression ']' +/// [GNU] '[' constant-expression '...' constant-expression ']' +/// +/// NOTE: [OBC] allows '[ objc-receiver objc-message-args ]' as an +/// initializer. We need to consider this case when parsing array designators. +/// +Parser::ExprResult Parser::ParseInitializerWithPotentialDesignator() { + // Parse each designator in the designator list until we find an initializer. + while (1) { + switch (Tok.getKind()) { + case tok::equal: + // We read some number (at least one due to the grammar we implemented) + // of designators and found an '=' sign. The following tokens must be + // the initializer. + ConsumeToken(); + return ParseInitializer(); + + default: { + // We read some number (at least one due to the grammar we implemented) + // of designators and found something that isn't an = or an initializer. + // If we have exactly one array designator [TODO CHECK], this is the GNU + // 'designation: array-designator' extension. Otherwise, it is a parse + // error. + SourceLocation Loc = Tok.getLocation(); + ExprResult Init = ParseInitializer(); + if (Init.isInvalid) return Init; + + Diag(Tok, diag::ext_gnu_missing_equal_designator); + return Init; + } + case tok::period: + // designator: '.' identifier + ConsumeToken(); + if (ExpectAndConsume(tok::identifier, diag::err_expected_ident)) + return ExprResult(true); + break; + + case tok::l_square: { + // array-designator: '[' constant-expression ']' + // array-designator: '[' constant-expression '...' constant-expression ']' + // When designation is empty, this can be '[' objc-message-expr ']'. Note + // that we also have the case of [4][foo bar], which is the gnu designator + // extension + objc message send. + SourceLocation StartLoc = ConsumeBracket(); + + // If Objective-C is enabled and this is a typename or other identifier + // receiver, parse this as a message send expression. + if (getLang().ObjC1 && isTokObjCMessageIdentifierReceiver()) { + // FIXME: Emit ext_gnu_missing_equal_designator for inits like + // [4][foo bar]. + IdentifierInfo *Name = Tok.getIdentifierInfo(); + ConsumeToken(); + ExprResult R = ParseObjCMessageExpressionBody(StartLoc, Name, 0); + return ParsePostfixExpressionSuffix(R); + } + + // Note that we parse this as an assignment expression, not a constant + // expression (allowing *=, =, etc) to handle the objc case. Sema needs + // to validate that the expression is a constant. + ExprResult Idx = ParseAssignmentExpression(); + if (Idx.isInvalid) { + SkipUntil(tok::r_square); + return Idx; + } + + // Given an expression, we could either have a designator (if the next + // tokens are '...' or ']' or an objc message send. If this is an objc + // message send, handle it now. + if (getLang().ObjC1 && Tok.isNot(tok::ellipsis) && + Tok.isNot(tok::r_square)) { + // FIXME: Emit ext_gnu_missing_equal_designator for inits like + // [4][foo bar]. + ExprResult R = ParseObjCMessageExpressionBody(StartLoc, 0, Idx.Val); + return ParsePostfixExpressionSuffix(R); + } + + // Handle the gnu array range extension. + if (Tok.is(tok::ellipsis)) { + Diag(Tok, diag::ext_gnu_array_range); + ConsumeToken(); + + ExprResult RHS = ParseConstantExpression(); + if (RHS.isInvalid) { + SkipUntil(tok::r_square); + return RHS; + } + } + + MatchRHSPunctuation(tok::r_square, StartLoc); + break; + } + case tok::identifier: { + // Due to the GNU "designation: identifier ':'" extension, we don't know + // whether something starting with an identifier is an + // assignment-expression or if it is an old-style structure field + // designator. + // TODO: Check that this is the first designator. + Token Ident = Tok; + ConsumeToken(); + + // If this is the gross GNU extension, handle it now. + if (Tok.is(tok::colon)) { + Diag(Ident, diag::ext_gnu_old_style_field_designator); + ConsumeToken(); + return ParseInitializer(); + } + + // Otherwise, we just consumed the first token of an expression. Parse + // the rest of it now. + return ParseAssignmentExprWithLeadingIdentifier(Ident); + } + } + } +} + + +/// ParseInitializer +/// initializer: [C99 6.7.8] +/// assignment-expression +/// '{' initializer-list '}' +/// '{' initializer-list ',' '}' +/// [GNU] '{' '}' +/// +/// initializer-list: +/// designation[opt] initializer +/// initializer-list ',' designation[opt] initializer +/// +Parser::ExprResult Parser::ParseInitializer() { + if (Tok.isNot(tok::l_brace)) + return ParseAssignmentExpression(); + + SourceLocation LBraceLoc = ConsumeBrace(); + + // We support empty initializers, but tell the user that they aren't using + // C99-clean code. + if (Tok.is(tok::r_brace)) { + Diag(LBraceLoc, diag::ext_gnu_empty_initializer); + // Match the '}'. + return Actions.ActOnInitList(LBraceLoc, 0, 0, ConsumeBrace()); + } + llvm::SmallVector<ExprTy*, 8> InitExprs; + bool InitExprsOk = true; + + while (1) { + // Parse: designation[opt] initializer + + // If we know that this cannot be a designation, just parse the nested + // initializer directly. + ExprResult SubElt; + if (!MayBeDesignationStart(Tok.getKind())) + SubElt = ParseInitializer(); + else + SubElt = ParseInitializerWithPotentialDesignator(); + + // If we couldn't parse the subelement, bail out. + if (SubElt.isInvalid) { + InitExprsOk = false; + SkipUntil(tok::r_brace, false, true); + break; + } else + InitExprs.push_back(SubElt.Val); + + // If we don't have a comma continued list, we're done. + if (Tok.isNot(tok::comma)) break; + + // FIXME: save comma locations. + ConsumeToken(); + + // Handle trailing comma. + if (Tok.is(tok::r_brace)) break; + } + if (InitExprsOk && Tok.is(tok::r_brace)) + return Actions.ActOnInitList(LBraceLoc, &InitExprs[0], InitExprs.size(), + ConsumeBrace()); + // Match the '}'. + MatchRHSPunctuation(tok::r_brace, LBraceLoc); + return ExprResult(true); // an error occurred. +} + diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp new file mode 100644 index 00000000000..77d2adbd320 --- /dev/null +++ b/clang/lib/Parse/ParseObjc.cpp @@ -0,0 +1,1578 @@ +//===--- ParseObjC.cpp - Objective C 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 Objective-C portions of the Parser interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Parse/DeclSpec.h" +#include "clang/Parse/Scope.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/SmallVector.h" +using namespace clang; + + +/// ParseExternalDeclaration: +/// external-declaration: [C99 6.9] +/// [OBJC] objc-class-definition +/// [OBJC] objc-class-declaration +/// [OBJC] objc-alias-declaration +/// [OBJC] objc-protocol-definition +/// [OBJC] objc-method-definition +/// [OBJC] '@' 'end' +Parser::DeclTy *Parser::ParseObjCAtDirectives() { + SourceLocation AtLoc = ConsumeToken(); // the "@" + + switch (Tok.getObjCKeywordID()) { + case tok::objc_class: + return ParseObjCAtClassDeclaration(AtLoc); + case tok::objc_interface: + return ParseObjCAtInterfaceDeclaration(AtLoc); + case tok::objc_protocol: + return ParseObjCAtProtocolDeclaration(AtLoc); + case tok::objc_implementation: + return ParseObjCAtImplementationDeclaration(AtLoc); + case tok::objc_end: + return ParseObjCAtEndDeclaration(AtLoc); + case tok::objc_compatibility_alias: + return ParseObjCAtAliasDeclaration(AtLoc); + case tok::objc_synthesize: + return ParseObjCPropertySynthesize(AtLoc); + case tok::objc_dynamic: + return ParseObjCPropertyDynamic(AtLoc); + default: + Diag(AtLoc, diag::err_unexpected_at); + SkipUntil(tok::semi); + return 0; + } +} + +/// +/// objc-class-declaration: +/// '@' 'class' identifier-list ';' +/// +Parser::DeclTy *Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) { + ConsumeToken(); // the identifier "class" + llvm::SmallVector<IdentifierInfo *, 8> ClassNames; + + while (1) { + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::semi); + return 0; + } + ClassNames.push_back(Tok.getIdentifierInfo()); + ConsumeToken(); + + if (Tok.isNot(tok::comma)) + break; + + ConsumeToken(); + } + + // Consume the ';'. + if (ExpectAndConsume(tok::semi, diag::err_expected_semi_after, "@class")) + return 0; + + return Actions.ActOnForwardClassDeclaration(atLoc, + &ClassNames[0], ClassNames.size()); +} + +/// +/// objc-interface: +/// objc-class-interface-attributes[opt] objc-class-interface +/// objc-category-interface +/// +/// objc-class-interface: +/// '@' 'interface' identifier objc-superclass[opt] +/// objc-protocol-refs[opt] +/// objc-class-instance-variables[opt] +/// objc-interface-decl-list +/// @end +/// +/// objc-category-interface: +/// '@' 'interface' identifier '(' identifier[opt] ')' +/// objc-protocol-refs[opt] +/// objc-interface-decl-list +/// @end +/// +/// objc-superclass: +/// ':' identifier +/// +/// objc-class-interface-attributes: +/// __attribute__((visibility("default"))) +/// __attribute__((visibility("hidden"))) +/// __attribute__((deprecated)) +/// __attribute__((unavailable)) +/// __attribute__((objc_exception)) - used by NSException on 64-bit +/// +Parser::DeclTy *Parser::ParseObjCAtInterfaceDeclaration( + SourceLocation atLoc, AttributeList *attrList) { + assert(Tok.isObjCAtKeyword(tok::objc_interface) && + "ParseObjCAtInterfaceDeclaration(): Expected @interface"); + ConsumeToken(); // the "interface" identifier + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing class or category name. + return 0; + } + // We have a class or category name - consume it. + IdentifierInfo *nameId = Tok.getIdentifierInfo(); + SourceLocation nameLoc = ConsumeToken(); + + if (Tok.is(tok::l_paren)) { // we have a category. + SourceLocation lparenLoc = ConsumeParen(); + SourceLocation categoryLoc, rparenLoc; + IdentifierInfo *categoryId = 0; + llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs; + + // For ObjC2, the category name is optional (not an error). + if (Tok.is(tok::identifier)) { + categoryId = Tok.getIdentifierInfo(); + categoryLoc = ConsumeToken(); + } else if (!getLang().ObjC2) { + Diag(Tok, diag::err_expected_ident); // missing category name. + return 0; + } + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + SkipUntil(tok::r_paren, false); // don't stop at ';' + return 0; + } + rparenLoc = ConsumeParen(); + SourceLocation endProtoLoc; + // Next, we need to check for any protocol references. + if (Tok.is(tok::less)) { + if (ParseObjCProtocolReferences(ProtocolRefs, endProtoLoc)) + return 0; + } + if (attrList) // categories don't support attributes. + Diag(Tok, diag::err_objc_no_attributes_on_category); + + DeclTy *CategoryType = Actions.ActOnStartCategoryInterface(atLoc, + nameId, nameLoc, categoryId, categoryLoc, + &ProtocolRefs[0], ProtocolRefs.size(), + endProtoLoc); + + ParseObjCInterfaceDeclList(CategoryType, tok::objc_not_keyword); + + // The @ sign was already consumed by ParseObjCInterfaceDeclList(). + if (Tok.isObjCAtKeyword(tok::objc_end)) { + ConsumeToken(); // the "end" identifier + return CategoryType; + } + Diag(Tok, diag::err_objc_missing_end); + return 0; + } + // Parse a class interface. + IdentifierInfo *superClassId = 0; + SourceLocation superClassLoc; + + if (Tok.is(tok::colon)) { // a super class is specified. + ConsumeToken(); + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing super class name. + return 0; + } + superClassId = Tok.getIdentifierInfo(); + superClassLoc = ConsumeToken(); + } + // Next, we need to check for any protocol references. + llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs; + SourceLocation endProtoLoc; + if (Tok.is(tok::less)) { + if (ParseObjCProtocolReferences(ProtocolRefs, endProtoLoc)) + return 0; + } + DeclTy *ClsType = Actions.ActOnStartClassInterface( + atLoc, nameId, nameLoc, + superClassId, superClassLoc, &ProtocolRefs[0], + ProtocolRefs.size(), endProtoLoc, attrList); + + if (Tok.is(tok::l_brace)) + ParseObjCClassInstanceVariables(ClsType, atLoc); + + ParseObjCInterfaceDeclList(ClsType, tok::objc_interface); + + // The @ sign was already consumed by ParseObjCInterfaceDeclList(). + if (Tok.isObjCAtKeyword(tok::objc_end)) { + ConsumeToken(); // the "end" identifier + return ClsType; + } + Diag(Tok, diag::err_objc_missing_end); + return 0; +} + +/// objc-interface-decl-list: +/// empty +/// objc-interface-decl-list objc-property-decl [OBJC2] +/// objc-interface-decl-list objc-method-requirement [OBJC2] +/// objc-interface-decl-list objc-method-proto ';' +/// objc-interface-decl-list declaration +/// objc-interface-decl-list ';' +/// +/// objc-method-requirement: [OBJC2] +/// @required +/// @optional +/// +void Parser::ParseObjCInterfaceDeclList(DeclTy *interfaceDecl, + tok::ObjCKeywordKind contextKey) { + llvm::SmallVector<DeclTy*, 32> allMethods; + llvm::SmallVector<DeclTy*, 16> allProperties; + tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword; + SourceLocation AtEndLoc; + + while (1) { + if (Tok.is(tok::at)) { + SourceLocation AtLoc = ConsumeToken(); // the "@" + tok::ObjCKeywordKind ocKind = Tok.getObjCKeywordID(); + + if (ocKind == tok::objc_end) { // terminate list + AtEndLoc = AtLoc; + break; + } else if (ocKind == tok::objc_required) { // protocols only + ConsumeToken(); + MethodImplKind = ocKind; + if (contextKey != tok::objc_protocol) + Diag(AtLoc, diag::err_objc_protocol_required); + } else if (ocKind == tok::objc_optional) { // protocols only + ConsumeToken(); + MethodImplKind = ocKind; + if (contextKey != tok::objc_protocol) + Diag(AtLoc, diag::err_objc_protocol_optional); + } else if (ocKind == tok::objc_property) { + allProperties.push_back(ParseObjCPropertyDecl(interfaceDecl, AtLoc)); + continue; + } else { + Diag(Tok, diag::err_objc_illegal_interface_qual); + ConsumeToken(); + } + } + if (Tok.is(tok::minus) || Tok.is(tok::plus)) { + DeclTy *methodPrototype = + ParseObjCMethodPrototype(interfaceDecl, MethodImplKind); + allMethods.push_back(methodPrototype); + // Consume the ';' here, since ParseObjCMethodPrototype() is re-used for + // method definitions. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after,"method proto"); + continue; + } + else if (Tok.is(tok::at)) + continue; + + if (Tok.is(tok::semi)) + ConsumeToken(); + else if (Tok.is(tok::eof)) + break; + else { + // FIXME: as the name implies, this rule allows function definitions. + // We could pass a flag or check for functions during semantic analysis. + ParseDeclarationOrFunctionDefinition(); + } + } + /// Insert collected methods declarations into the @interface object. + Actions.ActOnAtEnd(AtEndLoc, interfaceDecl, &allMethods[0], allMethods.size(), + &allProperties[0], allProperties.size()); +} + +/// Parse property attribute declarations. +/// +/// property-attr-decl: '(' property-attrlist ')' +/// property-attrlist: +/// property-attribute +/// property-attrlist ',' property-attribute +/// property-attribute: +/// getter '=' identifier +/// setter '=' identifier ':' +/// readonly +/// readwrite +/// assign +/// retain +/// copy +/// nonatomic +/// +void Parser::ParseObjCPropertyAttribute (ObjCDeclSpec &DS) { + SourceLocation loc = ConsumeParen(); // consume '(' + while (isObjCPropertyAttribute()) { + const IdentifierInfo *II = Tok.getIdentifierInfo(); + // getter/setter require extra treatment. + if (II == ObjCPropertyAttrs[objc_getter] || + II == ObjCPropertyAttrs[objc_setter]) { + // skip getter/setter part. + SourceLocation loc = ConsumeToken(); + if (Tok.is(tok::equal)) { + loc = ConsumeToken(); + if (Tok.is(tok::identifier)) { + if (II == ObjCPropertyAttrs[objc_setter]) { + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_setter); + DS.setSetterName(Tok.getIdentifierInfo()); + loc = ConsumeToken(); // consume method name + if (Tok.isNot(tok::colon)) { + Diag(loc, diag::err_expected_colon); + SkipUntil(tok::r_paren,true,true); + break; + } + } + else { + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_getter); + DS.setGetterName(Tok.getIdentifierInfo()); + } + } + else { + Diag(loc, diag::err_expected_ident); + SkipUntil(tok::r_paren,true,true); + break; + } + } + else { + Diag(loc, diag::err_objc_expected_equal); + SkipUntil(tok::r_paren,true,true); + break; + } + } + + else if (II == ObjCPropertyAttrs[objc_readonly]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readonly); + else if (II == ObjCPropertyAttrs[objc_assign]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_assign); + else if (II == ObjCPropertyAttrs[objc_readwrite]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readwrite); + else if (II == ObjCPropertyAttrs[objc_retain]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_retain); + else if (II == ObjCPropertyAttrs[objc_copy]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_copy); + else if (II == ObjCPropertyAttrs[objc_nonatomic]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nonatomic); + + ConsumeToken(); // consume last attribute token + if (Tok.is(tok::comma)) { + loc = ConsumeToken(); + continue; + } + if (Tok.is(tok::r_paren)) + break; + Diag(loc, diag::err_expected_rparen); + SkipUntil(tok::semi); + return; + } + if (Tok.is(tok::r_paren)) + ConsumeParen(); + else { + Diag(loc, diag::err_objc_expected_property_attr); + SkipUntil(tok::r_paren); // recover from error inside attribute list + } +} + +/// Main routine to parse property declaration. +/// +/// @property property-attr-decl[opt] property-component-decl ';' +/// +Parser::DeclTy *Parser::ParseObjCPropertyDecl(DeclTy *interfaceDecl, + SourceLocation AtLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_property) && + "ParseObjCPropertyDecl(): Expected @property"); + ObjCDeclSpec DS; + ConsumeToken(); // the "property" identifier + // Parse property attribute list, if any. + if (Tok.is(tok::l_paren)) { + // property has attribute list. + ParseObjCPropertyAttribute(DS); + } + // Parse declaration portion of @property. + llvm::SmallVector<DeclTy*, 8> PropertyDecls; + ParseStructDeclaration(interfaceDecl, PropertyDecls); + if (Tok.is(tok::semi)) + ConsumeToken(); + else { + Diag(Tok, diag::err_expected_semi_decl_list); + SkipUntil(tok::r_brace, true, true); + } + return Actions.ActOnAddObjCProperties(AtLoc, + &PropertyDecls[0], PropertyDecls.size(), DS); +} + +/// objc-method-proto: +/// objc-instance-method objc-method-decl objc-method-attributes[opt] +/// objc-class-method objc-method-decl objc-method-attributes[opt] +/// +/// objc-instance-method: '-' +/// objc-class-method: '+' +/// +/// objc-method-attributes: [OBJC2] +/// __attribute__((deprecated)) +/// +Parser::DeclTy *Parser::ParseObjCMethodPrototype(DeclTy *IDecl, + tok::ObjCKeywordKind MethodImplKind) { + assert((Tok.is(tok::minus) || Tok.is(tok::plus)) && "expected +/-"); + + tok::TokenKind methodType = Tok.getKind(); + SourceLocation mLoc = ConsumeToken(); + + DeclTy *MDecl = ParseObjCMethodDecl(mLoc, methodType, IDecl, MethodImplKind); + // Since this rule is used for both method declarations and definitions, + // the caller is (optionally) responsible for consuming the ';'. + return MDecl; +} + +/// objc-selector: +/// identifier +/// one of +/// enum struct union if else while do for switch case default +/// break continue return goto asm sizeof typeof __alignof +/// unsigned long const short volatile signed restrict _Complex +/// in out inout bycopy byref oneway int char float double void _Bool +/// +IdentifierInfo *Parser::ParseObjCSelector(SourceLocation &SelectorLoc) { + switch (Tok.getKind()) { + default: + return 0; + case tok::identifier: + case tok::kw_typeof: + case tok::kw___alignof: + case tok::kw_auto: + case tok::kw_break: + case tok::kw_case: + case tok::kw_char: + case tok::kw_const: + case tok::kw_continue: + case tok::kw_default: + case tok::kw_do: + case tok::kw_double: + case tok::kw_else: + case tok::kw_enum: + case tok::kw_extern: + case tok::kw_float: + case tok::kw_for: + case tok::kw_goto: + case tok::kw_if: + case tok::kw_inline: + case tok::kw_int: + case tok::kw_long: + case tok::kw_register: + case tok::kw_restrict: + case tok::kw_return: + case tok::kw_short: + case tok::kw_signed: + case tok::kw_sizeof: + case tok::kw_static: + case tok::kw_struct: + case tok::kw_switch: + case tok::kw_typedef: + case tok::kw_union: + case tok::kw_unsigned: + case tok::kw_void: + case tok::kw_volatile: + case tok::kw_while: + case tok::kw_bool: + case tok::kw__Bool: + case tok::kw__Complex: + IdentifierInfo *II = Tok.getIdentifierInfo(); + SelectorLoc = ConsumeToken(); + return II; + } +} + +/// property-attrlist: one of +/// readonly getter setter assign retain copy nonatomic +/// +bool Parser::isObjCPropertyAttribute() { + if (Tok.is(tok::identifier)) { + const IdentifierInfo *II = Tok.getIdentifierInfo(); + for (unsigned i = 0; i < objc_NumAttrs; ++i) + if (II == ObjCPropertyAttrs[i]) return true; + } + return false; +} + +/// objc-for-collection-in: 'in' +/// +bool Parser::isTokIdentifier_in() const { + // FIXME: May have to do additional look-ahead to only allow for + // valid tokens following an 'in'; such as an identifier, unary operators, + // '[' etc. + return (getLang().ObjC2 && Tok.is(tok::identifier) && + Tok.getIdentifierInfo() == ObjCForCollectionInKW); +} + +/// ParseObjCTypeQualifierList - This routine parses the objective-c's type +/// qualifier list and builds their bitmask representation in the input +/// argument. +/// +/// objc-type-qualifiers: +/// objc-type-qualifier +/// objc-type-qualifiers objc-type-qualifier +/// +void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS) { + while (1) { + if (Tok.isNot(tok::identifier)) + return; + + const IdentifierInfo *II = Tok.getIdentifierInfo(); + for (unsigned i = 0; i != objc_NumQuals; ++i) { + if (II != ObjCTypeQuals[i]) + continue; + + ObjCDeclSpec::ObjCDeclQualifier Qual; + switch (i) { + default: assert(0 && "Unknown decl qualifier"); + case objc_in: Qual = ObjCDeclSpec::DQ_In; break; + case objc_out: Qual = ObjCDeclSpec::DQ_Out; break; + case objc_inout: Qual = ObjCDeclSpec::DQ_Inout; break; + case objc_oneway: Qual = ObjCDeclSpec::DQ_Oneway; break; + case objc_bycopy: Qual = ObjCDeclSpec::DQ_Bycopy; break; + case objc_byref: Qual = ObjCDeclSpec::DQ_Byref; break; + } + DS.setObjCDeclQualifier(Qual); + ConsumeToken(); + II = 0; + break; + } + + // If this wasn't a recognized qualifier, bail out. + if (II) return; + } +} + +/// objc-type-name: +/// '(' objc-type-qualifiers[opt] type-name ')' +/// '(' objc-type-qualifiers[opt] ')' +/// +Parser::TypeTy *Parser::ParseObjCTypeName(ObjCDeclSpec &DS) { + assert(Tok.is(tok::l_paren) && "expected ("); + + SourceLocation LParenLoc = ConsumeParen(), RParenLoc; + TypeTy *Ty = 0; + + // Parse type qualifiers, in, inout, etc. + ParseObjCTypeQualifierList(DS); + + if (isTypeSpecifierQualifier()) { + Ty = ParseTypeName(); + // FIXME: back when Sema support is in place... + // assert(Ty && "Parser::ParseObjCTypeName(): missing type"); + } + if (Tok.isNot(tok::r_paren)) { + MatchRHSPunctuation(tok::r_paren, LParenLoc); + return 0; // FIXME: decide how we want to handle this error... + } + RParenLoc = ConsumeParen(); + return Ty; +} + +/// objc-method-decl: +/// objc-selector +/// objc-keyword-selector objc-parmlist[opt] +/// objc-type-name objc-selector +/// objc-type-name objc-keyword-selector objc-parmlist[opt] +/// +/// objc-keyword-selector: +/// objc-keyword-decl +/// objc-keyword-selector objc-keyword-decl +/// +/// objc-keyword-decl: +/// objc-selector ':' objc-type-name objc-keyword-attributes[opt] identifier +/// objc-selector ':' objc-keyword-attributes[opt] identifier +/// ':' objc-type-name objc-keyword-attributes[opt] identifier +/// ':' objc-keyword-attributes[opt] identifier +/// +/// objc-parmlist: +/// objc-parms objc-ellipsis[opt] +/// +/// objc-parms: +/// objc-parms , parameter-declaration +/// +/// objc-ellipsis: +/// , ... +/// +/// objc-keyword-attributes: [OBJC2] +/// __attribute__((unused)) +/// +Parser::DeclTy *Parser::ParseObjCMethodDecl(SourceLocation mLoc, + tok::TokenKind mType, + DeclTy *IDecl, + tok::ObjCKeywordKind MethodImplKind) +{ + // Parse the return type. + TypeTy *ReturnType = 0; + ObjCDeclSpec DSRet; + if (Tok.is(tok::l_paren)) + ReturnType = ParseObjCTypeName(DSRet); + SourceLocation selLoc; + IdentifierInfo *SelIdent = ParseObjCSelector(selLoc); + if (Tok.isNot(tok::colon)) { + if (!SelIdent) { + Diag(Tok, diag::err_expected_ident); // missing selector name. + // FIXME: this creates a unary selector with a null identifier, is this + // ok?? Maybe we should skip to the next semicolon or something. + } + + // If attributes exist after the method, parse them. + AttributeList *MethodAttrs = 0; + if (getLang().ObjC2 && Tok.is(tok::kw___attribute)) + MethodAttrs = ParseAttributes(); + + Selector Sel = PP.getSelectorTable().getNullarySelector(SelIdent); + return Actions.ActOnMethodDeclaration(mLoc, Tok.getLocation(), + mType, IDecl, DSRet, ReturnType, Sel, + 0, 0, 0, MethodAttrs, MethodImplKind); + } + + llvm::SmallVector<IdentifierInfo *, 12> KeyIdents; + llvm::SmallVector<Action::TypeTy *, 12> KeyTypes; + llvm::SmallVector<ObjCDeclSpec, 12> ArgTypeQuals; + llvm::SmallVector<IdentifierInfo *, 12> ArgNames; + + Action::TypeTy *TypeInfo; + while (1) { + KeyIdents.push_back(SelIdent); + + // Each iteration parses a single keyword argument. + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon); + break; + } + ConsumeToken(); // Eat the ':'. + ObjCDeclSpec DSType; + if (Tok.is(tok::l_paren)) { // Parse the argument type. + TypeInfo = ParseObjCTypeName(DSType); + } + else + TypeInfo = 0; + KeyTypes.push_back(TypeInfo); + ArgTypeQuals.push_back(DSType); + + // If attributes exist before the argument name, parse them. + if (getLang().ObjC2 && Tok.is(tok::kw___attribute)) + ParseAttributes(); // FIXME: pass attributes through. + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing argument name. + break; + } + ArgNames.push_back(Tok.getIdentifierInfo()); + ConsumeToken(); // Eat the identifier. + + // Check for another keyword selector. + SourceLocation Loc; + SelIdent = ParseObjCSelector(Loc); + if (!SelIdent && Tok.isNot(tok::colon)) + break; + // We have a selector or a colon, continue parsing. + } + + bool isVariadic = false; + + // Parse the (optional) parameter list. + while (Tok.is(tok::comma)) { + ConsumeToken(); + if (Tok.is(tok::ellipsis)) { + isVariadic = true; + ConsumeToken(); + break; + } + // FIXME: implement this... + // Parse the c-style argument declaration-specifier. + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + // Parse the declarator. + Declarator ParmDecl(DS, Declarator::PrototypeContext); + ParseDeclarator(ParmDecl); + } + + // FIXME: Add support for optional parmameter list... + // If attributes exist after the method, parse them. + AttributeList *MethodAttrs = 0; + if (getLang().ObjC2 && Tok.is(tok::kw___attribute)) + MethodAttrs = ParseAttributes(); + + Selector Sel = PP.getSelectorTable().getSelector(KeyIdents.size(), + &KeyIdents[0]); + return Actions.ActOnMethodDeclaration(mLoc, Tok.getLocation(), + mType, IDecl, DSRet, ReturnType, Sel, + &ArgTypeQuals[0], &KeyTypes[0], + &ArgNames[0], MethodAttrs, + MethodImplKind, isVariadic); +} + +/// CmpProtocolVals - Comparison predicate for sorting protocols. +static bool CmpProtocolVals(const IdentifierInfo* const& lhs, + const IdentifierInfo* const& rhs) { + return strcmp(lhs->getName(), rhs->getName()) < 0; +} + +/// objc-protocol-refs: +/// '<' identifier-list '>' +/// +bool Parser::ParseObjCProtocolReferences( + llvm::SmallVectorImpl<IdentifierInfo*> &ProtocolRefs, SourceLocation &endLoc) +{ + assert(Tok.is(tok::less) && "expected <"); + + ConsumeToken(); // the "<" + + while (1) { + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::greater); + return true; + } + ProtocolRefs.push_back(Tok.getIdentifierInfo()); + ConsumeToken(); + + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); + } + + // Sort protocols, keyed by name. + // Later on, we remove duplicates. + std::stable_sort(ProtocolRefs.begin(), ProtocolRefs.end(), CmpProtocolVals); + + // Make protocol names unique. + ProtocolRefs.erase(std::unique(ProtocolRefs.begin(), ProtocolRefs.end()), + ProtocolRefs.end()); + // Consume the '>'. + if (Tok.is(tok::greater)) { + endLoc = ConsumeAnyToken(); + return false; + } + Diag(Tok, diag::err_expected_greater); + return true; +} + +/// objc-class-instance-variables: +/// '{' objc-instance-variable-decl-list[opt] '}' +/// +/// objc-instance-variable-decl-list: +/// objc-visibility-spec +/// objc-instance-variable-decl ';' +/// ';' +/// objc-instance-variable-decl-list objc-visibility-spec +/// objc-instance-variable-decl-list objc-instance-variable-decl ';' +/// objc-instance-variable-decl-list ';' +/// +/// objc-visibility-spec: +/// @private +/// @protected +/// @public +/// @package [OBJC2] +/// +/// objc-instance-variable-decl: +/// struct-declaration +/// +void Parser::ParseObjCClassInstanceVariables(DeclTy *interfaceDecl, + SourceLocation atLoc) { + assert(Tok.is(tok::l_brace) && "expected {"); + llvm::SmallVector<DeclTy*, 16> IvarDecls; + llvm::SmallVector<DeclTy*, 32> AllIvarDecls; + llvm::SmallVector<tok::ObjCKeywordKind, 32> AllVisibilities; + + SourceLocation LBraceLoc = ConsumeBrace(); // the "{" + + tok::ObjCKeywordKind visibility = tok::objc_private; + // While we still have something to read, read the instance variables. + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + // Each iteration of this loop reads one objc-instance-variable-decl. + + // Check for extraneous top-level semicolon. + if (Tok.is(tok::semi)) { + Diag(Tok, diag::ext_extra_struct_semi); + ConsumeToken(); + continue; + } + // Set the default visibility to private. + if (Tok.is(tok::at)) { // parse objc-visibility-spec + ConsumeToken(); // eat the @ sign + switch (Tok.getObjCKeywordID()) { + case tok::objc_private: + case tok::objc_public: + case tok::objc_protected: + case tok::objc_package: + visibility = Tok.getObjCKeywordID(); + ConsumeToken(); + continue; + default: + Diag(Tok, diag::err_objc_illegal_visibility_spec); + ConsumeToken(); + continue; + } + } + ParseStructDeclaration(interfaceDecl, IvarDecls); + for (unsigned i = 0; i < IvarDecls.size(); i++) { + AllIvarDecls.push_back(IvarDecls[i]); + AllVisibilities.push_back(visibility); + } + IvarDecls.clear(); + + if (Tok.is(tok::semi)) { + ConsumeToken(); + } else if (Tok.is(tok::r_brace)) { + Diag(Tok.getLocation(), diag::ext_expected_semi_decl_list); + break; + } else { + Diag(Tok, diag::err_expected_semi_decl_list); + // Skip to end of block or statement + SkipUntil(tok::r_brace, true, true); + } + } + SourceLocation RBraceLoc = MatchRHSPunctuation(tok::r_brace, LBraceLoc); + // Call ActOnFields() even if we don't have any decls. This is useful + // for code rewriting tools that need to be aware of the empty list. + Actions.ActOnFields(CurScope, atLoc, interfaceDecl, + &AllIvarDecls[0], AllIvarDecls.size(), + LBraceLoc, RBraceLoc, &AllVisibilities[0]); + return; +} + +/// objc-protocol-declaration: +/// objc-protocol-definition +/// objc-protocol-forward-reference +/// +/// objc-protocol-definition: +/// @protocol identifier +/// objc-protocol-refs[opt] +/// objc-interface-decl-list +/// @end +/// +/// objc-protocol-forward-reference: +/// @protocol identifier-list ';' +/// +/// "@protocol identifier ;" should be resolved as "@protocol +/// identifier-list ;": objc-interface-decl-list may not start with a +/// semicolon in the first alternative if objc-protocol-refs are omitted. + +Parser::DeclTy *Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_protocol) && + "ParseObjCAtProtocolDeclaration(): Expected @protocol"); + ConsumeToken(); // the "protocol" identifier + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing protocol name. + return 0; + } + // Save the protocol name, then consume it. + IdentifierInfo *protocolName = Tok.getIdentifierInfo(); + SourceLocation nameLoc = ConsumeToken(); + + llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs; + if (Tok.is(tok::semi)) { // forward declaration of one protocol. + ConsumeToken(); + ProtocolRefs.push_back(protocolName); + } + if (Tok.is(tok::comma)) { // list of forward declarations. + // Parse the list of forward declarations. + ProtocolRefs.push_back(protocolName); + + while (1) { + ConsumeToken(); // the ',' + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::semi); + return 0; + } + ProtocolRefs.push_back(Tok.getIdentifierInfo()); + ConsumeToken(); // the identifier + + if (Tok.isNot(tok::comma)) + break; + } + // Consume the ';'. + if (ExpectAndConsume(tok::semi, diag::err_expected_semi_after, "@protocol")) + return 0; + } + if (ProtocolRefs.size() > 0) + return Actions.ActOnForwardProtocolDeclaration(AtLoc, + &ProtocolRefs[0], + ProtocolRefs.size()); + // Last, and definitely not least, parse a protocol declaration. + SourceLocation endProtoLoc; + if (Tok.is(tok::less)) { + if (ParseObjCProtocolReferences(ProtocolRefs, endProtoLoc)) + return 0; + } + + DeclTy *ProtoType = Actions.ActOnStartProtocolInterface(AtLoc, + protocolName, nameLoc, + &ProtocolRefs[0], + ProtocolRefs.size(), endProtoLoc); + ParseObjCInterfaceDeclList(ProtoType, tok::objc_protocol); + + // The @ sign was already consumed by ParseObjCInterfaceDeclList(). + if (Tok.isObjCAtKeyword(tok::objc_end)) { + ConsumeToken(); // the "end" identifier + return ProtoType; + } + Diag(Tok, diag::err_objc_missing_end); + return 0; +} + +/// objc-implementation: +/// objc-class-implementation-prologue +/// objc-category-implementation-prologue +/// +/// objc-class-implementation-prologue: +/// @implementation identifier objc-superclass[opt] +/// objc-class-instance-variables[opt] +/// +/// objc-category-implementation-prologue: +/// @implementation identifier ( identifier ) + +Parser::DeclTy *Parser::ParseObjCAtImplementationDeclaration( + SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_implementation) && + "ParseObjCAtImplementationDeclaration(): Expected @implementation"); + ConsumeToken(); // the "implementation" identifier + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing class or category name. + return 0; + } + // We have a class or category name - consume it. + IdentifierInfo *nameId = Tok.getIdentifierInfo(); + SourceLocation nameLoc = ConsumeToken(); // consume class or category name + + if (Tok.is(tok::l_paren)) { + // we have a category implementation. + SourceLocation lparenLoc = ConsumeParen(); + SourceLocation categoryLoc, rparenLoc; + IdentifierInfo *categoryId = 0; + + if (Tok.is(tok::identifier)) { + categoryId = Tok.getIdentifierInfo(); + categoryLoc = ConsumeToken(); + } else { + Diag(Tok, diag::err_expected_ident); // missing category name. + return 0; + } + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + SkipUntil(tok::r_paren, false); // don't stop at ';' + return 0; + } + rparenLoc = ConsumeParen(); + DeclTy *ImplCatType = Actions.ActOnStartCategoryImplementation( + atLoc, nameId, nameLoc, categoryId, + categoryLoc); + ObjCImpDecl = ImplCatType; + return 0; + } + // We have a class implementation + SourceLocation superClassLoc; + IdentifierInfo *superClassId = 0; + if (Tok.is(tok::colon)) { + // We have a super class + ConsumeToken(); + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing super class name. + return 0; + } + superClassId = Tok.getIdentifierInfo(); + superClassLoc = ConsumeToken(); // Consume super class name + } + DeclTy *ImplClsType = Actions.ActOnStartClassImplementation( + atLoc, nameId, nameLoc, + superClassId, superClassLoc); + + if (Tok.is(tok::l_brace)) // we have ivars + ParseObjCClassInstanceVariables(ImplClsType/*FIXME*/, atLoc); + ObjCImpDecl = ImplClsType; + + return 0; +} + +Parser::DeclTy *Parser::ParseObjCAtEndDeclaration(SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_end) && + "ParseObjCAtEndDeclaration(): Expected @end"); + ConsumeToken(); // the "end" identifier + if (ObjCImpDecl) + Actions.ActOnAtEnd(atLoc, ObjCImpDecl); + else + Diag(atLoc, diag::warn_expected_implementation); // missing @implementation + return ObjCImpDecl; +} + +/// compatibility-alias-decl: +/// @compatibility_alias alias-name class-name ';' +/// +Parser::DeclTy *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_compatibility_alias) && + "ParseObjCAtAliasDeclaration(): Expected @compatibility_alias"); + ConsumeToken(); // consume compatibility_alias + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return 0; + } + IdentifierInfo *aliasId = Tok.getIdentifierInfo(); + SourceLocation aliasLoc = ConsumeToken(); // consume alias-name + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return 0; + } + IdentifierInfo *classId = Tok.getIdentifierInfo(); + SourceLocation classLoc = ConsumeToken(); // consume class-name; + if (Tok.isNot(tok::semi)) { + Diag(Tok, diag::err_expected_semi_after, "@compatibility_alias"); + return 0; + } + DeclTy *ClsType = Actions.ActOnCompatiblityAlias(atLoc, + aliasId, aliasLoc, + classId, classLoc); + return ClsType; +} + +/// property-synthesis: +/// @synthesize property-ivar-list ';' +/// +/// property-ivar-list: +/// property-ivar +/// property-ivar-list ',' property-ivar +/// +/// property-ivar: +/// identifier +/// identifier '=' identifier +/// +Parser::DeclTy *Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_synthesize) && + "ParseObjCPropertyDynamic(): Expected '@synthesize'"); + SourceLocation loc = ConsumeToken(); // consume dynamic + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return 0; + } + while (Tok.is(tok::identifier)) { + ConsumeToken(); // consume property name + if (Tok.is(tok::equal)) { + // property '=' ivar-name + ConsumeToken(); // consume '=' + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + break; + } + ConsumeToken(); // consume ivar-name + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // consume ',' + } + if (Tok.isNot(tok::semi)) + Diag(Tok, diag::err_expected_semi_after, "@synthesize"); + return 0; +} + +/// property-dynamic: +/// @dynamic property-list +/// +/// property-list: +/// identifier +/// property-list ',' identifier +/// +Parser::DeclTy *Parser::ParseObjCPropertyDynamic(SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_dynamic) && + "ParseObjCPropertyDynamic(): Expected '@dynamic'"); + SourceLocation loc = ConsumeToken(); // consume dynamic + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return 0; + } + while (Tok.is(tok::identifier)) { + ConsumeToken(); // consume property name + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // consume ',' + } + if (Tok.isNot(tok::semi)) + Diag(Tok, diag::err_expected_semi_after, "@dynamic"); + return 0; +} + +/// objc-throw-statement: +/// throw expression[opt]; +/// +Parser::StmtResult Parser::ParseObjCThrowStmt(SourceLocation atLoc) { + ExprResult Res; + ConsumeToken(); // consume throw + if (Tok.isNot(tok::semi)) { + Res = ParseExpression(); + if (Res.isInvalid) { + SkipUntil(tok::semi); + return true; + } + } + ConsumeToken(); // consume ';' + return Actions.ActOnObjCAtThrowStmt(atLoc, Res.Val); +} + +/// objc-synchronized-statement: +/// @synchronized '(' expression ')' compound-statement +/// +Parser::StmtResult Parser::ParseObjCSynchronizedStmt(SourceLocation atLoc) { + ConsumeToken(); // consume synchronized + if (Tok.isNot(tok::l_paren)) { + Diag (Tok, diag::err_expected_lparen_after, "@synchronized"); + return true; + } + ConsumeParen(); // '(' + ExprResult Res = ParseExpression(); + if (Res.isInvalid) { + SkipUntil(tok::semi); + return true; + } + if (Tok.isNot(tok::r_paren)) { + Diag (Tok, diag::err_expected_lbrace); + return true; + } + ConsumeParen(); // ')' + if (Tok.isNot(tok::l_brace)) { + Diag (Tok, diag::err_expected_lbrace); + return true; + } + StmtResult SynchBody = ParseCompoundStatementBody(); + if (SynchBody.isInvalid) + SynchBody = Actions.ActOnNullStmt(Tok.getLocation()); + return Actions.ActOnObjCAtSynchronizedStmt(atLoc, Res.Val, SynchBody.Val); +} + +/// objc-try-catch-statement: +/// @try compound-statement objc-catch-list[opt] +/// @try compound-statement objc-catch-list[opt] @finally compound-statement +/// +/// objc-catch-list: +/// @catch ( parameter-declaration ) compound-statement +/// objc-catch-list @catch ( catch-parameter-declaration ) compound-statement +/// catch-parameter-declaration: +/// parameter-declaration +/// '...' [OBJC2] +/// +Parser::StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { + bool catch_or_finally_seen = false; + + ConsumeToken(); // consume try + if (Tok.isNot(tok::l_brace)) { + Diag (Tok, diag::err_expected_lbrace); + return true; + } + StmtResult CatchStmts; + StmtResult FinallyStmt; + StmtResult TryBody = ParseCompoundStatementBody(); + if (TryBody.isInvalid) + TryBody = Actions.ActOnNullStmt(Tok.getLocation()); + + while (Tok.is(tok::at)) { + // At this point, we need to lookahead to determine if this @ is the start + // of an @catch or @finally. We don't want to consume the @ token if this + // is an @try or @encode or something else. + Token AfterAt = GetLookAheadToken(1); + if (!AfterAt.isObjCAtKeyword(tok::objc_catch) && + !AfterAt.isObjCAtKeyword(tok::objc_finally)) + break; + + SourceLocation AtCatchFinallyLoc = ConsumeToken(); + if (Tok.isObjCAtKeyword(tok::objc_catch)) { + StmtTy *FirstPart = 0; + ConsumeToken(); // consume catch + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + EnterScope(Scope::DeclScope); + if (Tok.isNot(tok::ellipsis)) { + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + // FIXME: Is BlockContext right? + Declarator DeclaratorInfo(DS, Declarator::BlockContext); + ParseDeclarator(DeclaratorInfo); + DeclTy *aBlockVarDecl = Actions.ActOnDeclarator(CurScope, + DeclaratorInfo, 0); + StmtResult stmtResult = + Actions.ActOnDeclStmt(aBlockVarDecl, DS.getSourceRange().getBegin(), + DeclaratorInfo.getSourceRange().getEnd()); + FirstPart = stmtResult.isInvalid ? 0 : stmtResult.Val; + } else + ConsumeToken(); // consume '...' + SourceLocation RParenLoc = ConsumeParen(); + + StmtResult CatchBody(true); + if (Tok.is(tok::l_brace)) + CatchBody = ParseCompoundStatementBody(); + else + Diag(Tok, diag::err_expected_lbrace); + if (CatchBody.isInvalid) + CatchBody = Actions.ActOnNullStmt(Tok.getLocation()); + CatchStmts = Actions.ActOnObjCAtCatchStmt(AtCatchFinallyLoc, RParenLoc, + FirstPart, CatchBody.Val, CatchStmts.Val); + ExitScope(); + } else { + Diag(AtCatchFinallyLoc, diag::err_expected_lparen_after, + "@catch clause"); + return true; + } + catch_or_finally_seen = true; + } else { + assert(Tok.isObjCAtKeyword(tok::objc_finally) && "Lookahead confused?"); + ConsumeToken(); // consume finally + + StmtResult FinallyBody(true); + if (Tok.is(tok::l_brace)) + FinallyBody = ParseCompoundStatementBody(); + else + Diag(Tok, diag::err_expected_lbrace); + if (FinallyBody.isInvalid) + FinallyBody = Actions.ActOnNullStmt(Tok.getLocation()); + FinallyStmt = Actions.ActOnObjCAtFinallyStmt(AtCatchFinallyLoc, + FinallyBody.Val); + catch_or_finally_seen = true; + break; + } + } + if (!catch_or_finally_seen) { + Diag(atLoc, diag::err_missing_catch_finally); + return true; + } + return Actions.ActOnObjCAtTryStmt(atLoc, TryBody.Val, CatchStmts.Val, + FinallyStmt.Val); +} + +/// objc-method-def: objc-method-proto ';'[opt] '{' body '}' +/// +Parser::DeclTy *Parser::ParseObjCMethodDefinition() { + DeclTy *MDecl = ParseObjCMethodPrototype(ObjCImpDecl); + // parse optional ';' + if (Tok.is(tok::semi)) + ConsumeToken(); + + // We should have an opening brace now. + if (Tok.isNot(tok::l_brace)) { + Diag(Tok, diag::err_expected_method_body); + + // Skip over garbage, until we get to '{'. Don't eat the '{'. + SkipUntil(tok::l_brace, true, true); + + // If we didn't find the '{', bail out. + if (Tok.isNot(tok::l_brace)) + return 0; + } + SourceLocation BraceLoc = Tok.getLocation(); + + // Enter a scope for the method body. + EnterScope(Scope::FnScope|Scope::DeclScope); + + // Tell the actions module that we have entered a method definition with the + // specified Declarator for the method. + Actions.ObjCActOnStartOfMethodDef(CurScope, MDecl); + + StmtResult FnBody = ParseCompoundStatementBody(); + + // If the function body could not be parsed, make a bogus compoundstmt. + if (FnBody.isInvalid) + FnBody = Actions.ActOnCompoundStmt(BraceLoc, BraceLoc, 0, 0, false); + + // Leave the function body scope. + ExitScope(); + + // TODO: Pass argument information. + Actions.ActOnFinishFunctionBody(MDecl, FnBody.Val); + return MDecl; +} + +Parser::StmtResult Parser::ParseObjCAtStatement(SourceLocation AtLoc) { + if (Tok.isObjCAtKeyword(tok::objc_try)) { + return ParseObjCTryStmt(AtLoc); + } else if (Tok.isObjCAtKeyword(tok::objc_throw)) + return ParseObjCThrowStmt(AtLoc); + else if (Tok.isObjCAtKeyword(tok::objc_synchronized)) + return ParseObjCSynchronizedStmt(AtLoc); + ExprResult Res = ParseExpressionWithLeadingAt(AtLoc); + if (Res.isInvalid) { + // If the expression is invalid, skip ahead to the next semicolon. Not + // doing this opens us up to the possibility of infinite loops if + // ParseExpression does not consume any tokens. + SkipUntil(tok::semi); + return true; + } + // Otherwise, eat the semicolon. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_expr); + return Actions.ActOnExprStmt(Res.Val); +} + +Parser::ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { + + switch (Tok.getKind()) { + case tok::string_literal: // primary-expression: string-literal + case tok::wide_string_literal: + return ParsePostfixExpressionSuffix(ParseObjCStringLiteral(AtLoc)); + default: + break; + } + + switch (Tok.getIdentifierInfo()->getObjCKeywordID()) { + case tok::objc_encode: + return ParsePostfixExpressionSuffix(ParseObjCEncodeExpression(AtLoc)); + case tok::objc_protocol: + return ParsePostfixExpressionSuffix(ParseObjCProtocolExpression(AtLoc)); + case tok::objc_selector: + return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc)); + default: + Diag(AtLoc, diag::err_unexpected_at); + SkipUntil(tok::semi); + return true; + } +} + +/// objc-message-expr: +/// '[' objc-receiver objc-message-args ']' +/// +/// objc-receiver: +/// expression +/// class-name +/// type-name +Parser::ExprResult Parser::ParseObjCMessageExpression() { + assert(Tok.is(tok::l_square) && "'[' expected"); + SourceLocation LBracLoc = ConsumeBracket(); // consume '[' + + // Parse receiver + if (isTokObjCMessageIdentifierReceiver()) { + IdentifierInfo *ReceiverName = Tok.getIdentifierInfo(); + ConsumeToken(); + return ParseObjCMessageExpressionBody(LBracLoc, ReceiverName, 0); + } + + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid) { + Diag(Tok, diag::err_invalid_receiver_to_message); + SkipUntil(tok::r_square); + return Res; + } + return ParseObjCMessageExpressionBody(LBracLoc, 0, Res.Val); +} + +/// ParseObjCMessageExpressionBody - Having parsed "'[' objc-receiver", parse +/// the rest of a message expression. +/// +/// objc-message-args: +/// objc-selector +/// objc-keywordarg-list +/// +/// objc-keywordarg-list: +/// objc-keywordarg +/// objc-keywordarg-list objc-keywordarg +/// +/// objc-keywordarg: +/// selector-name[opt] ':' objc-keywordexpr +/// +/// objc-keywordexpr: +/// nonempty-expr-list +/// +/// nonempty-expr-list: +/// assignment-expression +/// nonempty-expr-list , assignment-expression +/// +Parser::ExprResult +Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc, + IdentifierInfo *ReceiverName, + ExprTy *ReceiverExpr) { + // Parse objc-selector + SourceLocation Loc; + IdentifierInfo *selIdent = ParseObjCSelector(Loc); + + llvm::SmallVector<IdentifierInfo *, 12> KeyIdents; + llvm::SmallVector<Action::ExprTy *, 12> KeyExprs; + + if (Tok.is(tok::colon)) { + while (1) { + // Each iteration parses a single keyword argument. + KeyIdents.push_back(selIdent); + + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon); + SkipUntil(tok::semi); + return true; + } + ConsumeToken(); // Eat the ':'. + /// Parse the expression after ':' + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid) { + SkipUntil(tok::identifier); + return Res; + } + // We have a valid expression. + KeyExprs.push_back(Res.Val); + + // Check for another keyword selector. + selIdent = ParseObjCSelector(Loc); + if (!selIdent && Tok.isNot(tok::colon)) + break; + // We have a selector or a colon, continue parsing. + } + // Parse the, optional, argument list, comma separated. + while (Tok.is(tok::comma)) { + ConsumeToken(); // Eat the ','. + /// Parse the expression after ',' + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid) { + SkipUntil(tok::identifier); + return Res; + } + // We have a valid expression. + KeyExprs.push_back(Res.Val); + } + } else if (!selIdent) { + Diag(Tok, diag::err_expected_ident); // missing selector name. + SkipUntil(tok::semi); + return true; + } + + if (Tok.isNot(tok::r_square)) { + Diag(Tok, diag::err_expected_rsquare); + SkipUntil(tok::semi); + return true; + } + SourceLocation RBracLoc = ConsumeBracket(); // consume ']' + + unsigned nKeys = KeyIdents.size(); + if (nKeys == 0) + KeyIdents.push_back(selIdent); + Selector Sel = PP.getSelectorTable().getSelector(nKeys, &KeyIdents[0]); + + // We've just parsed a keyword message. + if (ReceiverName) + return Actions.ActOnClassMessage(CurScope, + ReceiverName, Sel, LBracLoc, RBracLoc, + &KeyExprs[0], KeyExprs.size()); + return Actions.ActOnInstanceMessage(ReceiverExpr, Sel, LBracLoc, RBracLoc, + &KeyExprs[0], KeyExprs.size()); +} + +Parser::ExprResult Parser::ParseObjCStringLiteral(SourceLocation AtLoc) { + ExprResult Res = ParseStringLiteralExpression(); + if (Res.isInvalid) return Res; + + // @"foo" @"bar" is a valid concatenated string. Eat any subsequent string + // expressions. At this point, we know that the only valid thing that starts + // with '@' is an @"". + llvm::SmallVector<SourceLocation, 4> AtLocs; + llvm::SmallVector<ExprTy*, 4> AtStrings; + AtLocs.push_back(AtLoc); + AtStrings.push_back(Res.Val); + + while (Tok.is(tok::at)) { + AtLocs.push_back(ConsumeToken()); // eat the @. + + ExprResult Res(true); // Invalid unless there is a string literal. + if (isTokenStringLiteral()) + Res = ParseStringLiteralExpression(); + else + Diag(Tok, diag::err_objc_concat_string); + + if (Res.isInvalid) { + while (!AtStrings.empty()) { + Actions.DeleteExpr(AtStrings.back()); + AtStrings.pop_back(); + } + return Res; + } + + AtStrings.push_back(Res.Val); + } + + return Actions.ParseObjCStringLiteral(&AtLocs[0], &AtStrings[0], + AtStrings.size()); +} + +/// objc-encode-expression: +/// @encode ( type-name ) +Parser::ExprResult Parser::ParseObjCEncodeExpression(SourceLocation AtLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_encode) && "Not an @encode expression!"); + + SourceLocation EncLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "@encode"); + return true; + } + + SourceLocation LParenLoc = ConsumeParen(); + + TypeTy *Ty = ParseTypeName(); + + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + + return Actions.ParseObjCEncodeExpression(AtLoc, EncLoc, LParenLoc, Ty, + RParenLoc); +} + +/// objc-protocol-expression +/// @protocol ( protocol-name ) + +Parser::ExprResult Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) +{ + SourceLocation ProtoLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "@protocol"); + return true; + } + + SourceLocation LParenLoc = ConsumeParen(); + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return true; + } + IdentifierInfo *protocolId = Tok.getIdentifierInfo(); + ConsumeToken(); + + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + + return Actions.ParseObjCProtocolExpression(protocolId, AtLoc, ProtoLoc, + LParenLoc, RParenLoc); +} + +/// objc-selector-expression +/// @selector '(' objc-keyword-selector ')' +Parser::ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) +{ + SourceLocation SelectorLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "@selector"); + return 0; + } + + llvm::SmallVector<IdentifierInfo *, 12> KeyIdents; + SourceLocation LParenLoc = ConsumeParen(); + SourceLocation sLoc; + IdentifierInfo *SelIdent = ParseObjCSelector(sLoc); + if (!SelIdent && Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_ident); // missing selector name. + return 0; + } + KeyIdents.push_back(SelIdent); + unsigned nColons = 0; + if (Tok.isNot(tok::r_paren)) { + while (1) { + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon); + break; + } + nColons++; + ConsumeToken(); // Eat the ':'. + if (Tok.is(tok::r_paren)) + break; + // Check for another keyword selector. + SourceLocation Loc; + SelIdent = ParseObjCSelector(Loc); + KeyIdents.push_back(SelIdent); + if (!SelIdent && Tok.isNot(tok::colon)) + break; + } + } + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + Selector Sel = PP.getSelectorTable().getSelector(nColons, &KeyIdents[0]); + return Actions.ParseObjCSelectorExpression(Sel, AtLoc, SelectorLoc, LParenLoc, + RParenLoc); + } diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp new file mode 100644 index 00000000000..f9800219ed5 --- /dev/null +++ b/clang/lib/Parse/ParseStmt.cpp @@ -0,0 +1,1159 @@ +//===--- ParseStmt.cpp - Statement and Block Parser -----------------------===// +// +// 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 Statement and Block portions of the Parser +// interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Parse/DeclSpec.h" +#include "clang/Parse/Scope.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// C99 6.8: Statements and Blocks. +//===----------------------------------------------------------------------===// + +/// ParseStatementOrDeclaration - Read 'statement' or 'declaration'. +/// StatementOrDeclaration: +/// statement +/// declaration +/// +/// statement: +/// labeled-statement +/// compound-statement +/// expression-statement +/// selection-statement +/// iteration-statement +/// jump-statement +/// [OBC] objc-throw-statement +/// [OBC] objc-try-catch-statement +/// [OBC] objc-synchronized-statement +/// [GNU] asm-statement +/// [OMP] openmp-construct [TODO] +/// +/// labeled-statement: +/// identifier ':' statement +/// 'case' constant-expression ':' statement +/// 'default' ':' statement +/// +/// selection-statement: +/// if-statement +/// switch-statement +/// +/// iteration-statement: +/// while-statement +/// do-statement +/// for-statement +/// +/// expression-statement: +/// expression[opt] ';' +/// +/// jump-statement: +/// 'goto' identifier ';' +/// 'continue' ';' +/// 'break' ';' +/// 'return' expression[opt] ';' +/// [GNU] 'goto' '*' expression ';' +/// +/// [OBC] objc-throw-statement: +/// [OBC] '@' 'throw' expression ';' +/// [OBC] '@' 'throw' ';' +/// +Parser::StmtResult Parser::ParseStatementOrDeclaration(bool OnlyStatement) { + const char *SemiError = 0; + Parser::StmtResult Res; + + // Cases in this switch statement should fall through if the parser expects + // the token to end in a semicolon (in which case SemiError should be set), + // or they directly 'return;' if not. + tok::TokenKind Kind = Tok.getKind(); + SourceLocation AtLoc; + switch (Kind) { + case tok::identifier: // C99 6.8.1: labeled-statement + // identifier ':' statement + // declaration (if !OnlyStatement) + // expression[opt] ';' + return ParseIdentifierStatement(OnlyStatement); + + case tok::at: // May be a @try or @throw statement + { + AtLoc = ConsumeToken(); // consume @ + return ParseObjCAtStatement(AtLoc); + } + + default: + if (!OnlyStatement && isDeclarationSpecifier()) { + SourceLocation DeclStart = Tok.getLocation(); + DeclTy *Res = ParseDeclaration(Declarator::BlockContext); + // FIXME: Pass in the right location for the end of the declstmt. + return Actions.ActOnDeclStmt(Res, DeclStart, DeclStart); + } else if (Tok.is(tok::r_brace)) { + Diag(Tok, diag::err_expected_statement); + return true; + } else { + // expression[opt] ';' + ExprResult Res = ParseExpression(); + if (Res.isInvalid) { + // If the expression is invalid, skip ahead to the next semicolon. Not + // doing this opens us up to the possibility of infinite loops if + // ParseExpression does not consume any tokens. + SkipUntil(tok::semi); + return true; + } + // Otherwise, eat the semicolon. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_expr); + return Actions.ActOnExprStmt(Res.Val); + } + + case tok::kw_case: // C99 6.8.1: labeled-statement + return ParseCaseStatement(); + case tok::kw_default: // C99 6.8.1: labeled-statement + return ParseDefaultStatement(); + + case tok::l_brace: // C99 6.8.2: compound-statement + return ParseCompoundStatement(); + case tok::semi: // C99 6.8.3p3: expression[opt] ';' + return Actions.ActOnNullStmt(ConsumeToken()); + + case tok::kw_if: // C99 6.8.4.1: if-statement + return ParseIfStatement(); + case tok::kw_switch: // C99 6.8.4.2: switch-statement + return ParseSwitchStatement(); + + case tok::kw_while: // C99 6.8.5.1: while-statement + return ParseWhileStatement(); + case tok::kw_do: // C99 6.8.5.2: do-statement + Res = ParseDoStatement(); + SemiError = "do/while loop"; + break; + case tok::kw_for: // C99 6.8.5.3: for-statement + return ParseForStatement(); + + case tok::kw_goto: // C99 6.8.6.1: goto-statement + Res = ParseGotoStatement(); + SemiError = "goto statement"; + break; + case tok::kw_continue: // C99 6.8.6.2: continue-statement + Res = ParseContinueStatement(); + SemiError = "continue statement"; + break; + case tok::kw_break: // C99 6.8.6.3: break-statement + Res = ParseBreakStatement(); + SemiError = "break statement"; + break; + case tok::kw_return: // C99 6.8.6.4: return-statement + Res = ParseReturnStatement(); + SemiError = "return statement"; + break; + + case tok::kw_asm: + bool msAsm = false; + Res = ParseAsmStatement(msAsm); + if (msAsm) return Res; + SemiError = "asm statement"; + break; + } + + // If we reached this code, the statement must end in a semicolon. + if (Tok.is(tok::semi)) { + ConsumeToken(); + } else { + Diag(Tok, diag::err_expected_semi_after, SemiError); + SkipUntil(tok::semi); + } + return Res; +} + +/// ParseIdentifierStatement - Because we don't have two-token lookahead, we +/// have a bit of a quandry here. Reading the identifier is necessary to see if +/// there is a ':' after it. If there is, this is a label, regardless of what +/// else the identifier can mean. If not, this is either part of a declaration +/// (if the identifier is a type-name) or part of an expression. +/// +/// labeled-statement: +/// identifier ':' statement +/// [GNU] identifier ':' attributes[opt] statement +/// declaration (if !OnlyStatement) +/// expression[opt] ';' +/// +Parser::StmtResult Parser::ParseIdentifierStatement(bool OnlyStatement) { + assert(Tok.is(tok::identifier) && Tok.getIdentifierInfo() && + "Not an identifier!"); + + Token IdentTok = Tok; // Save the whole token. + ConsumeToken(); // eat the identifier. + + // identifier ':' statement + if (Tok.is(tok::colon)) { + SourceLocation ColonLoc = ConsumeToken(); + + // Read label attributes, if present. + DeclTy *AttrList = 0; + if (Tok.is(tok::kw___attribute)) + // TODO: save these somewhere. + AttrList = ParseAttributes(); + + StmtResult SubStmt = ParseStatement(); + + // Broken substmt shouldn't prevent the label from being added to the AST. + if (SubStmt.isInvalid) + SubStmt = Actions.ActOnNullStmt(ColonLoc); + + return Actions.ActOnLabelStmt(IdentTok.getLocation(), + IdentTok.getIdentifierInfo(), + ColonLoc, SubStmt.Val); + } + + // Check to see if this is a declaration. + void *TypeRep; + if (!OnlyStatement && + (TypeRep = Actions.isTypeName(*IdentTok.getIdentifierInfo(), CurScope))) { + // Handle this. Warn/disable if in middle of block and !C99. + DeclSpec DS; + + // Add the typedef name to the start of the decl-specs. + const char *PrevSpec = 0; + int isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typedef, + IdentTok.getLocation(), PrevSpec, + TypeRep); + assert(!isInvalid && "First declspec can't be invalid!"); + SourceLocation endProtoLoc; + if (Tok.is(tok::less)) { + llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs; + ParseObjCProtocolReferences(ProtocolRefs, endProtoLoc); + llvm::SmallVector<DeclTy *, 8> *ProtocolDecl = + new llvm::SmallVector<DeclTy *, 8>; + DS.setProtocolQualifiers(ProtocolDecl); + Actions.FindProtocolDeclaration(IdentTok.getLocation(), + &ProtocolRefs[0], ProtocolRefs.size(), + *ProtocolDecl); + } + + // ParseDeclarationSpecifiers will continue from there. + ParseDeclarationSpecifiers(DS); + + // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" + // declaration-specifiers init-declarator-list[opt] ';' + if (Tok.is(tok::semi)) { + // TODO: emit error on 'int;' or 'const enum foo;'. + // if (!DS.isMissingDeclaratorOk()) Diag(...); + + ConsumeToken(); + // FIXME: Return this as a type decl. + return 0; + } + + // Parse all the declarators. + Declarator DeclaratorInfo(DS, Declarator::BlockContext); + ParseDeclarator(DeclaratorInfo); + + DeclTy *Decl = ParseInitDeclaratorListAfterFirstDeclarator(DeclaratorInfo); + if (!Decl) return 0; + return Actions.ActOnDeclStmt(Decl, DS.getSourceRange().getBegin(), + DeclaratorInfo.getSourceRange().getEnd()); + } + + // Otherwise, this is an expression. Seed it with II and parse it. + ExprResult Res = ParseExpressionWithLeadingIdentifier(IdentTok); + if (Res.isInvalid) { + SkipUntil(tok::semi); + return true; + } else if (Tok.isNot(tok::semi)) { + Diag(Tok, diag::err_expected_semi_after, "expression"); + SkipUntil(tok::semi); + return true; + } else { + ConsumeToken(); + // Convert expr to a stmt. + return Actions.ActOnExprStmt(Res.Val); + } +} + +/// ParseCaseStatement +/// labeled-statement: +/// 'case' constant-expression ':' statement +/// [GNU] 'case' constant-expression '...' constant-expression ':' statement +/// +/// Note that this does not parse the 'statement' at the end. +/// +Parser::StmtResult Parser::ParseCaseStatement() { + assert(Tok.is(tok::kw_case) && "Not a case stmt!"); + SourceLocation CaseLoc = ConsumeToken(); // eat the 'case'. + + ExprResult LHS = ParseConstantExpression(); + if (LHS.isInvalid) { + SkipUntil(tok::colon); + return true; + } + + // GNU case range extension. + SourceLocation DotDotDotLoc; + ExprTy *RHSVal = 0; + if (Tok.is(tok::ellipsis)) { + Diag(Tok, diag::ext_gnu_case_range); + DotDotDotLoc = ConsumeToken(); + + ExprResult RHS = ParseConstantExpression(); + if (RHS.isInvalid) { + SkipUntil(tok::colon); + return true; + } + RHSVal = RHS.Val; + } + + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon_after, "'case'"); + SkipUntil(tok::colon); + return true; + } + + SourceLocation ColonLoc = ConsumeToken(); + + // Diagnose the common error "switch (X) { case 4: }", which is not valid. + if (Tok.is(tok::r_brace)) { + Diag(Tok, diag::err_label_end_of_compound_statement); + return true; + } + + StmtResult SubStmt = ParseStatement(); + + // Broken substmt shouldn't prevent the case from being added to the AST. + if (SubStmt.isInvalid) + SubStmt = Actions.ActOnNullStmt(ColonLoc); + + return Actions.ActOnCaseStmt(CaseLoc, LHS.Val, DotDotDotLoc, RHSVal, ColonLoc, + SubStmt.Val); +} + +/// ParseDefaultStatement +/// labeled-statement: +/// 'default' ':' statement +/// Note that this does not parse the 'statement' at the end. +/// +Parser::StmtResult Parser::ParseDefaultStatement() { + assert(Tok.is(tok::kw_default) && "Not a default stmt!"); + SourceLocation DefaultLoc = ConsumeToken(); // eat the 'default'. + + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon_after, "'default'"); + SkipUntil(tok::colon); + return true; + } + + SourceLocation ColonLoc = ConsumeToken(); + + // Diagnose the common error "switch (X) {... default: }", which is not valid. + if (Tok.is(tok::r_brace)) { + Diag(Tok, diag::err_label_end_of_compound_statement); + return true; + } + + StmtResult SubStmt = ParseStatement(); + if (SubStmt.isInvalid) + return true; + + return Actions.ActOnDefaultStmt(DefaultLoc, ColonLoc, SubStmt.Val, CurScope); +} + + +/// ParseCompoundStatement - Parse a "{}" block. +/// +/// compound-statement: [C99 6.8.2] +/// { block-item-list[opt] } +/// [GNU] { label-declarations block-item-list } [TODO] +/// +/// block-item-list: +/// block-item +/// block-item-list block-item +/// +/// block-item: +/// declaration +/// [GNU] '__extension__' declaration +/// statement +/// [OMP] openmp-directive [TODO] +/// +/// [GNU] label-declarations: +/// [GNU] label-declaration +/// [GNU] label-declarations label-declaration +/// +/// [GNU] label-declaration: +/// [GNU] '__label__' identifier-list ';' +/// +/// [OMP] openmp-directive: [TODO] +/// [OMP] barrier-directive +/// [OMP] flush-directive +/// +Parser::StmtResult Parser::ParseCompoundStatement(bool isStmtExpr) { + assert(Tok.is(tok::l_brace) && "Not a compount stmt!"); + + // Enter a scope to hold everything within the compound stmt. Compound + // statements can always hold declarations. + EnterScope(Scope::DeclScope); + + // Parse the statements in the body. + StmtResult Body = ParseCompoundStatementBody(isStmtExpr); + + ExitScope(); + return Body; +} + + +/// ParseCompoundStatementBody - Parse a sequence of statements and invoke the +/// ActOnCompoundStmt action. This expects the '{' to be the current token, and +/// consume the '}' at the end of the block. It does not manipulate the scope +/// stack. +Parser::StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { + SourceLocation LBraceLoc = ConsumeBrace(); // eat the '{'. + + // TODO: "__label__ X, Y, Z;" is the GNU "Local Label" extension. These are + // only allowed at the start of a compound stmt regardless of the language. + + llvm::SmallVector<StmtTy*, 32> Stmts; + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + StmtResult R; + if (Tok.isNot(tok::kw___extension__)) { + R = ParseStatementOrDeclaration(false); + } else { + // __extension__ can start declarations and it can also be a unary + // operator for expressions. Consume multiple __extension__ markers here + // until we can determine which is which. + SourceLocation ExtLoc = ConsumeToken(); + while (Tok.is(tok::kw___extension__)) + ConsumeToken(); + + // __extension__ silences extension warnings in the subexpression. + bool SavedExtWarn = Diags.getWarnOnExtensions(); + Diags.setWarnOnExtensions(false); + + // If this is the start of a declaration, parse it as such. + if (isDeclarationSpecifier()) { + // FIXME: Save the __extension__ on the decl as a node somehow. + SourceLocation DeclStart = Tok.getLocation(); + DeclTy *Res = ParseDeclaration(Declarator::BlockContext); + // FIXME: Pass in the right location for the end of the declstmt. + R = Actions.ActOnDeclStmt(Res, DeclStart, DeclStart); + + Diags.setWarnOnExtensions(SavedExtWarn); + } else { + // Otherwise this was a unary __extension__ marker. Parse the + // subexpression and add the __extension__ unary op. + ExprResult Res = ParseCastExpression(false); + Diags.setWarnOnExtensions(SavedExtWarn); + + if (Res.isInvalid) { + SkipUntil(tok::semi); + continue; + } + + // Add the __extension__ node to the AST. + Res = Actions.ActOnUnaryOp(ExtLoc, tok::kw___extension__, Res.Val); + if (Res.isInvalid) + continue; + + // Eat the semicolon at the end of stmt and convert the expr into a stmt. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_expr); + R = Actions.ActOnExprStmt(Res.Val); + } + } + + if (!R.isInvalid && R.Val) + Stmts.push_back(R.Val); + } + + // We broke out of the while loop because we found a '}' or EOF. + if (Tok.isNot(tok::r_brace)) { + Diag(Tok, diag::err_expected_rbrace); + return true; + } + + SourceLocation RBraceLoc = ConsumeBrace(); + return Actions.ActOnCompoundStmt(LBraceLoc, RBraceLoc, + &Stmts[0], Stmts.size(), isStmtExpr); +} + +/// ParseIfStatement +/// if-statement: [C99 6.8.4.1] +/// 'if' '(' expression ')' statement +/// 'if' '(' expression ')' statement 'else' statement +/// +Parser::StmtResult Parser::ParseIfStatement() { + assert(Tok.is(tok::kw_if) && "Not an if stmt!"); + SourceLocation IfLoc = ConsumeToken(); // eat the 'if'. + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "if"); + SkipUntil(tok::semi); + return true; + } + + // C99 6.8.4p3 - In C99, the if statement is a block. This is not + // the case for C90. + if (getLang().C99) + EnterScope(Scope::DeclScope); + + // Parse the condition. + ExprResult CondExp = ParseSimpleParenExpression(); + if (CondExp.isInvalid) { + SkipUntil(tok::semi); + if (getLang().C99) + ExitScope(); + return true; + } + + // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if + // there is no compound stmt. C90 does not have this clause. We only do this + // if the body isn't a compound statement to avoid push/pop in common cases. + bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + if (NeedsInnerScope) EnterScope(Scope::DeclScope); + + // Read the 'then' stmt. + SourceLocation ThenStmtLoc = Tok.getLocation(); + StmtResult ThenStmt = ParseStatement(); + + // Pop the 'if' scope if needed. + if (NeedsInnerScope) ExitScope(); + + // If it has an else, parse it. + SourceLocation ElseLoc; + SourceLocation ElseStmtLoc; + StmtResult ElseStmt(false); + + if (Tok.is(tok::kw_else)) { + ElseLoc = ConsumeToken(); + + // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if + // there is no compound stmt. C90 does not have this clause. We only do + // this if the body isn't a compound statement to avoid push/pop in common + // cases. + NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + if (NeedsInnerScope) EnterScope(Scope::DeclScope); + + ElseStmtLoc = Tok.getLocation(); + ElseStmt = ParseStatement(); + + // Pop the 'else' scope if needed. + if (NeedsInnerScope) ExitScope(); + } + + if (getLang().C99) + ExitScope(); + + // If the then or else stmt is invalid and the other is valid (and present), + // make turn the invalid one into a null stmt to avoid dropping the other + // part. If both are invalid, return error. + if ((ThenStmt.isInvalid && ElseStmt.isInvalid) || + (ThenStmt.isInvalid && ElseStmt.Val == 0) || + (ThenStmt.Val == 0 && ElseStmt.isInvalid)) { + // Both invalid, or one is invalid and other is non-present: delete cond and + // return error. + Actions.DeleteExpr(CondExp.Val); + return true; + } + + // Now if either are invalid, replace with a ';'. + if (ThenStmt.isInvalid) + ThenStmt = Actions.ActOnNullStmt(ThenStmtLoc); + if (ElseStmt.isInvalid) + ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc); + + return Actions.ActOnIfStmt(IfLoc, CondExp.Val, ThenStmt.Val, + ElseLoc, ElseStmt.Val); +} + +/// ParseSwitchStatement +/// switch-statement: +/// 'switch' '(' expression ')' statement +Parser::StmtResult Parser::ParseSwitchStatement() { + assert(Tok.is(tok::kw_switch) && "Not a switch stmt!"); + SourceLocation SwitchLoc = ConsumeToken(); // eat the 'switch'. + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "switch"); + SkipUntil(tok::semi); + return true; + } + + // C99 6.8.4p3 - In C99, the switch statement is a block. This is + // not the case for C90. Start the switch scope. + if (getLang().C99) + EnterScope(Scope::BreakScope|Scope::DeclScope); + else + EnterScope(Scope::BreakScope); + + // Parse the condition. + ExprResult Cond = ParseSimpleParenExpression(); + + if (Cond.isInvalid) { + ExitScope(); + return true; + } + + StmtResult Switch = Actions.ActOnStartOfSwitchStmt(Cond.Val); + + // C99 6.8.4p3 - In C99, the body of the switch statement is a scope, even if + // there is no compound stmt. C90 does not have this clause. We only do this + // if the body isn't a compound statement to avoid push/pop in common cases. + bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + if (NeedsInnerScope) EnterScope(Scope::DeclScope); + + // Read the body statement. + StmtResult Body = ParseStatement(); + + // Pop the body scope if needed. + if (NeedsInnerScope) ExitScope(); + + if (Body.isInvalid) { + Body = Actions.ActOnNullStmt(Tok.getLocation()); + // FIXME: Remove the case statement list from the Switch statement. + } + + ExitScope(); + + return Actions.ActOnFinishSwitchStmt(SwitchLoc, Switch.Val, Body.Val); +} + +/// ParseWhileStatement +/// while-statement: [C99 6.8.5.1] +/// 'while' '(' expression ')' statement +Parser::StmtResult Parser::ParseWhileStatement() { + assert(Tok.is(tok::kw_while) && "Not a while stmt!"); + SourceLocation WhileLoc = Tok.getLocation(); + ConsumeToken(); // eat the 'while'. + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "while"); + SkipUntil(tok::semi); + return true; + } + + // C99 6.8.5p5 - In C99, the while statement is a block. This is not + // the case for C90. Start the loop scope. + if (getLang().C99) + EnterScope(Scope::BreakScope | Scope::ContinueScope | Scope::DeclScope); + else + EnterScope(Scope::BreakScope | Scope::ContinueScope); + + // Parse the condition. + ExprResult Cond = ParseSimpleParenExpression(); + + // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if + // there is no compound stmt. C90 does not have this clause. We only do this + // if the body isn't a compound statement to avoid push/pop in common cases. + bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + if (NeedsInnerScope) EnterScope(Scope::DeclScope); + + // Read the body statement. + StmtResult Body = ParseStatement(); + + // Pop the body scope if needed. + if (NeedsInnerScope) ExitScope(); + + ExitScope(); + + if (Cond.isInvalid || Body.isInvalid) return true; + + return Actions.ActOnWhileStmt(WhileLoc, Cond.Val, Body.Val); +} + +/// ParseDoStatement +/// do-statement: [C99 6.8.5.2] +/// 'do' statement 'while' '(' expression ')' ';' +/// Note: this lets the caller parse the end ';'. +Parser::StmtResult Parser::ParseDoStatement() { + assert(Tok.is(tok::kw_do) && "Not a do stmt!"); + SourceLocation DoLoc = ConsumeToken(); // eat the 'do'. + + // C99 6.8.5p5 - In C99, the do statement is a block. This is not + // the case for C90. Start the loop scope. + if (getLang().C99) + EnterScope(Scope::BreakScope | Scope::ContinueScope | Scope::DeclScope); + else + EnterScope(Scope::BreakScope | Scope::ContinueScope); + + // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if + // there is no compound stmt. C90 does not have this clause. We only do this + // if the body isn't a compound statement to avoid push/pop in common cases. + bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + if (NeedsInnerScope) EnterScope(Scope::DeclScope); + + // Read the body statement. + StmtResult Body = ParseStatement(); + + // Pop the body scope if needed. + if (NeedsInnerScope) ExitScope(); + + if (Tok.isNot(tok::kw_while)) { + ExitScope(); + Diag(Tok, diag::err_expected_while); + Diag(DoLoc, diag::err_matching, "do"); + SkipUntil(tok::semi); + return true; + } + SourceLocation WhileLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + ExitScope(); + Diag(Tok, diag::err_expected_lparen_after, "do/while"); + SkipUntil(tok::semi); + return true; + } + + // Parse the condition. + ExprResult Cond = ParseSimpleParenExpression(); + + ExitScope(); + + if (Cond.isInvalid || Body.isInvalid) return true; + + return Actions.ActOnDoStmt(DoLoc, Body.Val, WhileLoc, Cond.Val); +} + +/// ParseForStatement +/// for-statement: [C99 6.8.5.3] +/// 'for' '(' expr[opt] ';' expr[opt] ';' expr[opt] ')' statement +/// 'for' '(' declaration expr[opt] ';' expr[opt] ')' statement +/// [OBJC2] 'for' '(' declaration 'in' expr ')' statement +/// [OBJC2] 'for' '(' expr 'in' expr ')' statement +Parser::StmtResult Parser::ParseForStatement() { + assert(Tok.is(tok::kw_for) && "Not a for stmt!"); + SourceLocation ForLoc = ConsumeToken(); // eat the 'for'. + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "for"); + SkipUntil(tok::semi); + return true; + } + + // C99 6.8.5p5 - In C99, the for statement is a block. This is not + // the case for C90. Start the loop scope. + if (getLang().C99) + EnterScope(Scope::BreakScope | Scope::ContinueScope | Scope::DeclScope); + else + EnterScope(Scope::BreakScope | Scope::ContinueScope); + + SourceLocation LParenLoc = ConsumeParen(); + ExprResult Value; + + StmtTy *FirstPart = 0; + ExprTy *SecondPart = 0; + StmtTy *ThirdPart = 0; + bool ForEach = false; + + // Parse the first part of the for specifier. + if (Tok.is(tok::semi)) { // for (; + // no first part, eat the ';'. + ConsumeToken(); + } else if (isDeclarationSpecifier()) { // for (int X = 4; + // Parse declaration, which eats the ';'. + if (!getLang().C99) // Use of C99-style for loops in C90 mode? + Diag(Tok, diag::ext_c99_variable_decl_in_for_loop); + + SourceLocation DeclStart = Tok.getLocation(); + DeclTy *aBlockVarDecl = ParseDeclaration(Declarator::ForContext); + // FIXME: Pass in the right location for the end of the declstmt. + StmtResult stmtResult = Actions.ActOnDeclStmt(aBlockVarDecl, DeclStart, + DeclStart); + FirstPart = stmtResult.isInvalid ? 0 : stmtResult.Val; + if ((ForEach = isTokIdentifier_in())) { + ConsumeToken(); // consume 'in' + Value = ParseExpression(); + if (!Value.isInvalid) + SecondPart = Value.Val; + } + } else { + Value = ParseExpression(); + + // Turn the expression into a stmt. + if (!Value.isInvalid) { + StmtResult R = Actions.ActOnExprStmt(Value.Val); + if (!R.isInvalid) + FirstPart = R.Val; + } + + if (Tok.is(tok::semi)) { + ConsumeToken(); + } + else if ((ForEach = isTokIdentifier_in())) { + ConsumeToken(); // consume 'in' + Value = ParseExpression(); + if (!Value.isInvalid) + SecondPart = Value.Val; + } + else { + if (!Value.isInvalid) Diag(Tok, diag::err_expected_semi_for); + SkipUntil(tok::semi); + } + } + if (!ForEach) { + // Parse the second part of the for specifier. + if (Tok.is(tok::semi)) { // for (...;; + // no second part. + Value = ExprResult(); + } else { + Value = ParseExpression(); + if (!Value.isInvalid) + SecondPart = Value.Val; + } + + if (Tok.is(tok::semi)) { + ConsumeToken(); + } else { + if (!Value.isInvalid) Diag(Tok, diag::err_expected_semi_for); + SkipUntil(tok::semi); + } + + // Parse the third part of the for specifier. + if (Tok.is(tok::r_paren)) { // for (...;...;) + // no third part. + Value = ExprResult(); + } else { + Value = ParseExpression(); + if (!Value.isInvalid) { + // Turn the expression into a stmt. + StmtResult R = Actions.ActOnExprStmt(Value.Val); + if (!R.isInvalid) + ThirdPart = R.Val; + } + } + } + // Match the ')'. + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + + // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if + // there is no compound stmt. C90 does not have this clause. We only do this + // if the body isn't a compound statement to avoid push/pop in common cases. + bool NeedsInnerScope = getLang().C99 && Tok.isNot(tok::l_brace); + if (NeedsInnerScope) EnterScope(Scope::DeclScope); + + // Read the body statement. + StmtResult Body = ParseStatement(); + + // Pop the body scope if needed. + if (NeedsInnerScope) ExitScope(); + + // Leave the for-scope. + ExitScope(); + + if (Body.isInvalid) + return Body; + + if (!ForEach) + return Actions.ActOnForStmt(ForLoc, LParenLoc, FirstPart, + SecondPart, ThirdPart, RParenLoc, Body.Val); + else + return Actions.ActOnObjCForCollectionStmt(ForLoc, LParenLoc, FirstPart, + SecondPart, RParenLoc, Body.Val); +} + +/// ParseGotoStatement +/// jump-statement: +/// 'goto' identifier ';' +/// [GNU] 'goto' '*' expression ';' +/// +/// Note: this lets the caller parse the end ';'. +/// +Parser::StmtResult Parser::ParseGotoStatement() { + assert(Tok.is(tok::kw_goto) && "Not a goto stmt!"); + SourceLocation GotoLoc = ConsumeToken(); // eat the 'goto'. + + StmtResult Res; + if (Tok.is(tok::identifier)) { + Res = Actions.ActOnGotoStmt(GotoLoc, Tok.getLocation(), + Tok.getIdentifierInfo()); + ConsumeToken(); + } else if (Tok.is(tok::star) && !getLang().NoExtensions) { + // GNU indirect goto extension. + Diag(Tok, diag::ext_gnu_indirect_goto); + SourceLocation StarLoc = ConsumeToken(); + ExprResult R = ParseExpression(); + if (R.isInvalid) { // Skip to the semicolon, but don't consume it. + SkipUntil(tok::semi, false, true); + return true; + } + Res = Actions.ActOnIndirectGotoStmt(GotoLoc, StarLoc, R.Val); + } else { + Diag(Tok, diag::err_expected_ident); + return true; + } + + return Res; +} + +/// ParseContinueStatement +/// jump-statement: +/// 'continue' ';' +/// +/// Note: this lets the caller parse the end ';'. +/// +Parser::StmtResult Parser::ParseContinueStatement() { + SourceLocation ContinueLoc = ConsumeToken(); // eat the 'continue'. + return Actions.ActOnContinueStmt(ContinueLoc, CurScope); +} + +/// ParseBreakStatement +/// jump-statement: +/// 'break' ';' +/// +/// Note: this lets the caller parse the end ';'. +/// +Parser::StmtResult Parser::ParseBreakStatement() { + SourceLocation BreakLoc = ConsumeToken(); // eat the 'break'. + return Actions.ActOnBreakStmt(BreakLoc, CurScope); +} + +/// ParseReturnStatement +/// jump-statement: +/// 'return' expression[opt] ';' +Parser::StmtResult Parser::ParseReturnStatement() { + assert(Tok.is(tok::kw_return) && "Not a return stmt!"); + SourceLocation ReturnLoc = ConsumeToken(); // eat the 'return'. + + ExprResult R(0); + if (Tok.isNot(tok::semi)) { + R = ParseExpression(); + if (R.isInvalid) { // Skip to the semicolon, but don't consume it. + SkipUntil(tok::semi, false, true); + return true; + } + } + return Actions.ActOnReturnStmt(ReturnLoc, R.Val); +} + +/// FuzzyParseMicrosoftAsmStatement. When -fms-extensions is enabled, this +/// routine is called to skip/ignore tokens that comprise the MS asm statement. +Parser::StmtResult Parser::FuzzyParseMicrosoftAsmStatement() { + if (Tok.is(tok::l_brace)) { + unsigned short savedBraceCount = BraceCount; + do { + ConsumeAnyToken(); + } while (BraceCount > savedBraceCount && Tok.isNot(tok::eof)); + } else { + // From the MS website: If used without braces, the __asm keyword means + // that the rest of the line is an assembly-language statement. + SourceManager &SrcMgr = PP.getSourceManager(); + SourceLocation TokLoc = Tok.getLocation(); + unsigned lineNo = SrcMgr.getLogicalLineNumber(TokLoc); + do { + ConsumeAnyToken(); + TokLoc = Tok.getLocation(); + } while ((SrcMgr.getLogicalLineNumber(TokLoc) == lineNo) && + Tok.isNot(tok::r_brace) && Tok.isNot(tok::semi) && + Tok.isNot(tok::eof)); + } + return false; +} + +/// ParseAsmStatement - Parse a GNU extended asm statement. +/// asm-statement: +/// gnu-asm-statement +/// ms-asm-statement +/// +/// [GNU] gnu-asm-statement: +/// 'asm' type-qualifier[opt] '(' asm-argument ')' ';' +/// +/// [GNU] asm-argument: +/// asm-string-literal +/// asm-string-literal ':' asm-operands[opt] +/// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt] +/// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt] +/// ':' asm-clobbers +/// +/// [GNU] asm-clobbers: +/// asm-string-literal +/// asm-clobbers ',' asm-string-literal +/// +/// [MS] ms-asm-statement: +/// '__asm' assembly-instruction ';'[opt] +/// '__asm' '{' assembly-instruction-list '}' ';'[opt] +/// +/// [MS] assembly-instruction-list: +/// assembly-instruction ';'[opt] +/// assembly-instruction-list ';' assembly-instruction ';'[opt] +/// +Parser::StmtResult Parser::ParseAsmStatement(bool &msAsm) { + assert(Tok.is(tok::kw_asm) && "Not an asm stmt"); + SourceLocation AsmLoc = ConsumeToken(); + + if (getLang().Microsoft && Tok.isNot(tok::l_paren) && !isTypeQualifier()) { + msAsm = true; + return FuzzyParseMicrosoftAsmStatement(); + } + DeclSpec DS; + SourceLocation Loc = Tok.getLocation(); + ParseTypeQualifierListOpt(DS); + + // GNU asms accept, but warn, about type-qualifiers other than volatile. + if (DS.getTypeQualifiers() & DeclSpec::TQ_const) + Diag(Loc, diag::w_asm_qualifier_ignored, "const"); + if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) + Diag(Loc, diag::w_asm_qualifier_ignored, "restrict"); + + // Remember if this was a volatile asm. + bool isVolatile = DS.getTypeQualifiers() & DeclSpec::TQ_volatile; + bool isSimple = false; + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "asm"); + SkipUntil(tok::r_paren); + return true; + } + Loc = ConsumeParen(); + + ExprResult AsmString = ParseAsmStringLiteral(); + if (AsmString.isInvalid) + return true; + + llvm::SmallVector<std::string, 4> Names; + llvm::SmallVector<ExprTy*, 4> Constraints; + llvm::SmallVector<ExprTy*, 4> Exprs; + llvm::SmallVector<ExprTy*, 4> Clobbers; + + unsigned NumInputs = 0, NumOutputs = 0; + + SourceLocation RParenLoc; + if (Tok.is(tok::r_paren)) { + // We have a simple asm expression + isSimple = true; + + RParenLoc = ConsumeParen(); + } else { + // Parse Outputs, if present. + if (ParseAsmOperandsOpt(Names, Constraints, Exprs)) + return true; + + NumOutputs = Names.size(); + + // Parse Inputs, if present. + if (ParseAsmOperandsOpt(Names, Constraints, Exprs)) + return true; + + assert(Names.size() == Constraints.size() && + Constraints.size() == Exprs.size() + && "Input operand size mismatch!"); + + NumInputs = Names.size() - NumOutputs; + + // Parse the clobbers, if present. + if (Tok.is(tok::colon)) { + ConsumeToken(); + + // Parse the asm-string list for clobbers. + while (1) { + ExprResult Clobber = ParseAsmStringLiteral(); + + if (Clobber.isInvalid) + break; + + Clobbers.push_back(Clobber.Val); + + if (Tok.isNot(tok::comma)) break; + ConsumeToken(); + } + } + + RParenLoc = MatchRHSPunctuation(tok::r_paren, Loc); + } + + return Actions.ActOnAsmStmt(AsmLoc, isSimple, isVolatile, + NumOutputs, NumInputs, + &Names[0], &Constraints[0], &Exprs[0], + AsmString.Val, + Clobbers.size(), &Clobbers[0], + RParenLoc); +} + +/// ParseAsmOperands - Parse the asm-operands production as used by +/// asm-statement. We also parse a leading ':' token. If the leading colon is +/// not present, we do not parse anything. +/// +/// [GNU] asm-operands: +/// asm-operand +/// asm-operands ',' asm-operand +/// +/// [GNU] asm-operand: +/// asm-string-literal '(' expression ')' +/// '[' identifier ']' asm-string-literal '(' expression ')' +/// +bool Parser::ParseAsmOperandsOpt(llvm::SmallVectorImpl<std::string> &Names, + llvm::SmallVectorImpl<ExprTy*> &Constraints, + llvm::SmallVectorImpl<ExprTy*> &Exprs) { + // Only do anything if this operand is present. + if (Tok.isNot(tok::colon)) return false; + ConsumeToken(); + + // 'asm-operands' isn't present? + if (!isTokenStringLiteral() && Tok.isNot(tok::l_square)) + return false; + + while (1) { + // Read the [id] if present. + if (Tok.is(tok::l_square)) { + SourceLocation Loc = ConsumeBracket(); + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::r_paren); + return true; + } + + IdentifierInfo *II = Tok.getIdentifierInfo(); + ConsumeToken(); + + Names.push_back(std::string(II->getName(), II->getLength())); + MatchRHSPunctuation(tok::r_square, Loc); + } else + Names.push_back(std::string()); + + ExprResult Constraint = ParseAsmStringLiteral(); + if (Constraint.isInvalid) { + SkipUntil(tok::r_paren); + return true; + } + Constraints.push_back(Constraint.Val); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "asm operand"); + SkipUntil(tok::r_paren); + return true; + } + + // Read the parenthesized expression. + ExprResult Res = ParseSimpleParenExpression(); + if (Res.isInvalid) { + SkipUntil(tok::r_paren); + return true; + } + Exprs.push_back(Res.Val); + // Eat the comma and continue parsing if it exists. + if (Tok.isNot(tok::comma)) return false; + ConsumeToken(); + } + + return true; +} + +Parser::DeclTy *Parser::ParseFunctionStatementBody(DeclTy *Decl, + SourceLocation L, SourceLocation R) { + // Do not enter a scope for the brace, as the arguments are in the same scope + // (the function body) as the body itself. Instead, just read the statement + // list and put it into a CompoundStmt for safe keeping. + StmtResult FnBody = ParseCompoundStatementBody(); + + // If the function body could not be parsed, make a bogus compoundstmt. + if (FnBody.isInvalid) + FnBody = Actions.ActOnCompoundStmt(L, R, 0, 0, false); + + // Leave the function body scope. + ExitScope(); + + return Actions.ActOnFinishFunctionBody(Decl, FnBody.Val); +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp new file mode 100644 index 00000000000..703144b3c44 --- /dev/null +++ b/clang/lib/Parse/Parser.cpp @@ -0,0 +1,647 @@ +//===--- Parser.cpp - C Language Family Parser ----------------------------===// +// +// 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 Parser interfaces. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Parse/DeclSpec.h" +#include "clang/Parse/Scope.h" +using namespace clang; + +Parser::Parser(Preprocessor &pp, Action &actions) + : PP(pp), Actions(actions), Diags(PP.getDiagnostics()) { + Tok.setKind(tok::eof); + CurScope = 0; + NumCachedScopes = 0; + ParenCount = BracketCount = BraceCount = 0; + ObjCImpDecl = 0; +} + +/// Out-of-line virtual destructor to provide home for Action class. +Action::~Action() {} + + +void Parser::Diag(SourceLocation Loc, unsigned DiagID, + const std::string &Msg) { + Diags.Report(FullSourceLoc(Loc,PP.getSourceManager()), DiagID, &Msg, 1); +} + +/// MatchRHSPunctuation - For punctuation with a LHS and RHS (e.g. '['/']'), +/// this helper function matches and consumes the specified RHS token if +/// present. If not present, it emits the specified diagnostic indicating +/// that the parser failed to match the RHS of the token at LHSLoc. LHSName +/// should be the name of the unmatched LHS token. +SourceLocation Parser::MatchRHSPunctuation(tok::TokenKind RHSTok, + SourceLocation LHSLoc) { + + if (Tok.is(RHSTok)) + return ConsumeAnyToken(); + + SourceLocation R = Tok.getLocation(); + const char *LHSName = "unknown"; + diag::kind DID = diag::err_parse_error; + switch (RHSTok) { + default: break; + case tok::r_paren : LHSName = "("; DID = diag::err_expected_rparen; break; + case tok::r_brace : LHSName = "{"; DID = diag::err_expected_rbrace; break; + case tok::r_square: LHSName = "["; DID = diag::err_expected_rsquare; break; + case tok::greater: LHSName = "<"; DID = diag::err_expected_greater; break; + } + Diag(Tok, DID); + Diag(LHSLoc, diag::err_matching, LHSName); + SkipUntil(RHSTok); + return R; +} + +/// ExpectAndConsume - The parser expects that 'ExpectedTok' is next in the +/// input. If so, it is consumed and false is returned. +/// +/// If the input is malformed, this emits the specified diagnostic. Next, if +/// SkipToTok is specified, it calls SkipUntil(SkipToTok). Finally, true is +/// returned. +bool Parser::ExpectAndConsume(tok::TokenKind ExpectedTok, unsigned DiagID, + const char *Msg, tok::TokenKind SkipToTok) { + if (Tok.is(ExpectedTok)) { + ConsumeAnyToken(); + return false; + } + + Diag(Tok, DiagID, Msg); + if (SkipToTok != tok::unknown) + SkipUntil(SkipToTok); + return true; +} + +//===----------------------------------------------------------------------===// +// Error recovery. +//===----------------------------------------------------------------------===// + +/// SkipUntil - Read tokens until we get to the specified token, then consume +/// it (unless DontConsume is true). Because we cannot guarantee that the +/// token will ever occur, this skips to the next token, or to some likely +/// good stopping point. If StopAtSemi is true, skipping will stop at a ';' +/// character. +/// +/// If SkipUntil finds the specified token, it returns true, otherwise it +/// returns false. +bool Parser::SkipUntil(const tok::TokenKind *Toks, unsigned NumToks, + bool StopAtSemi, bool DontConsume) { + // We always want this function to skip at least one token if the first token + // isn't T and if not at EOF. + bool isFirstTokenSkipped = true; + while (1) { + // If we found one of the tokens, stop and return true. + for (unsigned i = 0; i != NumToks; ++i) { + if (Tok.is(Toks[i])) { + if (DontConsume) { + // Noop, don't consume the token. + } else { + ConsumeAnyToken(); + } + return true; + } + } + + switch (Tok.getKind()) { + case tok::eof: + // Ran out of tokens. + return false; + + case tok::l_paren: + // Recursively skip properly-nested parens. + ConsumeParen(); + SkipUntil(tok::r_paren, false); + break; + case tok::l_square: + // Recursively skip properly-nested square brackets. + ConsumeBracket(); + SkipUntil(tok::r_square, false); + break; + case tok::l_brace: + // Recursively skip properly-nested braces. + ConsumeBrace(); + SkipUntil(tok::r_brace, false); + break; + + // Okay, we found a ']' or '}' or ')', which we think should be balanced. + // Since the user wasn't looking for this token (if they were, it would + // already be handled), this isn't balanced. If there is a LHS token at a + // higher level, we will assume that this matches the unbalanced token + // and return it. Otherwise, this is a spurious RHS token, which we skip. + case tok::r_paren: + if (ParenCount && !isFirstTokenSkipped) + return false; // Matches something. + ConsumeParen(); + break; + case tok::r_square: + if (BracketCount && !isFirstTokenSkipped) + return false; // Matches something. + ConsumeBracket(); + break; + case tok::r_brace: + if (BraceCount && !isFirstTokenSkipped) + return false; // Matches something. + ConsumeBrace(); + break; + + case tok::string_literal: + case tok::wide_string_literal: + ConsumeStringToken(); + break; + case tok::semi: + if (StopAtSemi) + return false; + // FALL THROUGH. + default: + // Skip this token. + ConsumeToken(); + break; + } + isFirstTokenSkipped = false; + } +} + +//===----------------------------------------------------------------------===// +// Scope manipulation +//===----------------------------------------------------------------------===// + +/// EnterScope - Start a new scope. +void Parser::EnterScope(unsigned ScopeFlags) { + if (NumCachedScopes) { + Scope *N = ScopeCache[--NumCachedScopes]; + N->Init(CurScope, ScopeFlags); + CurScope = N; + } else { + CurScope = new Scope(CurScope, ScopeFlags); + } +} + +/// ExitScope - Pop a scope off the scope stack. +void Parser::ExitScope() { + assert(CurScope && "Scope imbalance!"); + + // Inform the actions module that this scope is going away if there are any + // decls in it. + if (!CurScope->decl_empty()) + Actions.ActOnPopScope(Tok.getLocation(), CurScope); + + Scope *OldScope = CurScope; + CurScope = OldScope->getParent(); + + if (NumCachedScopes == ScopeCacheSize) + delete OldScope; + else + ScopeCache[NumCachedScopes++] = OldScope; +} + + + + +//===----------------------------------------------------------------------===// +// C99 6.9: External Definitions. +//===----------------------------------------------------------------------===// + +Parser::~Parser() { + // If we still have scopes active, delete the scope tree. + delete CurScope; + + // Free the scope cache. + for (unsigned i = 0, e = NumCachedScopes; i != e; ++i) + delete ScopeCache[i]; +} + +/// Initialize - Warm up the parser. +/// +void Parser::Initialize() { + // Prime the lexer look-ahead. + ConsumeToken(); + + // Create the translation unit scope. Install it as the current scope. + assert(CurScope == 0 && "A scope is already active?"); + EnterScope(Scope::DeclScope); + Actions.ActOnTranslationUnitScope(Tok.getLocation(), CurScope); + + if (Tok.is(tok::eof) && + !getLang().CPlusPlus) // Empty source file is an extension in C + Diag(Tok, diag::ext_empty_source_file); + + // Initialization for Objective-C context sensitive keywords recognition. + // Referenced in Parser::ParseObjCTypeQualifierList. + if (getLang().ObjC1) { + ObjCTypeQuals[objc_in] = &PP.getIdentifierTable().get("in"); + ObjCTypeQuals[objc_out] = &PP.getIdentifierTable().get("out"); + ObjCTypeQuals[objc_inout] = &PP.getIdentifierTable().get("inout"); + ObjCTypeQuals[objc_oneway] = &PP.getIdentifierTable().get("oneway"); + ObjCTypeQuals[objc_bycopy] = &PP.getIdentifierTable().get("bycopy"); + ObjCTypeQuals[objc_byref] = &PP.getIdentifierTable().get("byref"); + } + if (getLang().ObjC2) { + ObjCPropertyAttrs[objc_readonly] = &PP.getIdentifierTable().get("readonly"); + ObjCPropertyAttrs[objc_getter] = &PP.getIdentifierTable().get("getter"); + ObjCPropertyAttrs[objc_setter] = &PP.getIdentifierTable().get("setter"); + ObjCPropertyAttrs[objc_assign] = &PP.getIdentifierTable().get("assign"); + ObjCPropertyAttrs[objc_readwrite] = + &PP.getIdentifierTable().get("readwrite"); + ObjCPropertyAttrs[objc_retain] = &PP.getIdentifierTable().get("retain"); + ObjCPropertyAttrs[objc_copy] = &PP.getIdentifierTable().get("copy"); + ObjCPropertyAttrs[objc_nonatomic] = + &PP.getIdentifierTable().get("nonatomic"); + ObjCForCollectionInKW = &PP.getIdentifierTable().get("in"); + } +} + +/// ParseTopLevelDecl - Parse one top-level declaration, return whatever the +/// action tells us to. This returns true if the EOF was encountered. +bool Parser::ParseTopLevelDecl(DeclTy*& Result) { + Result = 0; + if (Tok.is(tok::eof)) return true; + + Result = ParseExternalDeclaration(); + return false; +} + +/// Finalize - Shut down the parser. +/// +void Parser::Finalize() { + ExitScope(); + assert(CurScope == 0 && "Scope imbalance!"); +} + +/// ParseTranslationUnit: +/// translation-unit: [C99 6.9] +/// external-declaration +/// translation-unit external-declaration +void Parser::ParseTranslationUnit() { + Initialize(); + + DeclTy *Res; + while (!ParseTopLevelDecl(Res)) + /*parse them all*/; + + Finalize(); +} + +/// ParseExternalDeclaration: +/// external-declaration: [C99 6.9] +/// function-definition +/// declaration +/// [EXT] ';' +/// [GNU] asm-definition +/// [GNU] __extension__ external-declaration +/// [OBJC] objc-class-definition +/// [OBJC] objc-class-declaration +/// [OBJC] objc-alias-declaration +/// [OBJC] objc-protocol-definition +/// [OBJC] objc-method-definition +/// [OBJC] @end +/// +/// [GNU] asm-definition: +/// simple-asm-expr ';' +/// +Parser::DeclTy *Parser::ParseExternalDeclaration() { + switch (Tok.getKind()) { + case tok::semi: + Diag(Tok, diag::ext_top_level_semi); + ConsumeToken(); + // TODO: Invoke action for top-level semicolon. + return 0; + case tok::kw___extension__: { + ConsumeToken(); + // FIXME: Disable extension warnings. + DeclTy *RV = ParseExternalDeclaration(); + // FIXME: Restore extension warnings. + return RV; + } + case tok::kw_asm: { + ExprResult Result = ParseSimpleAsm(); + + ExpectAndConsume(tok::semi, diag::err_expected_semi_after, + "top-level asm block"); + + if (!Result.isInvalid) + return Actions.ActOnFileScopeAsmDecl(Tok.getLocation(), Result.Val); + } + case tok::at: + // @ is not a legal token unless objc is enabled, no need to check. + return ParseObjCAtDirectives(); + case tok::minus: + case tok::plus: + if (getLang().ObjC1) + return ParseObjCMethodDefinition(); + else { + Diag(Tok, diag::err_expected_external_declaration); + ConsumeToken(); + } + return 0; + case tok::kw_namespace: + case tok::kw_typedef: + // A function definition cannot start with a these keywords. + return ParseDeclaration(Declarator::FileContext); + default: + // We can't tell whether this is a function-definition or declaration yet. + return ParseDeclarationOrFunctionDefinition(); + } +} + +/// ParseDeclarationOrFunctionDefinition - Parse either a function-definition or +/// a declaration. We can't tell which we have until we read up to the +/// compound-statement in function-definition. +/// +/// function-definition: [C99 6.9.1] +/// declaration-specifiers[opt] declarator declaration-list[opt] +/// compound-statement +/// declaration: [C99 6.7] +/// declaration-specifiers init-declarator-list[opt] ';' +/// [!C99] init-declarator-list ';' [TODO: warn in c99 mode] +/// [OMP] threadprivate-directive [TODO] +/// +Parser::DeclTy *Parser::ParseDeclarationOrFunctionDefinition() { + // Parse the common declaration-specifiers piece. + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + + // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" + // declaration-specifiers init-declarator-list[opt] ';' + if (Tok.is(tok::semi)) { + ConsumeToken(); + return Actions.ParsedFreeStandingDeclSpec(CurScope, DS); + } + + // ObjC2 allows prefix attributes on class interfaces. + if (getLang().ObjC2 && Tok.is(tok::at)) { + SourceLocation AtLoc = ConsumeToken(); // the "@" + if (!Tok.isObjCAtKeyword(tok::objc_interface)) { + Diag(Tok, diag::err_objc_expected_property_attr);//FIXME:better diagnostic + SkipUntil(tok::semi); // FIXME: better skip? + return 0; + } + const char *PrevSpec = 0; + if (DS.SetTypeSpecType(DeclSpec::TST_unspecified, AtLoc, PrevSpec)) + Diag(AtLoc, diag::err_invalid_decl_spec_combination, PrevSpec); + return ParseObjCAtInterfaceDeclaration(AtLoc, DS.getAttributes()); + } + + // If the declspec consisted only of 'extern' and we have a string + // literal following it, this must be a C++ linkage specifier like + // 'extern "C"'. + if (Tok.is(tok::string_literal) && getLang().CPlusPlus && + DS.getStorageClassSpec() == DeclSpec::SCS_extern && + DS.getParsedSpecifiers() == DeclSpec::PQ_StorageClassSpecifier) + return ParseLinkage(Declarator::FileContext); + + // Parse the first declarator. + Declarator DeclaratorInfo(DS, Declarator::FileContext); + ParseDeclarator(DeclaratorInfo); + // Error parsing the declarator? + if (DeclaratorInfo.getIdentifier() == 0) { + // If so, skip until the semi-colon or a }. + SkipUntil(tok::r_brace, true); + if (Tok.is(tok::semi)) + ConsumeToken(); + return 0; + } + + // If the declarator is the start of a function definition, handle it. + if (Tok.is(tok::equal) || // int X()= -> not a function def + Tok.is(tok::comma) || // int X(), -> not a function def + Tok.is(tok::semi) || // int X(); -> not a function def + Tok.is(tok::kw_asm) || // int X() __asm__ -> not a function def + Tok.is(tok::kw___attribute)) { // int X() __attr__ -> not a function def + // FALL THROUGH. + } else if (DeclaratorInfo.isFunctionDeclarator() && + (Tok.is(tok::l_brace) || // int X() {} + isDeclarationSpecifier())) { // int X(f) int f; {} + if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { + Diag(Tok, diag::err_function_declared_typedef); + + if (Tok.is(tok::l_brace)) { + // This recovery skips the entire function body. It would be nice + // to simply call ParseFunctionDefintion() below, however Sema + // assumes the declarator represents a function, not a typedef. + ConsumeBrace(); + SkipUntil(tok::r_brace, true); + } else { + SkipUntil(tok::semi); + } + return 0; + } + return ParseFunctionDefinition(DeclaratorInfo); + } else { + if (DeclaratorInfo.isFunctionDeclarator()) + Diag(Tok, diag::err_expected_fn_body); + else + Diag(Tok, diag::err_expected_after_declarator); + SkipUntil(tok::semi); + return 0; + } + + // Parse the init-declarator-list for a normal declaration. + return ParseInitDeclaratorListAfterFirstDeclarator(DeclaratorInfo); +} + +/// ParseFunctionDefinition - We parsed and verified that the specified +/// Declarator is well formed. If this is a K&R-style function, read the +/// parameters declaration-list, then start the compound-statement. +/// +/// declaration-specifiers[opt] declarator declaration-list[opt] +/// compound-statement [TODO] +/// +Parser::DeclTy *Parser::ParseFunctionDefinition(Declarator &D) { + const DeclaratorChunk &FnTypeInfo = D.getTypeObject(0); + assert(FnTypeInfo.Kind == DeclaratorChunk::Function && + "This isn't a function declarator!"); + const DeclaratorChunk::FunctionTypeInfo &FTI = FnTypeInfo.Fun; + + // If this declaration was formed with a K&R-style identifier list for the + // arguments, parse declarations for all of the args next. + // int foo(a,b) int a; float b; {} + if (!FTI.hasPrototype && FTI.NumArgs != 0) + ParseKNRParamDeclarations(D); + + // We should have an opening brace now. + if (Tok.isNot(tok::l_brace)) { + Diag(Tok, diag::err_expected_fn_body); + + // Skip over garbage, until we get to '{'. Don't eat the '{'. + SkipUntil(tok::l_brace, true, true); + + // If we didn't find the '{', bail out. + if (Tok.isNot(tok::l_brace)) + return 0; + } + + SourceLocation BraceLoc = Tok.getLocation(); + + // Enter a scope for the function body. + EnterScope(Scope::FnScope|Scope::DeclScope); + + // Tell the actions module that we have entered a function definition with the + // specified Declarator for the function. + DeclTy *Res = Actions.ActOnStartOfFunctionDef(CurScope, D); + + return ParseFunctionStatementBody(Res, BraceLoc, BraceLoc); +} + +/// ParseKNRParamDeclarations - Parse 'declaration-list[opt]' which provides +/// types for a function with a K&R-style identifier list for arguments. +void Parser::ParseKNRParamDeclarations(Declarator &D) { + // We know that the top-level of this declarator is a function. + DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun; + + // Read all the argument declarations. + while (isDeclarationSpecifier()) { + SourceLocation DSStart = Tok.getLocation(); + + // Parse the common declaration-specifiers piece. + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + + // C99 6.9.1p6: 'each declaration in the declaration list shall have at + // least one declarator'. + // NOTE: GCC just makes this an ext-warn. It's not clear what it does with + // the declarations though. It's trivial to ignore them, really hard to do + // anything else with them. + if (Tok.is(tok::semi)) { + Diag(DSStart, diag::err_declaration_does_not_declare_param); + ConsumeToken(); + continue; + } + + // C99 6.9.1p6: Declarations shall contain no storage-class specifiers other + // than register. + if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified && + DS.getStorageClassSpec() != DeclSpec::SCS_register) { + Diag(DS.getStorageClassSpecLoc(), + diag::err_invalid_storage_class_in_func_decl); + DS.ClearStorageClassSpecs(); + } + if (DS.isThreadSpecified()) { + Diag(DS.getThreadSpecLoc(), + diag::err_invalid_storage_class_in_func_decl); + DS.ClearStorageClassSpecs(); + } + + // Parse the first declarator attached to this declspec. + Declarator ParmDeclarator(DS, Declarator::KNRTypeListContext); + ParseDeclarator(ParmDeclarator); + + // Handle the full declarator list. + while (1) { + DeclTy *AttrList; + // If attributes are present, parse them. + if (Tok.is(tok::kw___attribute)) + // FIXME: attach attributes too. + AttrList = ParseAttributes(); + + // Ask the actions module to compute the type for this declarator. + Action::TypeResult TR = + Actions.ActOnParamDeclaratorType(CurScope, ParmDeclarator); + + if (!TR.isInvalid && + // A missing identifier has already been diagnosed. + ParmDeclarator.getIdentifier()) { + + // Scan the argument list looking for the correct param to apply this + // type. + for (unsigned i = 0; ; ++i) { + // C99 6.9.1p6: those declarators shall declare only identifiers from + // the identifier list. + if (i == FTI.NumArgs) { + Diag(ParmDeclarator.getIdentifierLoc(), diag::err_no_matching_param, + ParmDeclarator.getIdentifier()->getName()); + break; + } + + if (FTI.ArgInfo[i].Ident == ParmDeclarator.getIdentifier()) { + // Reject redefinitions of parameters. + if (FTI.ArgInfo[i].TypeInfo) { + Diag(ParmDeclarator.getIdentifierLoc(), + diag::err_param_redefinition, + ParmDeclarator.getIdentifier()->getName()); + } else { + FTI.ArgInfo[i].TypeInfo = TR.Val; + } + break; + } + } + } + + // If we don't have a comma, it is either the end of the list (a ';') or + // an error, bail out. + if (Tok.isNot(tok::comma)) + break; + + // Consume the comma. + ConsumeToken(); + + // Parse the next declarator. + ParmDeclarator.clear(); + ParseDeclarator(ParmDeclarator); + } + + if (Tok.is(tok::semi)) { + ConsumeToken(); + } else { + Diag(Tok, diag::err_parse_error); + // Skip to end of block or statement + SkipUntil(tok::semi, true); + if (Tok.is(tok::semi)) + ConsumeToken(); + } + } + + // The actions module must verify that all arguments were declared. +} + + +/// ParseAsmStringLiteral - This is just a normal string-literal, but is not +/// allowed to be a wide string, and is not subject to character translation. +/// +/// [GNU] asm-string-literal: +/// string-literal +/// +Parser::ExprResult Parser::ParseAsmStringLiteral() { + if (!isTokenStringLiteral()) { + Diag(Tok, diag::err_expected_string_literal); + return true; + } + + ExprResult Res = ParseStringLiteralExpression(); + if (Res.isInvalid) return true; + + // TODO: Diagnose: wide string literal in 'asm' + + return Res; +} + +/// ParseSimpleAsm +/// +/// [GNU] simple-asm-expr: +/// 'asm' '(' asm-string-literal ')' +/// +Parser::ExprResult Parser::ParseSimpleAsm() { + assert(Tok.is(tok::kw_asm) && "Not an asm!"); + SourceLocation Loc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "asm"); + return 0; + } + + ConsumeParen(); + + ExprResult Result = ParseAsmStringLiteral(); + + MatchRHSPunctuation(tok::r_paren, Loc); + + return Result; +} + |