Implement support for C++ nested-name-specifiers ('foo::bar::x') in the Parser side.
No Sema functionality change, just the signatures of the Action/Sema methods.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58913 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/Driver/PrintParserCallbacks.cpp b/Driver/PrintParserCallbacks.cpp
index 996051a..adf3bed 100644
--- a/Driver/PrintParserCallbacks.cpp
+++ b/Driver/PrintParserCallbacks.cpp
@@ -174,8 +174,9 @@
}
virtual DeclTy *ActOnTag(Scope *S, unsigned TagType, TagKind TK,
- SourceLocation KWLoc, IdentifierInfo *Name,
- SourceLocation NameLoc, AttributeList *Attr) {
+ SourceLocation KWLoc, const CXXScopeSpec &SS,
+ IdentifierInfo *Name, SourceLocation NameLoc,
+ AttributeList *Attr) {
// TagType is an instance of DeclSpec::TST, indicating what kind of tag this
// is (struct/union/enum/class).
llvm::cout << __FUNCTION__ << "\n";
@@ -399,7 +400,8 @@
/// token immediately after it.
virtual ExprResult ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
IdentifierInfo &II,
- bool HasTrailingLParen) {
+ bool HasTrailingLParen,
+ const CXXScopeSpec *SS) {
llvm::cout << __FUNCTION__ << "\n";
return 0;
}
diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def
index 0067061..fa39182 100644
--- a/include/clang/Basic/DiagnosticKinds.def
+++ b/include/clang/Basic/DiagnosticKinds.def
@@ -550,6 +550,8 @@
"property type '%0' does not match property type inherited from '%1'")
/// C++ parser diagnostics
+DIAG(err_expected_unqualified_id, ERROR,
+ "expected unqualified-id")
DIAG(err_no_declarators, ERROR,
"declaration does not declare anything")
DIAG(err_func_def_no_params, ERROR,
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h
index 698feff..038c858 100644
--- a/include/clang/Parse/Action.h
+++ b/include/clang/Parse/Action.h
@@ -22,6 +22,7 @@
// Semantic.
class DeclSpec;
class ObjCDeclSpec;
+ class CXXScopeSpec;
class Declarator;
class AttributeList;
struct FieldDeclarator;
@@ -60,6 +61,7 @@
typedef void AttrTy;
typedef void BaseTy;
typedef void MemInitTy;
+ typedef void CXXScopeTy;
/// ActionResult - This structure is used while parsing/acting on expressions,
/// stmts, etc. It encapsulates both the object returned by the action, plus
@@ -103,11 +105,54 @@
/// isTypeName - Return non-null if the specified identifier is a typedef name
/// in the current scope.
- virtual TypeTy *isTypeName(const IdentifierInfo &II, Scope *S) = 0;
+ /// An optional CXXScopeSpec can be passed to indicate the C++ scope (class or
+ /// namespace) that the identifier must be a member of.
+ /// i.e. for "foo::bar", 'II' will be "bar" and 'SS' will be "foo::".
+ virtual TypeTy *isTypeName(const IdentifierInfo &II, Scope *S,
+ const CXXScopeSpec *SS = 0) = 0;
/// isCurrentClassName - Return true if the specified name is the
/// name of the innermost C++ class type currently being defined.
- virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S) = 0;
+ virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
+ const CXXScopeSpec *SS = 0) = 0;
+
+ /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the
+ /// global scope ('::').
+ virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S,
+ SourceLocation CCLoc) {
+ return 0;
+ }
+
+ /// ActOnCXXNestedNameSpecifier - Called during parsing of a
+ /// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now
+ /// we want to resolve "bar::". 'SS' is empty or the previously parsed
+ /// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar',
+ /// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'.
+ /// Returns a CXXScopeTy* object representing the C++ scope.
+ virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
+ const CXXScopeSpec &SS,
+ SourceLocation IdLoc,
+ SourceLocation CCLoc,
+ const IdentifierInfo &II) {
+ return 0;
+ }
+
+ /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
+ /// scope or nested-name-specifier) is parsed, part of a declarator-id.
+ /// After this method is called, according to [C++ 3.4.3p3], names should be
+ /// looked up in the declarator-id's scope, until the declarator is parsed and
+ /// ActOnCXXExitDeclaratorScope is called.
+ /// The 'SS' should be a non-empty valid CXXScopeSpec.
+ virtual void ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
+ }
+
+ /// ActOnCXXExitDeclaratorScope - Called when a declarator that previously
+ /// invoked ActOnCXXEnterDeclaratorScope(), is finished. 'SS' is the same
+ /// CXXScopeSpec that was passed to ActOnCXXEnterDeclaratorScope as well.
+ /// Used to indicate that names should revert to being looked up in the
+ /// defining scope.
+ virtual void ActOnCXXExitDeclaratorScope(const CXXScopeSpec &SS) {
+ }
/// getTypeAsString - Returns a string that describes the given
/// type. This callback is used in C++ to form identifiers for
@@ -227,8 +272,9 @@
TK_Definition // Definition of a tag: 'struct foo { int X; } Y;'
};
virtual DeclTy *ActOnTag(Scope *S, unsigned TagType, TagKind TK,
- SourceLocation KWLoc, IdentifierInfo *Name,
- SourceLocation NameLoc, AttributeList *Attr) {
+ SourceLocation KWLoc, const CXXScopeSpec &SS,
+ IdentifierInfo *Name, SourceLocation NameLoc,
+ AttributeList *Attr) {
// TagType is an instance of DeclSpec::TST, indicating what kind of tag this
// is (struct/union/enum/class).
return 0;
@@ -413,9 +459,13 @@
/// ActOnIdentifierExpr - Parse an identifier in expression context.
/// 'HasTrailingLParen' indicates whether or not the identifier has a '('
/// token immediately after it.
+ /// An optional CXXScopeSpec can be passed to indicate the C++ scope (class or
+ /// namespace) that the identifier must be a member of.
+ /// i.e. for "foo::bar", 'II' will be "bar" and 'SS' will be "foo::".
virtual ExprResult ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
IdentifierInfo &II,
- bool HasTrailingLParen) {
+ bool HasTrailingLParen,
+ const CXXScopeSpec *SS = 0) {
return 0;
}
@@ -968,11 +1018,13 @@
/// isTypeName - This looks at the IdentifierInfo::FETokenInfo field to
/// determine whether the name is a typedef or not in this scope.
- virtual TypeTy *isTypeName(const IdentifierInfo &II, Scope *S);
+ virtual TypeTy *isTypeName(const IdentifierInfo &II, Scope *S,
+ const CXXScopeSpec *SS);
/// isCurrentClassName - Always returns false, because MinimalAction
/// does not support C++ classes with constructors.
- virtual bool isCurrentClassName(const IdentifierInfo& II, Scope *S);
+ virtual bool isCurrentClassName(const IdentifierInfo& II, Scope *S,
+ const CXXScopeSpec *SS);
/// ActOnDeclarator - If this is a typedef declarator, we modify the
/// IdentifierInfo::FETokenInfo field to keep track of this fact, until S is
diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h
index 76e3e7b..94bd839 100644
--- a/include/clang/Parse/DeclSpec.h
+++ b/include/clang/Parse/DeclSpec.h
@@ -385,6 +385,40 @@
IdentifierInfo *GetterName; // getter name of NULL if no getter
IdentifierInfo *SetterName; // setter name of NULL if no setter
};
+
+/// CXXScopeSpec - Represents a C++ nested-name-specifier or a global scope
+/// specifier.
+class CXXScopeSpec {
+ SourceRange Range;
+ Action::CXXScopeTy *ScopeRep;
+
+public:
+ CXXScopeSpec() : ScopeRep(0) {}
+
+ const SourceRange &getRange() const { return Range; }
+ void setRange(const SourceRange &R) { Range = R; }
+ void setBeginLoc(SourceLocation Loc) { Range.setBegin(Loc); }
+ void setEndLoc(SourceLocation Loc) { Range.setEnd(Loc); }
+ SourceLocation getBeginLoc() const { return Range.getBegin(); }
+ SourceLocation getEndLoc() const { return Range.getEnd(); }
+
+ Action::CXXScopeTy *getScopeRep() const { return ScopeRep; }
+ void setScopeRep(Action::CXXScopeTy *S) { ScopeRep = S; }
+
+ bool isEmpty() const { return !Range.isValid(); }
+ bool isNotEmpty() const { return !isEmpty(); }
+
+ /// isInvalid - An error occured during parsing of the scope specifier.
+ bool isInvalid() const { return isNotEmpty() && ScopeRep == 0; }
+
+ /// isSet - A scope specifier was resolved to a valid C++ scope.
+ bool isSet() const { return getScopeRep() != 0; }
+
+ void clear() {
+ Range = SourceRange();
+ ScopeRep = 0;
+ }
+};
/// DeclaratorChunk - One instance of this struct is used for each type in a
/// declarator that is parsed.
@@ -590,6 +624,7 @@
/// stack, not objects that are allocated in large quantities on the heap.
class Declarator {
const DeclSpec &DS;
+ CXXScopeSpec SS;
IdentifierInfo *Identifier;
SourceLocation IdentifierLoc;
@@ -667,6 +702,11 @@
/// be shared or when in error recovery etc.
DeclSpec &getMutableDeclSpec() { return const_cast<DeclSpec &>(DS); }
+ /// getCXXScopeSpec - Return the C++ scope specifier (global scope or
+ /// nested-name-specifier) that is part of the declarator-id.
+ const CXXScopeSpec &getCXXScopeSpec() const { return SS; }
+ CXXScopeSpec &getCXXScopeSpec() { return SS; }
+
TheContext getContext() const { return Context; }
DeclaratorKind getKind() const { return Kind; }
@@ -675,6 +715,7 @@
/// clear - Reset the contents of this Declarator.
void clear() {
+ SS.clear();
Identifier = 0;
IdentifierLoc = SourceLocation();
Kind = DK_Abstract;
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index f9b233c..c7558db 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -16,6 +16,7 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/Action.h"
+#include "clang/Parse/DeclSpec.h"
#include <stack>
namespace clang {
@@ -75,6 +76,7 @@
typedef Action::TypeTy TypeTy;
typedef Action::BaseTy BaseTy;
typedef Action::MemInitTy MemInitTy;
+ typedef Action::CXXScopeTy CXXScopeTy;
// Parsing methods.
@@ -114,7 +116,35 @@
return Tok.getKind() == tok::string_literal ||
Tok.getKind() == tok::wide_string_literal;
}
+
+ /// isTokenCXXScopeSpecifier - True if this token is '::', or identifier with
+ /// '::' as next token, or a 'C++ scope annotation' token.
+ /// When not in C++, always returns false.
+ ///
+ bool isTokenCXXScopeSpecifier() {
+ return getLang().CPlusPlus &&
+ (Tok.is(tok::coloncolon) ||
+ Tok.is(tok::annot_cxxscope) ||
+ (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)));
+ }
+ /// isTokenUnqualifiedId - True if token is the start of C++ unqualified-id
+ /// or an identifier in C.
+ ///
+ /// unqualified-id:
+ /// identifier
+ /// [C++] operator-function-id
+ /// [C++] conversion-function-id
+ /// [C++] '~' class-name
+ /// [C++] template-id [TODO]
+ ///
+ bool isTokenUnqualifiedId() const {
+ return Tok.is(tok::identifier) || // identifier or template-id
+ Tok.is(tok::kw_operator) || // operator/conversion-function-id or
+ // template-id
+ (Tok.is(tok::tilde) && getLang().CPlusPlus); // '~' class-name
+ }
+
/// ConsumeToken - Consume the current 'peek token' and lex the next one.
/// This does not work with all kinds of tokens: strings and specific other
/// tokens must be consumed with custom methods below. This returns the
@@ -215,6 +245,20 @@
return PP.LookAhead(0);
}
+ /// TryAnnotateTypeOrScopeToken - If the current token position is on a
+ /// typename (possibly qualified in C++) or a C++ scope specifier not followed
+ /// by a typename, TryAnnotateTypeOrScopeToken will replace one or more tokens
+ /// with a single annotation token representing the typename or C++ scope
+ /// respectively.
+ /// This simplifies handling of C++ scope specifiers and allows efficient
+ /// backtracking without the need to re-parse and resolve nested-names and
+ /// typenames.
+ void TryAnnotateTypeOrScopeToken();
+
+ /// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only
+ /// annotates C++ scope specifiers.
+ void TryAnnotateScopeToken();
+
/// TentativeParsingAction - An object that is used as a kind of "tentative
/// parsing transaction". It gets instantiated to mark the token position and
/// after the token consumption is done, Commit() or Revert() is called to
@@ -449,6 +493,11 @@
return ParseParenExpression(Op, CastTy, RParenLoc);
}
ExprResult ParseStringLiteralExpression();
+
+ //===--------------------------------------------------------------------===//
+ // C++ Expressions
+ ExprResult ParseCXXIdExpression();
+ void ParseCXXScopeSpecifier(CXXScopeSpec &SS);
//===--------------------------------------------------------------------===//
// C++ 5.2p1: C++ Casts
@@ -583,8 +632,8 @@
void ParseStructDeclaration(DeclSpec &DS,
llvm::SmallVectorImpl<FieldDeclarator> &Fields);
- bool isDeclarationSpecifier() const;
- bool isTypeSpecifierQualifier() const;
+ bool isDeclarationSpecifier();
+ bool isTypeSpecifierQualifier();
bool isTypeQualifier() const;
/// isDeclarationStatement - Disambiguates between a declaration or an
@@ -700,6 +749,26 @@
TypeTy *ParseTypeName();
AttributeList *ParseAttributes();
void ParseTypeofSpecifier(DeclSpec &DS);
+
+ /// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to
+ /// enter a new C++ declarator scope and exit it when the function is
+ /// finished.
+ class DeclaratorScopeObj {
+ CXXScopeSpec &SS;
+ Parser &P;
+ public:
+ DeclaratorScopeObj(Parser &p, CXXScopeSpec &ss) : P(p), SS(ss) {}
+
+ void EnterDeclaratorScope() {
+ if (SS.isSet())
+ P.Actions.ActOnCXXEnterDeclaratorScope(P.CurScope, SS);
+ }
+
+ ~DeclaratorScopeObj() {
+ if (SS.isSet())
+ P.Actions.ActOnCXXExitDeclaratorScope(SS);
+ }
+ };
/// ParseDeclarator - Parse and verify a newly-initialized declarator.
void ParseDeclarator(Declarator &D);
@@ -722,7 +791,7 @@
//===--------------------------------------------------------------------===//
// C++ 9: classes [class] and C structs/unions.
- TypeTy *ParseClassName();
+ TypeTy *ParseClassName(const CXXScopeSpec *SS = 0);
void ParseClassSpecifier(DeclSpec &DS);
void ParseCXXMemberSpecification(SourceLocation StartLoc, unsigned TagType,
DeclTy *TagDecl);
diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp
index 2fec359..58e391e 100644
--- a/lib/Parse/MinimalAction.cpp
+++ b/lib/Parse/MinimalAction.cpp
@@ -55,8 +55,11 @@
/// 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.
+///
+/// FIXME: Use the passed CXXScopeSpec for accurate C++ type checking.
Action::TypeTy *
-MinimalAction::isTypeName(const IdentifierInfo &II, Scope *S) {
+MinimalAction::isTypeName(const IdentifierInfo &II, Scope *S,
+ const CXXScopeSpec *SS) {
if (TypeNameInfo *TI = II.getFETokenInfo<TypeNameInfo>())
if (TI->isTypeName)
return TI;
@@ -65,7 +68,8 @@
/// isCurrentClassName - Always returns false, because MinimalAction
/// does not support C++ classes with constructors.
-bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *) {
+bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *,
+ const CXXScopeSpec *) {
return false;
}
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 2b18be0..e3094ad 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -13,7 +13,6 @@
#include "clang/Parse/Parser.h"
#include "clang/Basic/Diagnostic.h"
-#include "clang/Parse/DeclSpec.h"
#include "clang/Parse/Scope.h"
#include "ExtensionRAIIObject.h"
#include "llvm/ADT/SmallSet.h"
@@ -419,6 +418,10 @@
const char *PrevSpec = 0;
SourceLocation Loc = Tok.getLocation();
+ // Only annotate C++ scope. Allow class-name as an identifier in case
+ // it's a constructor.
+ TryAnnotateScopeToken();
+
switch (Tok.getKind()) {
default:
// Try to parse a type-specifier; if we found one, continue.
@@ -430,7 +433,45 @@
// specifiers. First verify that DeclSpec's are consistent.
DS.Finish(Diags, PP.getSourceManager(), getLang());
return;
-
+
+ case tok::annot_cxxscope: {
+ if (DS.hasTypeSpecifier())
+ goto DoneWithDeclSpec;
+
+ // We are looking for a qualified typename.
+ if (NextToken().isNot(tok::identifier))
+ goto DoneWithDeclSpec;
+
+ CXXScopeSpec SS;
+ SS.setScopeRep(Tok.getAnnotationValue());
+ SS.setRange(Tok.getAnnotationRange());
+
+ // If the next token is the name of the class type that the C++ scope
+ // denotes, followed by a '(', then this is a constructor declaration.
+ // We're done with the decl-specifiers.
+ if (Actions.isCurrentClassName(*NextToken().getIdentifierInfo(),
+ CurScope, &SS) &&
+ GetLookAheadToken(2).is(tok::l_paren))
+ goto DoneWithDeclSpec;
+
+ TypeTy *TypeRep = Actions.isTypeName(*NextToken().getIdentifierInfo(),
+ CurScope, &SS);
+ if (TypeRep == 0)
+ goto DoneWithDeclSpec;
+
+ ConsumeToken(); // The C++ scope.
+
+ isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typedef, Loc, PrevSpec,
+ TypeRep);
+ if (isInvalid)
+ break;
+
+ DS.SetRangeEnd(Tok.getLocation());
+ ConsumeToken(); // The typename.
+
+ continue;
+ }
+
// typedef-name
case tok::identifier: {
// This identifier can only be a typedef name if we haven't already seen
@@ -605,19 +646,18 @@
/// [OBJC] typedef-name objc-protocol-refs[opt] [TODO]
bool Parser::MaybeParseTypeSpecifier(DeclSpec &DS, int& isInvalid,
const char *&PrevSpec) {
+ // Annotate typenames and C++ scope specifiers.
+ TryAnnotateTypeOrScopeToken();
+
SourceLocation Loc = Tok.getLocation();
switch (Tok.getKind()) {
// simple-type-specifier:
- case tok::identifier: {
- TypeTy *TypeRep = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope);
- if (!TypeRep)
- return false;
-
+ case tok::annot_qualtypename: {
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typedef, Loc, PrevSpec,
- TypeRep);
- DS.SetRangeEnd(Loc);
- ConsumeToken(); // The identifier
+ Tok.getAnnotationValue());
+ DS.SetRangeEnd(Tok.getAnnotationEndLoc());
+ ConsumeToken(); // The typename
// Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id'
// is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an
@@ -914,11 +954,15 @@
/// ParseEnumSpecifier
/// enum-specifier: [C99 6.7.2.2]
/// 'enum' identifier[opt] '{' enumerator-list '}'
-/// [C99] 'enum' identifier[opt] '{' enumerator-list ',' '}'
+///[C99/C++]'enum' identifier[opt] '{' enumerator-list ',' '}'
/// [GNU] 'enum' attributes[opt] identifier[opt] '{' enumerator-list ',' [opt]
/// '}' attributes[opt]
/// 'enum' identifier
/// [GNU] 'enum' attributes[opt] identifier
+///
+/// [C++] elaborated-type-specifier:
+/// [C++] 'enum' '::'[opt] nested-name-specifier[opt] identifier
+///
void Parser::ParseEnumSpecifier(DeclSpec &DS) {
assert(Tok.is(tok::kw_enum) && "Not an enum specifier");
SourceLocation StartLoc = ConsumeToken();
@@ -929,6 +973,20 @@
// If attributes exist after tag, parse them.
if (Tok.is(tok::kw___attribute))
Attr = ParseAttributes();
+
+ CXXScopeSpec SS;
+ if (isTokenCXXScopeSpecifier()) {
+ ParseCXXScopeSpecifier(SS);
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected_ident);
+ if (Tok.isNot(tok::l_brace)) {
+ // Has no name and is not a definition.
+ // Skip the rest of this declarator, up until the comma or semicolon.
+ SkipUntil(tok::comma, true);
+ return;
+ }
+ }
+ }
// Must have either 'enum name' or 'enum {...}'.
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace)) {
@@ -963,7 +1021,7 @@
else
TK = Action::TK_Reference;
DeclTy *TagDecl = Actions.ActOnTag(CurScope, DeclSpec::TST_enum, TK, StartLoc,
- Name, NameLoc, Attr);
+ SS, Name, NameLoc, Attr);
if (Tok.is(tok::l_brace))
ParseEnumBody(StartLoc, TagDecl);
@@ -1054,7 +1112,10 @@
/// isTypeSpecifierQualifier - Return true if the current token could be the
/// start of a specifier-qualifier-list.
-bool Parser::isTypeSpecifierQualifier() const {
+bool Parser::isTypeSpecifierQualifier() {
+ // Annotate typenames and C++ scope specifiers.
+ TryAnnotateTypeOrScopeToken();
+
switch (Tok.getKind()) {
default: return false;
// GNU attributes support.
@@ -1092,21 +1153,23 @@
case tok::kw_const:
case tok::kw_volatile:
case tok::kw_restrict:
+
+ // typedef-name
+ case tok::annot_qualtypename:
return true;
// GNU ObjC bizarre protocol extension: <proto1,proto2> with implicit 'id'.
case tok::less:
return getLang().ObjC1;
-
- // 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 {
+bool Parser::isDeclarationSpecifier() {
+ // Annotate typenames and C++ scope specifiers.
+ TryAnnotateTypeOrScopeToken();
+
switch (Tok.getKind()) {
default: return false;
// storage-class-specifier
@@ -1154,6 +1217,9 @@
case tok::kw_virtual:
case tok::kw_explicit:
+ // typedef-name
+ case tok::annot_qualtypename:
+
// GNU typeof support.
case tok::kw_typeof:
@@ -1164,10 +1230,6 @@
// GNU ObjC bizarre protocol extension: <proto1,proto2> with implicit 'id'.
case tok::less:
return getLang().ObjC1;
-
- // typedef-name
- case tok::identifier:
- return Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope) != 0;
}
}
@@ -1351,12 +1413,22 @@
///
/// unqualified-id: [C++ 5.1]
/// identifier
-/// operator-function-id [TODO]
+/// operator-function-id
/// conversion-function-id [TODO]
/// '~' class-name
/// template-id [TODO]
///
void Parser::ParseDirectDeclarator(Declarator &D) {
+ CXXScopeSpec &SS = D.getCXXScopeSpec();
+ DeclaratorScopeObj DeclScopeObj(*this, SS);
+
+ if (D.mayHaveIdentifier() && isTokenCXXScopeSpecifier()) {
+ ParseCXXScopeSpecifier(SS);
+ // Change the declaration context for name lookup, until this function is
+ // exited (and the declarator has been parsed).
+ DeclScopeObj.EnterDeclaratorScope();
+ }
+
// Parse the first direct-declarator seen.
if (Tok.is(tok::identifier) && D.mayHaveIdentifier()) {
assert(Tok.getIdentifierInfo() && "Not an identifier?");
@@ -1407,18 +1479,20 @@
D.SetConversionFunction(ConvType, II, OperatorLoc);
}
}
- } else if (Tok.is(tok::l_paren)) {
+ } else if (Tok.is(tok::l_paren) && SS.isEmpty()) {
// direct-declarator: '(' declarator ')'
// direct-declarator: '(' attributes declarator ')'
// Example: 'char (*X)' or 'int (*XX)(void)'
ParseParenDeclarator(D);
- } else if (D.mayOmitIdentifier()) {
+ } else if (D.mayOmitIdentifier() && SS.isEmpty()) {
// 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);
+ if (getLang().CPlusPlus)
+ Diag(Tok, diag::err_expected_unqualified_id);
+ else
+ Diag(Tok, diag::err_expected_ident_lparen); // Expected identifier or '('.
D.SetIdentifier(0, Tok.getLocation());
}
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index f90469a..752d552 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -138,7 +138,7 @@
/// identifier
/// template-id [TODO]
///
-Parser::TypeTy *Parser::ParseClassName() {
+Parser::TypeTy *Parser::ParseClassName(const CXXScopeSpec *SS) {
// Parse the class-name.
// FIXME: Alternatively, parse a simple-template-id.
if (Tok.isNot(tok::identifier)) {
@@ -147,7 +147,7 @@
}
// We have an identifier; check whether it is actually a type.
- TypeTy *Type = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope);
+ TypeTy *Type = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope, SS);
if (!Type) {
Diag(Tok.getLocation(), diag::err_expected_class_name);
return 0;
@@ -216,7 +216,13 @@
if (Tok.is(tok::kw___attribute))
Attr = ParseAttributes();
- // FIXME: Parse the (optional) nested-name-specifier.
+ // Parse the (optional) nested-name-specifier.
+ CXXScopeSpec SS;
+ if (isTokenCXXScopeSpecifier()) {
+ ParseCXXScopeSpecifier(SS);
+ if (Tok.isNot(tok::identifier))
+ Diag(Tok, diag::err_expected_ident);
+ }
// Parse the (optional) class name.
// FIXME: Alternatively, parse a simple-template-id.
@@ -250,7 +256,7 @@
}
// Parse the tag portion of this.
- DeclTy *TagDecl = Actions.ActOnTag(CurScope, TagType, TK, StartLoc, Name,
+ DeclTy *TagDecl = Actions.ActOnTag(CurScope, TagType, TK, StartLoc, SS, Name,
NameLoc, Attr);
// Parse the optional base clause (C++ only).
@@ -354,13 +360,16 @@
IsVirtual = true;
}
- // FIXME: Parse optional '::' and optional nested-name-specifier.
+ // Parse optional '::' and optional nested-name-specifier.
+ CXXScopeSpec SS;
+ if (isTokenCXXScopeSpecifier())
+ ParseCXXScopeSpecifier(SS);
// The location of the base class itself.
SourceLocation BaseLoc = Tok.getLocation();
// Parse the class-name.
- TypeTy *BaseType = ParseClassName();
+ TypeTy *BaseType = ParseClassName(&SS);
if (!BaseType)
return true;
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 4515e4b..e8758e1 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -401,7 +401,14 @@
/// conversion-function-id [TODO]
/// '~' class-name [TODO]
/// template-id [TODO]
+///
Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) {
+ if (getLang().CPlusPlus) {
+ // Annotate typenames and C++ scope specifiers.
+ // Used only in C++; in C let the typedef name be handled as an identifier.
+ TryAnnotateTypeOrScopeToken();
+ }
+
ExprResult Res;
tok::TokenKind SavedKind = Tok.getKind();
@@ -463,17 +470,9 @@
case tok::kw_false:
return ParseCXXBoolLiteral();
- case tok::identifier: {
- if (getLang().CPlusPlus &&
- Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope)) {
- // Handle C++ function-style cast, e.g. "T(4.5)" where T is a typedef for
- // double.
- goto HandleType;
- }
-
- // primary-expression: identifier
- // unqualified-id: identifier
- // constant: enumeration-constant
+ case tok::identifier: { // primary-expression: identifier
+ // unqualified-id: 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
@@ -587,7 +586,8 @@
case tok::kw_typeof: {
if (!getLang().CPlusPlus)
goto UnhandledToken;
- HandleType:
+ case tok::annot_qualtypename:
+ assert(getLang().CPlusPlus && "Expected C++");
// postfix-expression: simple-type-specifier '(' expression-list[opt] ')'
//
DeclSpec DS;
@@ -601,16 +601,11 @@
return ParsePostfixExpressionSuffix(Res);
}
- case tok::kw_operator: {
- SourceLocation OperatorLoc = Tok.getLocation();
- if (IdentifierInfo *II = MaybeParseOperatorFunctionId()) {
- Res = Actions.ActOnIdentifierExpr(CurScope, OperatorLoc, *II,
- Tok.is(tok::l_paren));
- // These can be followed by postfix-expr pieces.
- return ParsePostfixExpressionSuffix(Res);
- }
- break;
- }
+ case tok::annot_cxxscope: // [C++] id-expression: qualified-id
+ case tok::kw_operator: // [C++] id-expression: operator/conversion-function-id
+ // template-id
+ Res = ParseCXXIdExpression();
+ return ParsePostfixExpressionSuffix(Res);
case tok::at: {
SourceLocation AtLoc = ConsumeToken();
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 7ce136c..2fe3bcf 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -16,6 +16,154 @@
#include "clang/Parse/DeclSpec.h"
using namespace clang;
+/// ParseCXXScopeSpecifier - Parse global scope or nested-name-specifier.
+///
+/// '::'[opt] nested-name-specifier
+/// '::'
+///
+/// nested-name-specifier:
+/// type-name '::'
+/// namespace-name '::'
+/// nested-name-specifier identifier '::'
+/// nested-name-specifier 'template'[opt] simple-template-id '::' [TODO]
+///
+void Parser::ParseCXXScopeSpecifier(CXXScopeSpec &SS) {
+ assert(isTokenCXXScopeSpecifier() && "Not scope specifier!");
+
+ if (Tok.is(tok::annot_cxxscope)) {
+ SS.setScopeRep(Tok.getAnnotationValue());
+ SS.setRange(Tok.getAnnotationRange());
+ ConsumeToken();
+ return;
+ }
+
+ SS.setBeginLoc(Tok.getLocation());
+
+ // '::'
+
+ if (Tok.is(tok::coloncolon)) {
+ // Global scope.
+ SourceLocation CCLoc = ConsumeToken();
+ SS.setScopeRep(Actions.ActOnCXXGlobalScopeSpecifier(CurScope, CCLoc));
+ SS.setEndLoc(CCLoc);
+ }
+
+ // nested-name-specifier:
+ // type-name '::'
+ // namespace-name '::'
+ // nested-name-specifier identifier '::'
+ // nested-name-specifier 'template'[opt] simple-template-id '::' [TODO]
+
+ while (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ SourceLocation IdLoc = ConsumeToken();
+ assert(Tok.is(tok::coloncolon) &&
+ "NextToken() not working properly!");
+ SourceLocation CCLoc = ConsumeToken();
+ if (SS.isInvalid())
+ continue;
+
+ SS.setScopeRep(
+ Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, *II) );
+ SS.setEndLoc(CCLoc);
+ }
+}
+
+/// ParseCXXIdExpression - Handle id-expression.
+///
+/// id-expression:
+/// unqualified-id
+/// qualified-id
+///
+/// unqualified-id:
+/// identifier
+/// operator-function-id
+/// conversion-function-id [TODO]
+/// '~' class-name [TODO]
+/// template-id [TODO]
+///
+/// qualified-id:
+/// '::'[opt] nested-name-specifier 'template'[opt] unqualified-id
+/// '::' identifier
+/// '::' operator-function-id
+/// '::' template-id [TODO]
+///
+/// nested-name-specifier:
+/// type-name '::'
+/// namespace-name '::'
+/// nested-name-specifier identifier '::'
+/// nested-name-specifier 'template'[opt] simple-template-id '::' [TODO]
+///
+/// NOTE: The standard specifies that, for qualified-id, the parser does not
+/// expect:
+///
+/// '::' conversion-function-id
+/// '::' '~' class-name
+///
+/// This may cause a slight inconsistency on diagnostics:
+///
+/// class C {};
+/// namespace A {}
+/// void f() {
+/// :: A :: ~ C(); // Some Sema error about using destructor with a
+/// // namespace.
+/// :: ~ C(); // Some Parser error like 'unexpected ~'.
+/// }
+///
+/// We simplify the parser a bit and make it work like:
+///
+/// qualified-id:
+/// '::'[opt] nested-name-specifier 'template'[opt] unqualified-id
+/// '::' unqualified-id
+///
+/// That way Sema can handle and report similar errors for namespaces and the
+/// global scope.
+///
+Parser::ExprResult Parser::ParseCXXIdExpression() {
+ // qualified-id:
+ // '::'[opt] nested-name-specifier 'template'[opt] unqualified-id
+ // '::' unqualified-id
+ //
+ CXXScopeSpec SS;
+ if (isTokenCXXScopeSpecifier())
+ ParseCXXScopeSpecifier(SS);
+
+ // unqualified-id:
+ // identifier
+ // operator-function-id
+ // conversion-function-id [TODO]
+ // '~' class-name [TODO]
+ // template-id [TODO]
+ //
+ switch (Tok.getKind()) {
+ default:
+ return Diag(Tok, diag::err_expected_unqualified_id);
+
+ case tok::identifier: {
+ // Consume the identifier so that we can see if it is followed by a '('.
+ IdentifierInfo &II = *Tok.getIdentifierInfo();
+ SourceLocation L = ConsumeToken();
+ return Actions.ActOnIdentifierExpr(CurScope, L, II,
+ Tok.is(tok::l_paren), &SS);
+ }
+
+ case tok::kw_operator: {
+ SourceLocation OperatorLoc = Tok.getLocation();
+ if (IdentifierInfo *II = MaybeParseOperatorFunctionId()) {
+ return Actions.ActOnIdentifierExpr(CurScope, OperatorLoc, *II,
+ Tok.is(tok::l_paren), &SS);
+ }
+ // FIXME: Handle conversion-function-id.
+ unsigned DiagID = PP.getDiagnostics().getCustomDiagID(Diagnostic::Error,
+ "expected operator-function-id");
+ return Diag(Tok, DiagID);
+ }
+
+ } // switch.
+
+ assert(0 && "The switch was supposed to take care everything.");
+}
+
/// ParseCXXCasts - This handles the various ways to cast expressions to another
/// type.
///
@@ -207,7 +355,7 @@
/// simple-type-specifier.
///
/// simple-type-specifier:
-/// '::'[opt] nested-name-specifier[opt] type-name [TODO]
+/// '::'[opt] nested-name-specifier[opt] type-name
/// '::'[opt] nested-name-specifier 'template' simple-template-id [TODO]
/// char
/// wchar_t
@@ -229,6 +377,9 @@
/// typedef-name
///
void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
+ // Annotate typenames and C++ scope specifiers.
+ TryAnnotateTypeOrScopeToken();
+
DS.SetRangeStart(Tok.getLocation());
const char *PrevSpec;
SourceLocation Loc = Tok.getLocation();
@@ -239,10 +390,9 @@
abort();
// type-name
- case tok::identifier: {
- TypeTy *TypeRep = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope);
- assert(TypeRep && "Identifier wasn't a type-name!");
- DS.SetTypeSpecType(DeclSpec::TST_typedef, Loc, PrevSpec, TypeRep);
+ case tok::annot_qualtypename: {
+ DS.SetTypeSpecType(DeclSpec::TST_typedef, Loc, PrevSpec,
+ Tok.getAnnotationValue());
break;
}
@@ -287,7 +437,10 @@
DS.Finish(Diags, PP.getSourceManager(), getLang());
return;
}
- DS.SetRangeEnd(Tok.getLocation());
+ if (Tok.is(tok::annot_qualtypename))
+ DS.SetRangeEnd(Tok.getAnnotationEndLoc());
+ else
+ DS.SetRangeEnd(Tok.getLocation());
ConsumeToken();
DS.Finish(Diags, PP.getSourceManager(), getLang());
}
diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp
index 3e7aeb8..4f6f117 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -569,6 +569,9 @@
/// [GNU] restrict
///
Parser::TPResult Parser::isCXXDeclarationSpecifier() {
+ // Annotate typenames and C++ scope specifiers.
+ TryAnnotateTypeOrScopeToken();
+
switch (Tok.getKind()) {
// decl-specifier:
// storage-class-specifier
@@ -634,11 +637,6 @@
// simple-type-specifier:
- case tok::identifier:
- if (!Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope))
- return TPResult::False();
- // FALL THROUGH.
-
case tok::kw_char:
case tok::kw_wchar_t:
case tok::kw_bool:
@@ -650,6 +648,7 @@
case tok::kw_float:
case tok::kw_double:
case tok::kw_void:
+ case tok::annot_qualtypename:
if (NextToken().is(tok::l_paren))
return TPResult::Ambiguous();
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index 625da6c..ee4cd7b 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -706,3 +706,74 @@
return Result;
}
+/// TryAnnotateTypeOrScopeToken - If the current token position is on a
+/// typename (possibly qualified in C++) or a C++ scope specifier not followed
+/// by a typename, TryAnnotateTypeOrScopeToken will replace one or more tokens
+/// with a single annotation token representing the typename or C++ scope
+/// respectively.
+/// This simplifies handling of C++ scope specifiers and allows efficient
+/// backtracking without the need to re-parse and resolve nested-names and
+/// typenames.
+void Parser::TryAnnotateTypeOrScopeToken() {
+ if (Tok.is(tok::annot_qualtypename) || Tok.is(tok::annot_cxxscope))
+ return;
+
+ CXXScopeSpec SS;
+ if (isTokenCXXScopeSpecifier())
+ ParseCXXScopeSpecifier(SS);
+
+ if (Tok.is(tok::identifier)) {
+ TypeTy *Ty = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope, &SS);
+ if (Ty) {
+ // This is a typename. Replace the current token in-place with an
+ // annotation type token.
+ Tok.setKind(tok::annot_qualtypename);
+ Tok.setAnnotationValue(Ty);
+ Tok.setAnnotationEndLoc(Tok.getLocation());
+ if (SS.isNotEmpty()) // it was a C++ qualified type name.
+ Tok.setLocation(SS.getBeginLoc());
+
+ // In case the tokens were cached, have Preprocessor replace them with the
+ // annotation token.
+ PP.AnnotateCachedTokens(Tok);
+ return;
+ }
+ }
+
+ if (SS.isNotEmpty()) {
+ // A C++ scope specifier that isn't followed by a typename.
+ // Push the current token back into the token stream and use an annotation
+ // scope token for current token.
+ PP.EnterToken(Tok);
+ Tok.setKind(tok::annot_cxxscope);
+ Tok.setAnnotationValue(SS.getScopeRep());
+ Tok.setAnnotationRange(SS.getRange());
+
+ // In case the tokens were cached, have Preprocessor replace them with the
+ // annotation token.
+ PP.AnnotateCachedTokens(Tok);
+ }
+}
+
+/// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only
+/// annotates C++ scope specifiers.
+void Parser::TryAnnotateScopeToken() {
+ if (Tok.is(tok::annot_cxxscope))
+ return;
+
+ if (isTokenCXXScopeSpecifier()) {
+ CXXScopeSpec SS;
+ ParseCXXScopeSpecifier(SS);
+
+ // Push the current token back into the token stream and use an annotation
+ // scope token for current token.
+ PP.EnterToken(Tok);
+ Tok.setKind(tok::annot_cxxscope);
+ Tok.setAnnotationValue(SS.getScopeRep());
+ Tok.setAnnotationRange(SS.getRange());
+
+ // In case the tokens were cached, have Preprocessor replace them with the
+ // annotation token.
+ PP.AnnotateCachedTokens(Tok);
+ }
+}
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 38f7540..c04f80d 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -270,7 +270,8 @@
//===--------------------------------------------------------------------===//
// Symbol table / Decl tracking callbacks: SemaDecl.cpp.
//
- virtual TypeTy *isTypeName(const IdentifierInfo &II, Scope *S);
+ virtual TypeTy *isTypeName(const IdentifierInfo &II, Scope *S,
+ const CXXScopeSpec *SS);
virtual std::string getTypeAsString(TypeTy *Type);
virtual DeclTy *ActOnDeclarator(Scope *S, Declarator &D, DeclTy *LastInGroup);
virtual DeclTy *ActOnParamDeclarator(Scope *S, Declarator &D);
@@ -300,8 +301,9 @@
virtual DeclTy *ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS);
virtual DeclTy *ActOnTag(Scope *S, unsigned TagType, TagKind TK,
- SourceLocation KWLoc, IdentifierInfo *Name,
- SourceLocation NameLoc, AttributeList *Attr);
+ SourceLocation KWLoc, const CXXScopeSpec &SS,
+ IdentifierInfo *Name, SourceLocation NameLoc,
+ AttributeList *Attr);
DeclTy* ActOnTagStruct(Scope *S, TagDecl::TagKind Kind, TagKind TK,
SourceLocation KWLoc, IdentifierInfo *Name,
@@ -585,7 +587,8 @@
// Primary Expressions.
virtual ExprResult ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
IdentifierInfo &II,
- bool HasTrailingLParen);
+ bool HasTrailingLParen,
+ const CXXScopeSpec *SS = 0);
virtual ExprResult ActOnPredefinedExpr(SourceLocation Loc,
tok::TokenKind Kind);
virtual ExprResult ActOnNumericConstant(const Token &);
@@ -797,7 +800,8 @@
//===--------------------------------------------------------------------===//
// C++ Classes
//
- virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S);
+ virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
+ const CXXScopeSpec *SS);
virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl,
SourceLocation LBrace);
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 18d77c4..57a5aa8 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -28,7 +28,8 @@
#include "llvm/ADT/StringExtras.h"
using namespace clang;
-Sema::TypeTy *Sema::isTypeName(const IdentifierInfo &II, Scope *S) {
+Sema::TypeTy *Sema::isTypeName(const IdentifierInfo &II, Scope *S,
+ const CXXScopeSpec *SS) {
Decl *IIDecl = LookupDecl(&II, Decl::IDNS_Ordinary, S, false);
if (IIDecl && (isa<TypedefDecl>(IIDecl) ||
@@ -2067,8 +2068,9 @@
/// TagType indicates what kind of tag this is. TK indicates whether this is a
/// reference/declaration/definition of a tag.
Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
- SourceLocation KWLoc, IdentifierInfo *Name,
- SourceLocation NameLoc, AttributeList *Attr) {
+ SourceLocation KWLoc, const CXXScopeSpec &SS,
+ IdentifierInfo *Name, SourceLocation NameLoc,
+ AttributeList *Attr) {
// If this is a use of an existing tag, it must have a name.
assert((Name != 0 || TK == TK_Definition) &&
"Nameless record must be a definition!");
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index fe7efba..2ac5804 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -262,7 +262,8 @@
/// name of the class type currently being defined. In the case of
/// nested classes, this will only return true if II is the name of
/// the innermost class.
-bool Sema::isCurrentClassName(const IdentifierInfo &II, Scope *) {
+bool Sema::isCurrentClassName(const IdentifierInfo &II, Scope *,
+ const CXXScopeSpec *SS) {
if (CXXRecordDecl *CurDecl = dyn_cast_or_null<CXXRecordDecl>(CurContext))
return &II == CurDecl->getIdentifier();
else
@@ -614,7 +615,7 @@
}
// It didn't name a member, so see if it names a class.
- TypeTy *BaseTy = isTypeName(*MemberOrBase, S);
+ TypeTy *BaseTy = isTypeName(*MemberOrBase, S, 0/*SS*/);
if (!BaseTy)
return Diag(IdLoc, diag::err_mem_init_not_member_or_class,
MemberOrBase->getName(), SourceRange(IdLoc, RParenLoc));
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index d3650c8..d638b8d 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -337,7 +337,8 @@
/// identifier is used in a function call context.
Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
IdentifierInfo &II,
- bool HasTrailingLParen) {
+ bool HasTrailingLParen,
+ const CXXScopeSpec *SS) {
// Could be enum-constant, value decl, instance variable, etc.
Decl *D = LookupDecl(&II, Decl::IDNS_Ordinary, S);
diff --git a/test/Lexer/cxx0x_keyword.cpp b/test/Lexer/cxx0x_keyword.cpp
index d55649c..a54d263 100644
--- a/test/Lexer/cxx0x_keyword.cpp
+++ b/test/Lexer/cxx0x_keyword.cpp
@@ -1,2 +1,2 @@
// RUN: clang -fsyntax-only -verify -std=c++0x %s 2>&1
-int static_assert; /* expected-error {{expected identifier or '('}} */
+int static_assert; /* expected-error {{expected unqualified-id}} */