Introduce a new class, UnqualifiedId, that provides a parsed
representation of a C++ unqualified-id, along with a single parsing
function (Parser::ParseUnqualifiedId) that will parse all of the
various forms of unqualified-id in C++.

Replace the representation of the declarator name in Declarator with
the new UnqualifiedId class, simplifying declarator-id parsing
considerably and providing more source-location information to
Sema. In the future, I hope to migrate all of the other
unqualified-id-parsing code over to this single representation, then
begin to merge actions that are currently only different because we
didn't have a unqualified notion of the name in the parser.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85851 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Parse/DeclSpec.cpp b/lib/Parse/DeclSpec.cpp
index b8422aa..3436900 100644
--- a/lib/Parse/DeclSpec.cpp
+++ b/lib/Parse/DeclSpec.cpp
@@ -446,3 +446,28 @@
        || tst == TST_enum
           ) && getTypeRep() != 0 && StorageClassSpec != DeclSpec::SCS_typedef;
 }
+
+void UnqualifiedId::clear() {
+  if (Kind == IK_TemplateId)
+    TemplateId->Destroy();
+  
+  Kind = IK_Identifier;
+  Identifier = 0;
+  StartLocation = SourceLocation();
+  EndLocation = SourceLocation();
+}
+
+void UnqualifiedId::setOperatorFunctionId(SourceLocation OperatorLoc, 
+                                          OverloadedOperatorKind Op,
+                                          SourceLocation SymbolLocations[3]) {
+  Kind = IK_OperatorFunctionId;
+  StartLocation = OperatorLoc;
+  EndLocation = OperatorLoc;
+  OperatorFunctionId.Operator = Op;
+  for (unsigned I = 0; I != 3; ++I) {
+    OperatorFunctionId.SymbolLocations[I] = SymbolLocations[I].getRawEncoding();
+    
+    if (SymbolLocations[I].isValid())
+      EndLocation = SymbolLocations[I];
+  }
+}
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index b56c331..55970c0 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -2270,97 +2270,46 @@
 void Parser::ParseDirectDeclarator(Declarator &D) {
   DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec());
 
-  if (getLang().CPlusPlus) {
-    if (D.mayHaveIdentifier()) {
-      // ParseDeclaratorInternal might already have parsed the scope.
-      bool afterCXXScope = D.getCXXScopeSpec().isSet() ||
-        ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), /*ObjectType=*/0,
-                                       true);
-      if (afterCXXScope) {
-        // Change the declaration context for name lookup, until this function
-        // is exited (and the declarator has been parsed).
-        DeclScopeObj.EnterDeclaratorScope();
-      }
-
-      if (Tok.is(tok::identifier)) {
-        assert(Tok.getIdentifierInfo() && "Not an identifier?");
-
-        // If this identifier is the name of the current class, it's a
-        // constructor name.
-        if (!D.getDeclSpec().hasTypeSpecifier() &&
-            Actions.isCurrentClassName(*Tok.getIdentifierInfo(),CurScope)) {
-          CXXScopeSpec *SS = afterCXXScope? &D.getCXXScopeSpec() : 0;
-          D.setConstructor(Actions.getTypeName(*Tok.getIdentifierInfo(),
-                                               Tok.getLocation(), CurScope, SS),
-                           Tok.getLocation());
-        // This is a normal identifier.
-        } else
-          D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
-        ConsumeToken();
-        goto PastIdentifier;
-      } else if (Tok.is(tok::annot_template_id)) {
-        TemplateIdAnnotation *TemplateId
-          = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
-
-        D.setTemplateId(TemplateId);
-        ConsumeToken();
-        goto PastIdentifier;
-      } else if (Tok.is(tok::kw_operator)) {
-        SourceLocation OperatorLoc = Tok.getLocation();
-        SourceLocation EndLoc;
-
-        // First try the name of an overloaded operator
-        if (OverloadedOperatorKind Op = TryParseOperatorFunctionId(&EndLoc)) {
-          D.setOverloadedOperator(Op, OperatorLoc, EndLoc);
-        } else {
-          // This must be a conversion function (C++ [class.conv.fct]).
-          if (TypeTy *ConvType = ParseConversionFunctionId(&EndLoc))
-            D.setConversionFunction(ConvType, OperatorLoc, EndLoc);
-          else {
-            D.SetIdentifier(0, Tok.getLocation());
-          }
-        }
-        goto PastIdentifier;
-      } else if (Tok.is(tok::tilde)) {
-        // This should be a C++ destructor.
-        SourceLocation TildeLoc = ConsumeToken();
-        if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id)) {
-          // FIXME: Inaccurate.
-          SourceLocation NameLoc = Tok.getLocation();
-          SourceLocation EndLoc;
-          CXXScopeSpec *SS = afterCXXScope? &D.getCXXScopeSpec() : 0;
-          TypeResult Type = ParseClassName(EndLoc, SS, true);
-          if (Type.isInvalid())
-            D.SetIdentifier(0, TildeLoc);
-          else
-            D.setDestructor(Type.get(), TildeLoc, NameLoc);
-        } else {
-          Diag(Tok, diag::err_destructor_class_name);
-          D.SetIdentifier(0, TildeLoc);
-        }
-        goto PastIdentifier;
-      }
-
-      // If we reached this point, token is not identifier and not '~'.
-
-      if (afterCXXScope) {
-        Diag(Tok, diag::err_expected_unqualified_id);
+  if (getLang().CPlusPlus && D.mayHaveIdentifier()) {
+    // ParseDeclaratorInternal might already have parsed the scope.
+    bool afterCXXScope = D.getCXXScopeSpec().isSet() ||
+      ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), /*ObjectType=*/0,
+                                     true);
+    if (afterCXXScope) {
+      // Change the declaration context for name lookup, until this function
+      // is exited (and the declarator has been parsed).
+      DeclScopeObj.EnterDeclaratorScope();
+    } 
+    
+    if (Tok.is(tok::identifier) || Tok.is(tok::kw_operator) ||
+        Tok.is(tok::annot_template_id) || Tok.is(tok::tilde)) {
+      // We found something that indicates the start of an unqualified-id.
+      // Parse that unqualified-id.
+      if (ParseUnqualifiedId(D.getCXXScopeSpec(), 
+                             /*EnteringContext=*/true, 
+                             /*AllowDestructorName=*/true, 
+                   /*AllowConstructorName=*/!D.getDeclSpec().hasTypeSpecifier(), 
+                             D.getName())) {
         D.SetIdentifier(0, Tok.getLocation());
         D.setInvalidType(true);
-        goto PastIdentifier;
+      } else {
+        // Parsed the unqualified-id; update range information and move along.
+        if (D.getSourceRange().getBegin().isInvalid())
+          D.SetRangeBegin(D.getName().getSourceRange().getBegin());
+        D.SetRangeEnd(D.getName().getSourceRange().getEnd());
       }
+      goto PastIdentifier;
     }
-  }
-
-  // If we reached this point, we are either in C/ObjC or the token didn't
-  // satisfy any of the C++-specific checks.
-  if (Tok.is(tok::identifier) && D.mayHaveIdentifier()) {
+  } else if (Tok.is(tok::identifier) && D.mayHaveIdentifier()) {
     assert(!getLang().CPlusPlus &&
            "There's a C++-specific check for tok::identifier above");
     assert(Tok.getIdentifierInfo() && "Not an identifier?");
     D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
     ConsumeToken();
-  } else if (Tok.is(tok::l_paren)) {
+    goto PastIdentifier;
+  }
+    
+  if (Tok.is(tok::l_paren)) {
     // direct-declarator: '(' declarator ')'
     // direct-declarator: '(' attributes declarator ')'
     // Example: 'char (*X)'   or 'int (*XX)(void)'
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 65265af..a8e1276 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -323,6 +323,7 @@
       SkipUntil(tok::semi);
       return DeclPtrTy();
     }
+    // FIXME: what about conversion functions?
   } else if (Tok.is(tok::identifier)) {
     // Parse identifier.
     TargetName = Tok.getIdentifierInfo();
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index fa65156..dc79741 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -14,6 +14,8 @@
 #include "clang/Parse/ParseDiagnostic.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Parse/DeclSpec.h"
+#include "llvm/Support/ErrorHandling.h"
+
 using namespace clang;
 
 /// \brief Parse global scope or nested-name-specifier if present.
@@ -761,6 +763,417 @@
   return false;
 }
 
+/// \brief Finish parsing a C++ unqualified-id that is a template-id of
+/// some form. 
+///
+/// This routine is invoked when a '<' is encountered after an identifier or
+/// operator-function-id is parsed by \c ParseUnqualifiedId() to determine
+/// whether the unqualified-id is actually a template-id. This routine will
+/// then parse the template arguments and form the appropriate template-id to
+/// return to the caller.
+///
+/// \param SS the nested-name-specifier that precedes this template-id, if
+/// we're actually parsing a qualified-id.
+///
+/// \param Name for constructor and destructor names, this is the actual
+/// identifier that may be a template-name.
+///
+/// \param NameLoc the location of the class-name in a constructor or 
+/// destructor.
+///
+/// \param EnteringContext whether we're entering the scope of the 
+/// nested-name-specifier.
+///
+/// \param Id as input, describes the template-name or operator-function-id
+/// that precedes the '<'. If template arguments were parsed successfully,
+/// will be updated with the template-id.
+/// 
+/// \returns true if a parse error occurred, false otherwise.
+bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
+                                          IdentifierInfo *Name,
+                                          SourceLocation NameLoc,
+                                          bool EnteringContext,
+                                          UnqualifiedId &Id) {
+  assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");
+  
+  TemplateTy Template;
+  TemplateNameKind TNK = TNK_Non_template;
+  switch (Id.getKind()) {
+  case UnqualifiedId::IK_Identifier:
+    TNK = Actions.isTemplateName(CurScope, *Id.Identifier, Id.StartLocation, 
+                                 &SS, /*ObjectType=*/0, EnteringContext,
+                                 Template);
+    break;
+      
+  case UnqualifiedId::IK_OperatorFunctionId: {
+    // FIXME: Temporary hack: warn that we are completely ignoring the 
+    // template arguments for now.
+    // Parse the enclosed template argument list.
+    SourceLocation LAngleLoc, RAngleLoc;
+    TemplateArgList TemplateArgs;
+    TemplateArgIsTypeList TemplateArgIsType;
+    TemplateArgLocationList TemplateArgLocations;
+    if (ParseTemplateIdAfterTemplateName(Template, Id.StartLocation,
+                                         &SS, true, LAngleLoc,
+                                         TemplateArgs,
+                                         TemplateArgIsType,
+                                         TemplateArgLocations,
+                                         RAngleLoc))
+      return true;
+    
+    Diag(Id.StartLocation, diag::warn_operator_template_id_ignores_args)
+      << SourceRange(LAngleLoc, RAngleLoc);
+    break;
+  }
+      
+  case UnqualifiedId::IK_ConstructorName:
+    TNK = Actions.isTemplateName(CurScope, *Name, NameLoc, 
+                                 &SS, /*ObjectType=*/0, EnteringContext,
+                                 Template);
+    break;
+      
+  case UnqualifiedId::IK_DestructorName:
+    TNK = Actions.isTemplateName(CurScope, *Name, NameLoc,
+                                 &SS, /*ObjectType=*/0, EnteringContext,
+                                 Template);
+    break;
+      
+  default:
+    return false;
+  }
+  
+  if (TNK == TNK_Non_template)
+    return false;
+  
+  // Parse the enclosed template argument list.
+  SourceLocation LAngleLoc, RAngleLoc;
+  TemplateArgList TemplateArgs;
+  TemplateArgIsTypeList TemplateArgIsType;
+  TemplateArgLocationList TemplateArgLocations;
+  if (ParseTemplateIdAfterTemplateName(Template, Id.StartLocation,
+                                       &SS, true, LAngleLoc,
+                                       TemplateArgs,
+                                       TemplateArgIsType,
+                                       TemplateArgLocations,
+                                       RAngleLoc))
+    return true;
+  
+  if (Id.getKind() == UnqualifiedId::IK_Identifier ||
+      Id.getKind() == UnqualifiedId::IK_OperatorFunctionId) {
+    // Form a parsed representation of the template-id to be stored in the
+    // UnqualifiedId.
+    TemplateIdAnnotation *TemplateId
+      = TemplateIdAnnotation::Allocate(TemplateArgs.size());
+
+    if (Id.getKind() == UnqualifiedId::IK_Identifier) {
+      TemplateId->Name = Id.Identifier;
+      TemplateId->TemplateNameLoc = Id.StartLocation;
+    } else {
+      // FIXME: Handle IK_OperatorFunctionId
+    }
+
+    TemplateId->Template = Template.getAs<void*>();
+    TemplateId->Kind = TNK;
+    TemplateId->LAngleLoc = LAngleLoc;
+    TemplateId->RAngleLoc = RAngleLoc;
+    void **Args = TemplateId->getTemplateArgs();
+    bool *ArgIsType = TemplateId->getTemplateArgIsType();
+    SourceLocation *ArgLocs = TemplateId->getTemplateArgLocations();
+    for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); 
+         Arg != ArgEnd; ++Arg) {
+      Args[Arg] = TemplateArgs[Arg];
+      ArgIsType[Arg] = TemplateArgIsType[Arg];
+      ArgLocs[Arg] = TemplateArgLocations[Arg];
+    }
+    
+    Id.setTemplateId(TemplateId);
+    return false;
+  }
+
+  // Bundle the template arguments together.
+  ASTTemplateArgsPtr TemplateArgsPtr(Actions, TemplateArgs.data(),
+                                     TemplateArgIsType.data(),
+                                     TemplateArgs.size());
+  
+  // Constructor and destructor names.
+  Action::TypeResult Type
+    = Actions.ActOnTemplateIdType(Template, NameLoc,
+                                  LAngleLoc, TemplateArgsPtr,
+                                  &TemplateArgLocations[0],
+                                  RAngleLoc);
+  if (Type.isInvalid())
+    return true;
+  
+  if (Id.getKind() == UnqualifiedId::IK_ConstructorName)
+    Id.setConstructorName(Type.get(), NameLoc, RAngleLoc);
+  else
+    Id.setDestructorName(Id.StartLocation, Type.get(), RAngleLoc);
+  
+  return false;
+}
+
+/// \brief Parse a C++ unqualified-id (or a C identifier), which describes the
+/// name of an entity.
+///
+/// \code
+///       unqualified-id: [C++ expr.prim.general]
+///         identifier
+///         operator-function-id
+///         conversion-function-id
+/// [C++0x] literal-operator-id [TODO]
+///         ~ class-name
+///         template-id
+///
+///       operator-function-id: [C++ 13.5]
+///         'operator' operator
+///
+/// operator: one of
+///            new   delete  new[]   delete[]
+///            +     -    *  /    %  ^    &   |   ~
+///            !     =    <  >    += -=   *=  /=  %=
+///            ^=    &=   |= <<   >> >>= <<=  ==  !=
+///            <=    >=   && ||   ++ --   ,   ->* ->
+///            ()    []
+///
+///       conversion-function-id: [C++ 12.3.2]
+///         operator conversion-type-id
+///
+///       conversion-type-id:
+///         type-specifier-seq conversion-declarator[opt]
+///
+///       conversion-declarator:
+///         ptr-operator conversion-declarator[opt]
+/// \endcode
+///
+/// \param The nested-name-specifier that preceded this unqualified-id. If
+/// non-empty, then we are parsing the unqualified-id of a qualified-id.
+///
+/// \param EnteringContext whether we are entering the scope of the 
+/// nested-name-specifier.
+///
+/// \param AllowDestructorName whether we allow parsing of a destructor name.
+///
+/// \param AllowConstructorName whether we allow parsing a constructor name.
+///
+/// \param Result on a successful parse, contains the parsed unqualified-id.
+///
+/// \returns true if parsing fails, false otherwise.
+bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
+                                bool AllowDestructorName,
+                                bool AllowConstructorName,
+                                UnqualifiedId &Result) {
+  // unqualified-id:
+  //   identifier
+  //   template-id (when it hasn't already been annotated)
+  if (Tok.is(tok::identifier)) {
+    // Consume the identifier.
+    IdentifierInfo *Id = Tok.getIdentifierInfo();
+    SourceLocation IdLoc = ConsumeToken();
+
+    if (AllowConstructorName && 
+        Actions.isCurrentClassName(*Id, CurScope, &SS)) {
+      // We have parsed a constructor name.
+      Result.setConstructorName(Actions.getTypeName(*Id, IdLoc, CurScope,
+                                                    &SS, false),
+                                IdLoc, IdLoc);
+    } else {
+      // We have parsed an identifier.
+      Result.setIdentifier(Id, IdLoc);      
+    }
+
+    // If the next token is a '<', we may have a template.
+    if (Tok.is(tok::less))
+      return ParseUnqualifiedIdTemplateId(SS, Id, IdLoc, EnteringContext, 
+                                          Result);
+    
+    return false;
+  }
+  
+  // unqualified-id:
+  //   template-id (already parsed and annotated)
+  if (Tok.is(tok::annot_template_id)) {
+    // FIXME: Could this be a constructor name???
+    
+    // We have already parsed a template-id; consume the annotation token as
+    // our unqualified-id.
+    Result.setTemplateId(
+                  static_cast<TemplateIdAnnotation*>(Tok.getAnnotationValue()));
+    ConsumeToken();
+    return false;
+  }
+  
+  // unqualified-id:
+  //   operator-function-id
+  //   conversion-function-id
+  if (Tok.is(tok::kw_operator)) {
+    // Consume the 'operator' keyword.
+    SourceLocation KeywordLoc = ConsumeToken();
+
+    // Determine what kind of operator name we have.
+    unsigned SymbolIdx = 0;
+    SourceLocation SymbolLocations[3];
+    OverloadedOperatorKind Op = OO_None;
+    switch (Tok.getKind()) {
+      case tok::kw_new:
+      case tok::kw_delete: {
+        bool isNew = Tok.getKind() == tok::kw_new;
+        // Consume the 'new' or 'delete'.
+        SymbolLocations[SymbolIdx++] = ConsumeToken();
+        if (Tok.is(tok::l_square)) {
+          // Consume the '['.
+          SourceLocation LBracketLoc = ConsumeBracket();
+          // Consume the ']'.
+          SourceLocation RBracketLoc = MatchRHSPunctuation(tok::r_square,
+                                                           LBracketLoc);
+          if (RBracketLoc.isInvalid())
+            return true;
+          
+          SymbolLocations[SymbolIdx++] = LBracketLoc;
+          SymbolLocations[SymbolIdx++] = RBracketLoc;
+          Op = isNew? OO_Array_New : OO_Array_Delete;
+        } else {
+          Op = isNew? OO_New : OO_Delete;
+        }
+        break;
+      }
+        
+  #define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \
+      case tok::Token:                                                     \
+        SymbolLocations[SymbolIdx++] = ConsumeToken();                     \
+        Op = OO_##Name;                                                    \
+        break;
+  #define OVERLOADED_OPERATOR_MULTI(Name,Spelling,Unary,Binary,MemberOnly)
+  #include "clang/Basic/OperatorKinds.def"
+        
+      case tok::l_paren: {
+        // Consume the '('.
+        SourceLocation LParenLoc = ConsumeParen();
+        // Consume the ')'.
+        SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren,
+                                                       LParenLoc);
+        if (RParenLoc.isInvalid())
+          return true;
+
+        SymbolLocations[SymbolIdx++] = LParenLoc;
+        SymbolLocations[SymbolIdx++] = RParenLoc;
+        Op = OO_Call;
+        break;
+      }
+        
+      case tok::l_square: {
+        // Consume the '['.
+        SourceLocation LBracketLoc = ConsumeBracket();
+        // Consume the ']'.
+        SourceLocation RBracketLoc = MatchRHSPunctuation(tok::r_square,
+                                                         LBracketLoc);
+        if (RBracketLoc.isInvalid())
+          return true;
+        
+        SymbolLocations[SymbolIdx++] = LBracketLoc;
+        SymbolLocations[SymbolIdx++] = RBracketLoc;
+        Op = OO_Subscript;
+        break;
+      }
+        
+      case tok::code_completion: {
+        // Code completion for the operator name.
+        Actions.CodeCompleteOperatorName(CurScope);
+        
+        // Consume the operator token.
+        ConsumeToken();
+        
+        // Don't try to parse any further.
+        return true;
+      }
+
+      default:
+        break;
+    }
+    
+    if (Op != OO_None) {
+      // We have parsed an operator-function-id.
+      Result.setOperatorFunctionId(KeywordLoc, Op, SymbolLocations);
+      
+      // If the next token is a '<', we may have a template.
+      if (Tok.is(tok::less))
+        return ParseUnqualifiedIdTemplateId(SS, 0, SourceLocation(), 
+                                            EnteringContext, Result);
+
+      return false;
+    }
+    
+    // Parse a conversion-function-id.
+    //
+    //   conversion-function-id: [C++ 12.3.2]
+    //     operator conversion-type-id
+    //
+    //   conversion-type-id:
+    //     type-specifier-seq conversion-declarator[opt]
+    //
+    //   conversion-declarator:
+    //     ptr-operator conversion-declarator[opt]
+    
+    // Parse the type-specifier-seq.
+    DeclSpec DS;
+    if (ParseCXXTypeSpecifierSeq(DS))
+      return true;
+    
+    // Parse the conversion-declarator, which is merely a sequence of
+    // ptr-operators.
+    Declarator D(DS, Declarator::TypeNameContext);
+    ParseDeclaratorInternal(D, /*DirectDeclParser=*/0);
+    
+    // Finish up the type.
+    Action::TypeResult Ty = Actions.ActOnTypeName(CurScope, D);
+    if (Ty.isInvalid())
+      return true;
+    
+    // Note that this is a conversion-function-id.
+    Result.setConversionFunctionId(KeywordLoc, Ty.get(), 
+                                   D.getSourceRange().getEnd());
+    return false;
+  }
+  
+  if ((AllowDestructorName || SS.isSet()) && Tok.is(tok::tilde)) {
+    // C++ [expr.unary.op]p10:
+    //   There is an ambiguity in the unary-expression ~X(), where X is a 
+    //   class-name. The ambiguity is resolved in favor of treating ~ as a 
+    //    unary complement rather than treating ~X as referring to a destructor.
+    
+    // Parse the '~'.
+    SourceLocation TildeLoc = ConsumeToken();
+    
+    // Parse the class-name.
+    if (Tok.isNot(tok::identifier)) {
+      Diag(Tok, diag::err_destructor_class_name);
+      return true;
+    }
+
+    // Parse the class-name (or template-name in a simple-template-id).
+    IdentifierInfo *ClassName = Tok.getIdentifierInfo();
+    SourceLocation ClassNameLoc = ConsumeToken();
+    
+    // Note that this is a destructor name.
+    Action::TypeTy *Ty = Actions.getTypeName(*ClassName, ClassNameLoc,
+                                             CurScope, &SS);
+    if (!Ty) {
+      Diag(ClassNameLoc, diag::err_destructor_class_name);
+      return true;
+    }
+    
+    Result.setDestructorName(TildeLoc, Ty, ClassNameLoc);
+    
+    if (Tok.is(tok::less))
+      return ParseUnqualifiedIdTemplateId(SS, ClassName, ClassNameLoc,
+                                          EnteringContext, Result);
+
+    return false;
+  }
+  
+  Diag(Tok, diag::err_expected_unqualified_id);
+  return true;
+}
+
 /// TryParseOperatorFunctionId - Attempts to parse a C++ overloaded
 /// operator name (C++ [over.oper]). If successful, returns the
 /// predefined identifier that corresponds to that overloaded