Improve recovery when we see a dependent template name that is missing
the required "template" keyword, using the same heuristics we do for
dependent template names in member access expressions, e.g.,

test/SemaTemplate/dependent-template-recover.cpp:11:8: error: use 'template'
      keyword to treat 'getAs' as a dependent template name
    T::getAs<U>();
       ^
       template 

Fixes PR5404.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@104409 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index ea263f9..646f840 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1463,7 +1463,7 @@
                                SourceLocation TemplateKWLoc = SourceLocation(),
                                bool AllowTypeAnnotation = true);
   void AnnotateTemplateIdTokenAsType(const CXXScopeSpec *SS = 0);
-  bool IsTemplateArgumentList();
+  bool IsTemplateArgumentList(unsigned Skip = 0);
   bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs);
   ParsedTemplateArgument ParseTemplateTemplateArgument();
   ParsedTemplateArgument ParseTemplateArgument();
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 1588b69..0a909f6 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -307,6 +307,31 @@
                                     SourceLocation(), false))
           return true;
         continue;
+      } 
+      
+      if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) && 
+          IsTemplateArgumentList(1)) {
+        // We have something like t::getAs<T>, where getAs is a 
+        // member of an unknown specialization. However, this will only
+        // parse correctly as a template, so suggest the keyword 'template'
+        // before 'getAs' and treat this as a dependent template name.
+        Diag(Tok.getLocation(), diag::err_missing_dependent_template_keyword)
+          << II.getName()
+          << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
+        
+        Template = Actions.ActOnDependentTemplateName(Tok.getLocation(), SS, 
+                                                      TemplateName, ObjectType,
+                                                      EnteringContext);
+        if (!Template.get())
+          return true;     
+        
+        // Consume the identifier.
+        ConsumeToken();
+        if (AnnotateTemplateIdToken(Template, TNK_Dependent_template_name, &SS,
+                                    TemplateName, SourceLocation(), false))
+          return true;
+        
+        continue;        
       }
     }
 
diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp
index 8b9142c..c87ddad 100644
--- a/lib/Parse/ParseTemplate.cpp
+++ b/lib/Parse/ParseTemplate.cpp
@@ -971,12 +971,17 @@
 /// \brief Determine whether the current tokens can only be parsed as a 
 /// template argument list (starting with the '<') and never as a '<' 
 /// expression.
-bool Parser::IsTemplateArgumentList() {
+bool Parser::IsTemplateArgumentList(unsigned Skip) {
   struct AlwaysRevertAction : TentativeParsingAction {
     AlwaysRevertAction(Parser &P) : TentativeParsingAction(P) { }
     ~AlwaysRevertAction() { Revert(); }
   } Tentative(*this);
   
+  while (Skip) {
+    ConsumeToken();
+    --Skip;
+  }
+  
   // '<'
   if (!Tok.is(tok::less))
     return false;
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index b4f9c3d..8ff637f 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -175,6 +175,7 @@
                                        TemplateNameKind &SuggestedKind) {
   // We can't recover unless there's a dependent scope specifier preceding the
   // template name.
+  // FIXME: Typo correction?
   if (!SS || !SS->isSet() || !isDependentScopeSpecifier(*SS) ||
       computeDeclContext(*SS))
     return false;
diff --git a/test/SemaTemplate/dependent-base-classes.cpp b/test/SemaTemplate/dependent-base-classes.cpp
index d0dd9c9..e64d623 100644
--- a/test/SemaTemplate/dependent-base-classes.cpp
+++ b/test/SemaTemplate/dependent-base-classes.cpp
@@ -6,7 +6,7 @@
 };
 
 template<typename T, typename U>
-struct X1 : T::apply<U> { }; // expected-error{{missing 'template' keyword prior to dependent template name 'T::apply'}}
+struct X1 : T::apply<U> { }; // expected-error{{use 'template' keyword to treat 'apply' as a dependent template name}}
 
 template<typename T>
 struct X2 : vector<T> { }; // expected-error{{unknown template name 'vector'}}
diff --git a/test/SemaTemplate/dependent-template-recover.cpp b/test/SemaTemplate/dependent-template-recover.cpp
index 632a11b..e91ffb5 100644
--- a/test/SemaTemplate/dependent-template-recover.cpp
+++ b/test/SemaTemplate/dependent-template-recover.cpp
@@ -8,6 +8,9 @@
     t->operator+<U const, 1>(); // expected-error{{use 'template' keyword to treat 'operator +' as a dependent template name}}
     t->f1<int const, 2>(); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}}
 
+    T::getAs<U>(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}}
+    t->T::getAs<U>(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}}
+
     // FIXME: We can't recover from these yet
     (*t).f2<N>(); // expected-error{{expected expression}}
     (*t).f2<0>(); // expected-error{{expected expression}}