This patch adds very basic support for parsing and type-checking class
inheritance in C++. It'll parse the base-specifier list, e.g.,

 class D : public B1, virtual public B2 { };

and do some of the simpler semantic checks (B1 and B2 are classes;
they aren't unions or incomplete types, etc).



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@49623 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 46dcb57..606ff57 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -11,9 +11,10 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Parse/DeclSpec.h"
 #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
@@ -117,3 +118,233 @@
 
   return Actions.ActOnLinkageSpec(Loc, LBrace, RBrace, LangBufPtr, StrSize, D);
 }
+
+/// ParseClassSpecifier - Parse a C++ class-specifier [C++ class] or
+/// elaborated-type-specifier [C++ dcl.type.elab]; we can't tell which
+/// until we reach the start of a definition or see a token that
+/// cannot start a definition.
+///
+///       class-specifier: [C++ class]
+///         class-head '{' member-specification[opt] '}'
+///         class-head '{' member-specification[opt] '}' attributes[opt]
+///       class-head:
+///         class-key identifier[opt] base-clause[opt]
+///         class-key nested-name-specifier identifier base-clause[opt]
+///         class-key nested-name-specifier[opt] simple-template-id
+///                          base-clause[opt]
+/// [GNU]   class-key attributes[opt] identifier[opt] base-clause[opt]
+/// [GNU]   class-key attributes[opt] nested-name-specifier 
+///                          identifier base-clause[opt]
+/// [GNU]   class-key attributes[opt] nested-name-specifier[opt] 
+///                          simple-template-id base-clause[opt]
+///       class-key:
+///         'class'
+///         'struct'
+///         'union'
+///
+///       elaborated-type-specifier: [C++ dcl.type.elab]
+///         class-key ::[opt] nested-name-specifier[opt] identifier 
+///         class-key ::[opt] nested-name-specifier[opt] 'template'[opt] 
+///                          simple-template-id 
+///
+///  Note that the C++ class-specifier and elaborated-type-specifier,
+///  together, subsume the C99 struct-or-union-specifier:
+///
+///       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::ParseClassSpecifier(DeclSpec &DS) {
+  assert((Tok.is(tok::kw_class) || 
+          Tok.is(tok::kw_struct) || 
+          Tok.is(tok::kw_union)) &&
+         "Not a class specifier");
+  DeclSpec::TST TagType =
+    Tok.is(tok::kw_class) ? DeclSpec::TST_class : 
+    Tok.is(tok::kw_struct) ? DeclSpec::TST_struct : 
+    DeclSpec::TST_union;
+
+  SourceLocation StartLoc = ConsumeToken();
+
+  AttributeList *Attr = 0;
+  // If attributes exist after tag, parse them.
+  if (Tok.is(tok::kw___attribute))
+    Attr = ParseAttributes();
+
+  // FIXME: Parse the (optional) nested-name-specifier.
+
+  // Parse the (optional) class name.
+  // FIXME: Alternatively, parse a simple-template-id.
+  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 {...' or
+  // 'struct fo :...' then this is a definition. Otherwise we have
+  // something like 'struct foo xyz', a reference.
+  Action::TagKind TK;
+  if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
+    TK = Action::TK_Definition;
+  else if (Tok.is(tok::semi))
+    TK = Action::TK_Declaration;
+  else
+    TK = Action::TK_Reference;
+
+  if (!Name && TK != Action::TK_Definition) {
+    // We have a declaration or reference to an anonymous class.
+    Diag(StartLoc, diag::err_anon_type_definition, 
+         DeclSpec::getSpecifierName(TagType));
+
+    // Skip the rest of this declarator, up until the comma or semicolon.
+    SkipUntil(tok::comma, true);
+    return;
+  }
+
+  // Parse the tag portion of this.
+  DeclTy *TagDecl = Actions.ActOnTag(CurScope, TagType, TK, StartLoc, Name, 
+                                     NameLoc, Attr);
+
+  // Parse the optional base clause (C++ only).
+  if (getLang().CPlusPlus && Tok.is(tok::colon)) {
+    ParseBaseClause(TagDecl);
+  }
+
+  // If there is a body, parse it and inform the actions module.
+  if (Tok.is(tok::l_brace))
+    ParseStructUnionBody(StartLoc, TagType, TagDecl);
+  else if (TK == Action::TK_Definition) {
+    // FIXME: Complain that we have a base-specifier list but no
+    // definition.
+    Diag(Tok.getLocation(), diag::err_expected_lbrace);
+  }
+
+  const char *PrevSpec = 0;
+  if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, TagDecl))
+    Diag(StartLoc, diag::err_invalid_decl_spec_combination, PrevSpec);
+}
+
+/// ParseBaseClause - Parse the base-clause of a C++ class [C++ class.derived]. 
+///
+///       base-clause : [C++ class.derived]
+///         ':' base-specifier-list
+///       base-specifier-list:
+///         base-specifier '...'[opt]
+///         base-specifier-list ',' base-specifier '...'[opt]
+void Parser::ParseBaseClause(DeclTy *ClassDecl)
+{
+  assert(Tok.is(tok::colon) && "Not a base clause");
+  ConsumeToken();
+
+  while (true) {
+    // Parse a base-specifier.
+    if (ParseBaseSpecifier(ClassDecl)) {
+      // Skip the rest of this base specifier, up until the comma or
+      // opening brace.
+      SkipUntil(tok::comma, tok::l_brace);
+    }
+
+    // If the next token is a comma, consume it and keep reading
+    // base-specifiers.
+    if (Tok.isNot(tok::comma)) break;
+    
+    // Consume the comma.
+    ConsumeToken();
+  }
+}
+
+/// ParseBaseSpecifier - Parse a C++ base-specifier. A base-specifier is
+/// one entry in the base class list of a class specifier, for example:
+///    class foo : public bar, virtual private baz {
+/// 'public bar' and 'virtual private baz' are each base-specifiers.
+///
+///       base-specifier: [C++ class.derived]
+///         ::[opt] nested-name-specifier[opt] class-name
+///         'virtual' access-specifier[opt] ::[opt] nested-name-specifier[opt]
+///                        class-name
+///         access-specifier 'virtual'[opt] ::[opt] nested-name-specifier[opt]
+///                        class-name
+bool Parser::ParseBaseSpecifier(DeclTy *ClassDecl)
+{
+  bool IsVirtual = false;
+  SourceLocation StartLoc = Tok.getLocation();
+
+  // Parse the 'virtual' keyword.
+  if (Tok.is(tok::kw_virtual))  {
+    ConsumeToken();
+    IsVirtual = true;
+  }
+
+  // Parse an (optional) access specifier.
+  AccessSpecifier Access = getAccessSpecifierIfPresent();
+  if (Access)
+    ConsumeToken();
+  
+  // Parse the 'virtual' keyword (again!), in case it came after the
+  // access specifier.
+  if (Tok.is(tok::kw_virtual))  {
+    SourceLocation VirtualLoc = ConsumeToken();
+    if (IsVirtual) {
+      // Complain about duplicate 'virtual'
+      Diag(VirtualLoc, diag::err_dup_virtual);
+    }
+
+    IsVirtual = true;
+  }
+
+  // FIXME: Parse optional '::' and optional nested-name-specifier.
+
+  // Parse the class-name.
+  // FIXME: Alternatively, parse a simple-template-id.
+  if (Tok.isNot(tok::identifier)) {
+    Diag(Tok.getLocation(), diag::err_expected_class_name);
+    return true;
+  }
+
+  // We have an identifier; check whether it is actually a type.
+  DeclTy *BaseType = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope);
+  if (!BaseType) {
+    Diag(Tok.getLocation(), diag::err_expected_class_name);
+    return true;
+  }
+
+  // The location of the base class itself.
+  SourceLocation BaseLoc = Tok.getLocation();
+  
+  // Find the complete source range for the base-specifier.  
+  SourceRange Range(StartLoc, BaseLoc);
+  
+  // Consume the identifier token (finally!).
+  ConsumeToken();
+  
+  // Notify semantic analysis that we have parsed a complete
+  // base-specifier.
+  Actions.ActOnBaseSpecifier(ClassDecl, Range, IsVirtual, Access, BaseType,
+                             BaseLoc);
+  return false;
+}
+
+/// getAccessSpecifierIfPresent - Determine whether the next token is
+/// a C++ access-specifier.
+///
+///       access-specifier: [C++ class.derived]
+///         'private'
+///         'protected'
+///         'public'
+AccessSpecifier Parser::getAccessSpecifierIfPresent()
+{
+  switch (Tok.getKind()) {
+  default: return AS_none;
+  case tok::kw_private: return AS_private;
+  case tok::kw_protected: return AS_protected;
+  case tok::kw_public: return AS_public;
+  }
+}