From cc54d594d4f6509c0e3a8e349e481d9b5d899df6 Mon Sep 17 00:00:00 2001 From: Anders Carlsson Date: Sat, 22 Jan 2011 16:56:46 +0000 Subject: [PATCH] Parse class-virt-specifier-seqs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124036 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticParseKinds.td | 2 + include/clang/Parse/Parser.h | 5 +- include/clang/Sema/DeclSpec.h | 12 ++-- lib/Parse/ParseDeclCXX.cpp | 80 ++++++++++++++++++--- lib/Sema/DeclSpec.cpp | 9 ++- test/CXX/class/p1-0x.cpp | 10 +++ 6 files changed, 95 insertions(+), 23 deletions(-) create mode 100644 test/CXX/class/p1-0x.cpp diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 3da1f5d80e..2ef92b69d8 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -392,6 +392,8 @@ def warn_deleted_function_accepted_as_extension: ExtWarn< // C++0x override control def err_duplicate_virt_specifier : Error< "class member already marked '%0'">; +def err_duplicate_class_virt_specifier : Error< + "class already marked '%0'">; def err_scoped_enum_missing_identifier : Error< "scoped enumeration requires a name">; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 185f263d5e..7f22a49533 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1528,9 +1528,12 @@ private: ExprResult ParseCXX0XAlignArgument(SourceLocation Start); - VirtSpecifiers::VirtSpecifier isCXX0XVirtSpecifier() const; + VirtSpecifiers::Specifier isCXX0XVirtSpecifier() const; void ParseOptionalCXX0XVirtSpecifierSeq(VirtSpecifiers &VS); + ClassVirtSpecifiers::Specifier isCXX0XClassVirtSpecifier() const; + void ParseOptionalCXX0XClassVirtSpecifierSeq(ClassVirtSpecifiers &CVS); + /// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to /// enter a new C++ declarator scope and exit it when the function is /// finished. diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index d984ba2c54..dced53f471 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -1471,7 +1471,7 @@ struct FieldDeclarator { /// VirtSpecifiers - Represents a C++0x virt-specifier-seq. class VirtSpecifiers { public: - enum VirtSpecifier { + enum Specifier { VS_None = 0, VS_Override = 1, VS_Final = 2, @@ -1480,7 +1480,7 @@ public: VirtSpecifiers() : Specifiers(0) { } - bool SetSpecifier(VirtSpecifier VS, SourceLocation Loc, + bool SetSpecifier(Specifier VS, SourceLocation Loc, const char *&PrevSpec); bool isOverrideSpecified() const { return Specifiers & VS_Override; } @@ -1497,14 +1497,14 @@ private: SourceLocation VS_overrideLoc, VS_finalLoc, VS_newLoc; - static const char *getSpecifierName(VirtSpecifier VS); + static const char *getSpecifierName(Specifier VS); }; /// ClassVirtSpecifiers - Represents a C++0x class-virt-specifier-seq. class ClassVirtSpecifiers { public: - enum ClassVirtSpecifier { + enum Specifier { CVS_None = 0, CVS_Final = 1, CVS_Explicit = 2 @@ -1512,7 +1512,7 @@ public: ClassVirtSpecifiers() : Specifiers(0) { } - bool SetSpecifier(ClassVirtSpecifier CVS, SourceLocation Loc, + bool SetSpecifier(Specifier CVS, SourceLocation Loc, const char *&PrevSpec); bool isFinalSpecified() const { return Specifiers & CVS_Final; } @@ -1526,7 +1526,7 @@ private: SourceLocation CVS_finalLoc, CVS_explicitLoc; - static const char *getSpecifierName(ClassVirtSpecifier CVS); + static const char *getSpecifierName(Specifier CVS); }; } // end namespace clang diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 5ce4d1f00e..e2bb751b87 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -807,9 +807,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // There are four options here. If we have 'struct foo;', then this // is either a forward declaration or a friend declaration, which - // have to be treated differently. If we have 'struct foo {...' or - // 'struct foo :...' then this is a definition. Otherwise we have - // something like 'struct foo xyz', a reference. + // have to be treated differently. If we have 'struct foo {...', + // 'struct foo :...' or 'struct foo ' then this is a + // definition. Otherwise we have something like 'struct foo xyz', a reference. // However, in some contexts, things look like declarations but are just // references, e.g. // new struct s; @@ -819,7 +819,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, Sema::TagUseKind TUK; if (SuppressDeclarations) TUK = Sema::TUK_Reference; - else if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon))){ + else if (Tok.is(tok::l_brace) || + (getLang().CPlusPlus && Tok.is(tok::colon)) || + isCXX0XClassVirtSpecifier() != ClassVirtSpecifiers::CVS_None) { if (DS.isFriendSpecified()) { // C++ [class.friend]p2: // A class shall not be defined in a friend declaration. @@ -1004,7 +1006,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // If there is a body, parse it and inform the actions module. if (TUK == Sema::TUK_Definition) { assert(Tok.is(tok::l_brace) || - (getLang().CPlusPlus && Tok.is(tok::colon))); + (getLang().CPlusPlus && Tok.is(tok::colon)) || + isCXX0XClassVirtSpecifier() != ClassVirtSpecifiers::CVS_None); if (getLang().CPlusPlus) ParseCXXMemberSpecification(StartLoc, TagType, TagOrTempResult.get()); else @@ -1268,7 +1271,10 @@ void Parser::HandleMemberFunctionDefaultArgs(Declarator& DeclaratorInfo, /// override /// final /// new -VirtSpecifiers::VirtSpecifier Parser::isCXX0XVirtSpecifier() const { +VirtSpecifiers::Specifier Parser::isCXX0XVirtSpecifier() const { + if (!getLang().CPlusPlus0x) + return VirtSpecifiers::VS_None; + if (Tok.is(tok::kw_new)) return VirtSpecifiers::VS_New; @@ -1297,17 +1303,14 @@ VirtSpecifiers::VirtSpecifier Parser::isCXX0XVirtSpecifier() const { /// virt-specifier /// virt-specifier-seq virt-specifier void Parser::ParseOptionalCXX0XVirtSpecifierSeq(VirtSpecifiers &VS) { - if (!getLang().CPlusPlus0x) - return; - while (true) { - VirtSpecifiers::VirtSpecifier Specifier = isCXX0XVirtSpecifier(); + VirtSpecifiers::Specifier Specifier = isCXX0XVirtSpecifier(); if (Specifier == VirtSpecifiers::VS_None) return; // C++ [class.mem]p8: // A virt-specifier-seq shall contain at most one of each virt-specifier. - const char* PrevSpec = 0; + const char *PrevSpec = 0; if (VS.SetSpecifier(Specifier, Tok.getLocation(), PrevSpec)) Diag(Tok.getLocation(), diag::err_duplicate_virt_specifier) << PrevSpec @@ -1317,6 +1320,58 @@ void Parser::ParseOptionalCXX0XVirtSpecifierSeq(VirtSpecifiers &VS) { } } +/// isCXX0XClassVirtSpecifier - Determine whether the next token is a C++0x +/// class-virt-specifier. +/// +/// class-virt-specifier: +/// final +/// explicit +ClassVirtSpecifiers::Specifier Parser::isCXX0XClassVirtSpecifier() const { + if (!getLang().CPlusPlus0x) + return ClassVirtSpecifiers::CVS_None; + + if (Tok.is(tok::kw_explicit)) + return ClassVirtSpecifiers::CVS_Explicit; + + if (Tok.is(tok::identifier)) { + IdentifierInfo *II = Tok.getIdentifierInfo(); + + // Initialize the contextual keywords. + if (!Ident_final) { + Ident_final = &PP.getIdentifierTable().get("final"); + Ident_override = &PP.getIdentifierTable().get("override"); + } + + if (II == Ident_final) + return ClassVirtSpecifiers::CVS_Final; + } + + return ClassVirtSpecifiers::CVS_None; +} + +/// ParseOptionalCXX0XClassVirtSpecifierSeq - Parse a class-virt-specifier-seq. +/// +/// class-virt-specifier-seq: +/// class-virt-specifier +/// class-virt-specifier-seq class-virt-specifier +void Parser::ParseOptionalCXX0XClassVirtSpecifierSeq(ClassVirtSpecifiers &CVS) { + while (true) { + ClassVirtSpecifiers::Specifier Specifier = isCXX0XClassVirtSpecifier(); + if (Specifier == ClassVirtSpecifiers::CVS_None) + return; + + // C++ [class]p1: + // A class-virt-specifier-seq shall contain at most one of each + // class-virt-specifier. + const char *PrevSpec = 0; + if (CVS.SetSpecifier(Specifier, Tok.getLocation(), PrevSpec)) + Diag(Tok.getLocation(), diag::err_duplicate_class_virt_specifier) + << PrevSpec + << FixItHint::CreateRemoval(Tok.getLocation()); + ConsumeToken(); + } +} + /// ParseCXXClassMemberDeclaration - Parse a C++ class member declaration. /// /// member-declaration: @@ -1697,6 +1752,9 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, if (TagDecl) Actions.ActOnTagStartDefinition(getCurScope(), TagDecl); + ClassVirtSpecifiers CVS; + ParseOptionalCXX0XClassVirtSpecifierSeq(CVS); + if (Tok.is(tok::colon)) { ParseBaseClause(TagDecl); diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index 8fbcc7ba43..4152c5740a 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -669,7 +669,7 @@ void UnqualifiedId::setOperatorFunctionId(SourceLocation OperatorLoc, } } -bool VirtSpecifiers::SetSpecifier(VirtSpecifier VS, SourceLocation Loc, +bool VirtSpecifiers::SetSpecifier(Specifier VS, SourceLocation Loc, const char *&PrevSpec) { if (Specifiers & VS) { PrevSpec = getSpecifierName(VS); @@ -688,7 +688,7 @@ bool VirtSpecifiers::SetSpecifier(VirtSpecifier VS, SourceLocation Loc, return false; } -const char *VirtSpecifiers::getSpecifierName(VirtSpecifier VS) { +const char *VirtSpecifiers::getSpecifierName(Specifier VS) { switch (VS) { default: assert(0 && "Unknown specifier"); case VS_Override: return "override"; @@ -697,8 +697,7 @@ const char *VirtSpecifiers::getSpecifierName(VirtSpecifier VS) { } } -bool ClassVirtSpecifiers::SetSpecifier(ClassVirtSpecifier CVS, - SourceLocation Loc, +bool ClassVirtSpecifiers::SetSpecifier(Specifier CVS, SourceLocation Loc, const char *&PrevSpec) { if (Specifiers & CVS) { PrevSpec = getSpecifierName(CVS); @@ -716,7 +715,7 @@ bool ClassVirtSpecifiers::SetSpecifier(ClassVirtSpecifier CVS, return false; } -const char *ClassVirtSpecifiers::getSpecifierName(ClassVirtSpecifier CVS) { +const char *ClassVirtSpecifiers::getSpecifierName(Specifier CVS) { switch (CVS) { default: assert(0 && "Unknown specifier"); case CVS_Final: return "final"; diff --git a/test/CXX/class/p1-0x.cpp b/test/CXX/class/p1-0x.cpp new file mode 100644 index 0000000000..5851de6cc3 --- /dev/null +++ b/test/CXX/class/p1-0x.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++0x +namespace Test1 { + +class A final { }; +class B explicit { }; +class C final explicit { }; +class D final final { }; // expected-error {{class already marked 'final'}} +class E explicit explicit { }; // expected-error {{class already marked 'explicit'}} + +} -- 2.40.0