Add parsing support for C++ classes.

Note that Parser::ParseCXXMemberSpecification is temporarily disabled until the Sema support is in place.
Once ParseCXXMemberSpecification is enabled, the Parser/cxx-class.cpp test will pass.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@52694 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 0a20911..718cea9 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -229,7 +229,12 @@
 
   // If there is a body, parse it and inform the actions module.
   if (Tok.is(tok::l_brace))
-    ParseStructUnionBody(StartLoc, TagType, TagDecl);
+    // FIXME: Temporarily disable parsing for C++ classes until the Sema support
+    // is in place.
+    //if (getLang().CPlusPlus)
+    //  ParseCXXMemberSpecification(StartLoc, TagType, TagDecl);
+    //else
+      ParseStructUnionBody(StartLoc, TagType, TagDecl);
   else if (TK == Action::TK_Definition) {
     // FIXME: Complain that we have a base-specifier list but no
     // definition.
@@ -357,3 +362,263 @@
   case tok::kw_public: return AS_public;
   }
 }
+
+/// ParseCXXClassMemberDeclaration - Parse a C++ class member declaration.
+///
+///       member-declaration:
+///         decl-specifier-seq[opt] member-declarator-list[opt] ';'
+///         function-definition ';'[opt]
+///         ::[opt] nested-name-specifier template[opt] unqualified-id ';'[TODO]
+///         using-declaration                                            [TODO]
+/// [C++0x] static_assert-declaration                                    [TODO]
+///         template-declaration                                         [TODO]
+///
+///       member-declarator-list:
+///         member-declarator
+///         member-declarator-list ',' member-declarator
+///
+///       member-declarator:
+///         declarator pure-specifier[opt]
+///         declarator constant-initializer[opt]
+///         identifier[opt] ':' constant-expression
+///
+///       pure-specifier:   [TODO]
+///         '= 0'
+///
+///       constant-initializer:
+///         '=' constant-expression
+///
+Parser::DeclTy *Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) {
+  SourceLocation DSStart = Tok.getLocation();
+  // decl-specifier-seq:
+  // Parse the common declaration-specifiers piece.
+  DeclSpec DS;
+  ParseDeclarationSpecifiers(DS);
+
+  if (Tok.is(tok::semi)) {
+    ConsumeToken();
+    // C++ 9.2p7: The member-declarator-list can be omitted only after a
+    // class-specifier or an enum-specifier or in a friend declaration.
+    // FIXME: Friend declarations.
+    switch (DS.getTypeSpecType()) {
+      case DeclSpec::TST_struct:
+      case DeclSpec::TST_union:
+      case DeclSpec::TST_class:
+      case DeclSpec::TST_enum:
+        return Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
+      default:
+        Diag(DSStart, diag::err_no_declarators);
+        return 0;
+    }
+  }
+  
+  // Parse the first declarator.
+  Declarator DeclaratorInfo(DS, Declarator::MemberContext);
+  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;
+  }
+
+  // function-definition:
+  if (Tok.is(tok::l_brace)) {
+    if (!DeclaratorInfo.isFunctionDeclarator()) {
+      Diag(Tok, diag::err_func_def_no_params);
+      ConsumeBrace();
+      SkipUntil(tok::r_brace, true);
+      return 0;
+    }
+
+    if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
+      Diag(Tok, diag::err_function_declared_typedef);
+      // This recovery skips the entire function body. It would be nice
+      // to simply call ParseCXXInlineMethodDef() below, however Sema
+      // assumes the declarator represents a function, not a typedef.
+      ConsumeBrace();
+      SkipUntil(tok::r_brace, true);
+      return 0;
+    }
+
+    return ParseCXXInlineMethodDef(AS, DeclaratorInfo);
+  }
+
+  // member-declarator-list:
+  //   member-declarator
+  //   member-declarator-list ',' member-declarator
+
+  DeclTy *LastDeclInGroup = 0;
+  ExprTy *BitfieldSize = 0;
+  ExprTy *Init = 0;
+
+  while (1) {
+
+    // member-declarator:
+    //   declarator pure-specifier[opt]
+    //   declarator constant-initializer[opt]
+    //   identifier[opt] ':' constant-expression
+
+    if (Tok.is(tok::colon)) {
+      ConsumeToken();
+      ExprResult Res = ParseConstantExpression();
+      if (Res.isInvalid)
+        SkipUntil(tok::comma, true, true);
+      else
+        BitfieldSize = Res.Val;
+    }
+    
+    // pure-specifier:
+    //   '= 0'
+    //
+    // constant-initializer:
+    //   '=' constant-expression
+
+    if (Tok.is(tok::equal)) {
+      ConsumeToken();
+      ExprResult Res = ParseInitializer();
+      if (Res.isInvalid)
+        SkipUntil(tok::comma, true, true);
+      else
+        Init = Res.Val;
+    }
+
+    // If attributes exist after the declarator, parse them.
+    if (Tok.is(tok::kw___attribute))
+      DeclaratorInfo.AddAttributes(ParseAttributes());
+
+    LastDeclInGroup = Actions.ActOnCXXMemberDeclarator(CurScope, AS,
+                                                       DeclaratorInfo,
+                                                       BitfieldSize, Init,
+                                                       LastDeclInGroup);
+
+    // 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.
+    DeclaratorInfo.clear();
+    BitfieldSize = Init = 0;
+    
+    // Attributes are only allowed on the second declarator.
+    if (Tok.is(tok::kw___attribute))
+      DeclaratorInfo.AddAttributes(ParseAttributes());
+
+    ParseDeclarator(DeclaratorInfo);
+  }
+
+  if (Tok.is(tok::semi)) {
+    ConsumeToken();
+    // Reverse the chain list.
+    return Actions.FinalizeDeclaratorGroup(CurScope, LastDeclInGroup);
+  }
+
+  Diag(Tok, diag::err_expected_semi_decl_list);
+  // Skip to end of block or statement
+  SkipUntil(tok::r_brace, true, true);
+  if (Tok.is(tok::semi))
+    ConsumeToken();
+  return 0;
+}
+
+/// ParseCXXMemberSpecification - Parse the class definition.
+///
+///       member-specification:
+///         member-declaration member-specification[opt]
+///         access-specifier ':' member-specification[opt]
+///
+void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
+                                         unsigned TagType, DeclTy *TagDecl) {
+  assert(TagType == DeclSpec::TST_struct ||
+         TagType == DeclSpec::TST_union  ||
+         TagType == DeclSpec::TST_class && "Invalid TagType!");
+
+  SourceLocation LBraceLoc = ConsumeBrace();
+
+  if (!CurScope->isCXXClassScope() && // Not about to define a nested class.
+      CurScope->isInCXXInlineMethodScope()) {
+    // We will define a local class of an inline method.
+    // Push a new LexedMethodsForTopClass for its inline methods.
+    PushTopClassStack();
+  }
+
+  // Enter a scope for the class.
+  EnterScope(Scope::CXXClassScope|Scope::DeclScope);
+
+  Actions.ActOnStartCXXClassDef(CurScope, TagDecl, LBraceLoc);
+
+  // C++ 11p3: Members of a class defined with the keyword class are private
+  // by default. Members of a class defined with the keywords struct or union
+  // are public by default.
+  AccessSpecifier CurAS;
+  if (TagType == DeclSpec::TST_class)
+    CurAS = AS_private;
+  else
+    CurAS = AS_public;
+
+  // While we still have something to read, read the member-declarations.
+  while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
+    // Each iteration of this loop reads one member-declaration.
+    
+    // Check for extraneous top-level semicolon.
+    if (Tok.is(tok::semi)) {
+      Diag(Tok, diag::ext_extra_struct_semi);
+      ConsumeToken();
+      continue;
+    }
+
+    AccessSpecifier AS = getAccessSpecifierIfPresent();
+    if (AS != AS_none) {
+      // Current token is a C++ access specifier.
+      CurAS = AS;
+      ConsumeToken();
+      ExpectAndConsume(tok::colon, diag::err_expected_colon);
+      continue;
+    }
+
+    // Parse all the comma separated declarators.
+    ParseCXXClassMemberDeclaration(CurAS);
+  }
+  
+  SourceLocation RBraceLoc = MatchRHSPunctuation(tok::r_brace, LBraceLoc);
+  
+  AttributeList *AttrList = 0;
+  // If attributes exist after class contents, parse them.
+  if (Tok.is(tok::kw___attribute))
+    AttrList = ParseAttributes(); // FIXME: where should I put them?
+
+  Actions.ActOnFinishCXXMemberSpecification(CurScope, RecordLoc, TagDecl,
+                                            LBraceLoc, RBraceLoc);
+
+  // C++ 9.2p2: Within the class member-specification, the class is regarded as
+  // complete within function bodies, default arguments,
+  // exception-specifications, and constructor ctor-initializers (including
+  // such things in nested classes).
+  //
+  // FIXME: Only function bodies are parsed correctly, fix the rest.
+  if (!CurScope->getParent()->isCXXClassScope()) {
+    // We are not inside a nested class. This class and its nested classes
+    // are complete and we can parse the lexed inline method definitions.
+    ParseLexedMethodDefs();
+
+    // For a local class of inline method, pop the LexedMethodsForTopClass that
+    // was previously pushed.
+
+    assert(CurScope->isInCXXInlineMethodScope() ||
+           TopClassStacks.size() == 1    &&
+           "MethodLexers not getting popped properly!");
+    if (CurScope->isInCXXInlineMethodScope())
+      PopTopClassStack();
+  }
+
+  // Leave the class scope.
+  ExitScope();
+
+  Actions.ActOnFinishCXXClassDef(TagDecl, RBraceLoc);
+}