Support templateids in friend declarations.  Fixes bug 4859.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81233 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index c37ff10..1c7157c 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -3917,61 +3917,60 @@
   assert(DS.isFriendSpecified());
   assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
 
-  // Check to see if the decl spec was syntactically like "struct foo".
-  RecordDecl *RD = NULL;
-
-  switch (DS.getTypeSpecType()) {
-  case DeclSpec::TST_class:
-  case DeclSpec::TST_struct:
-  case DeclSpec::TST_union:
-    RD = dyn_cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep());
-    if (!RD) return DeclPtrTy();
-
-    // The parser doesn't quite handle
-    //   friend class A {}
-    // as we'd like, because it might have been the (valid) prefix of
-    //   friend class A {} foo();
-    // So even in C++0x mode we don't want to 
-    IsDefinition |= RD->isDefinition();
-    break;
-
-  default: break;
-  }
-
-  FriendDecl::FriendUnion FU = RD;
+  // Try to convert the decl specifier to a type.
+  bool invalid = false;
+  QualType T = ConvertDeclSpecToType(DS, Loc, invalid);
+  if (invalid) return DeclPtrTy();
 
   // C++ [class.friend]p2:
   //   An elaborated-type-specifier shall be used in a friend declaration
   //   for a class.*
   //   * The class-key of the elaborated-type-specifier is required.
-  // So if we didn't get a record decl above, we're invalid in C++98 mode.
-  if (!RD) {
-    bool invalid = false;
-    QualType T = ConvertDeclSpecToType(DS, Loc, invalid);
-    if (invalid) return DeclPtrTy();
-    
+  // This is one of the rare places in Clang where it's legitimate to
+  // ask about the "spelling" of the type.
+  if (!getLangOptions().CPlusPlus0x && !isa<ElaboratedType>(T)) {
+    // If we evaluated the type to a record type, suggest putting
+    // a tag in front.
     if (const RecordType *RT = T->getAs<RecordType>()) {
-      FU = RD = cast<CXXRecordDecl>(RT->getDecl());
-      
-      // Untagged typenames are invalid prior to C++0x, but we can
-      // suggest an easy fix which should work.
-      if (!getLangOptions().CPlusPlus0x) {
-        Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type)
-          << (RD->isUnion())
-          << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
-                                        RD->isUnion() ? " union" : " class");
-        return DeclPtrTy();
-      }
-    }else if (!getLangOptions().CPlusPlus0x) {
-      Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
-          << DS.getSourceRange();
+      RecordDecl *RD = RT->getDecl();
+
+      std::string InsertionText = std::string(" ") + RD->getKindName();
+
+      Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type)
+        << (RD->isUnion())
+        << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
+                                                 InsertionText);
       return DeclPtrTy();
     }else {
-      FU = T.getTypePtr();
+      Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
+          << DS.getSourceRange();
+      return DeclPtrTy();      
     }
   }
 
-  assert(FU && "should have a friend decl/type by here!");
+  FriendDecl::FriendUnion FU = T.getTypePtr();
+
+  // The parser doesn't quite handle
+  //   friend class A { ... }
+  // optimally, because it might have been the (valid) prefix of
+  //   friend class A { ... } foo();
+  // So in a very particular set of circumstances, we need to adjust
+  // IsDefinition.
+  //
+  // Also, if we made a RecordDecl in ActOnTag, we want that to be the
+  // object of our friend declaration.
+  switch (DS.getTypeSpecType()) {
+  default: break;
+  case DeclSpec::TST_class:
+  case DeclSpec::TST_struct:
+  case DeclSpec::TST_union:
+    CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep());
+    if (RD) {
+      IsDefinition |= RD->isDefinition();
+      FU = RD;
+    }
+    break;
+  }
 
   // C++ [class.friend]p2: A class shall not be defined inside
   //   a friend declaration.
@@ -3986,11 +3985,10 @@
   // But that's a silly restriction which nobody implements for
   // inner classes, and C++0x removes it anyway, so we only report
   // this (as a warning) if we're being pedantic.
-  if (!getLangOptions().CPlusPlus0x) {
-    assert(RD && "must have a record decl in C++98 mode");
-    if (RD->getDeclContext() == CurContext)
-      Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class);
-  }
+  if (!getLangOptions().CPlusPlus0x)
+    if (const RecordType *RT = T->getAs<RecordType>())
+      if (RT->getDecl()->getDeclContext() == CurContext)
+        Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class);
 
   FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU,
                                       DS.getFriendSpecLoc());