[cfe-commits] r104406 - in /cfe/trunk: include/clang/Basic/DiagnosticParseKinds.td include/clang/Parse/Action.h include/clang/Parse/Parser.h lib/Parse/MinimalAction.cpp lib/Parse/ParseExprCXX.cpp lib/Parse/ParseTemplate.cpp lib/Parse/Parser.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaExpr.cpp lib/Sema/SemaTemplate.cpp test/SemaTemplate/dependent-template-recover.cpp

Douglas Gregor dgregor at apple.com
Fri May 21 16:18:07 PDT 2010


Author: dgregor
Date: Fri May 21 18:18:07 2010
New Revision: 104406

URL: http://llvm.org/viewvc/llvm-project?rev=104406&view=rev
Log:
Improve parser recovery when we encounter a dependent template name
that is missing the 'template' keyword, e.g., 

  t->getAs<T>()

where getAs is a member of an unknown specialization. C++ requires
that we treat "getAs" as a value, but that would fail to parse since T
is the name of a type. We would then fail at the '>', since a type
cannot be followed by a '>'.

This is a very common error for C++ programmers to make, especially
since GCC occasionally allows it when it shouldn't (as does Visual
C++). So, when we are in this case, we use tentative parsing to see if
the tokens starting at "<" can only be parsed as a template argument
list. If so, we produce a diagnostic with a fix-it that states that
the 'template' keyword is needed:

test/SemaTemplate/dependent-template-recover.cpp:5:8: error: 'template' keyword
      is required to treat 'getAs' as a dependent template name
    t->getAs<T>();
       ^
       template 

This is just a start of this patch; I'd like to apply the same
approach to everywhere that a template-id with dependent template name
can be parsed.

Added:
    cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/MinimalAction.cpp
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/lib/Parse/ParseTemplate.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Fri May 21 18:18:07 2010
@@ -313,6 +313,9 @@
     "'template' keyword">;
 def err_enum_template : Error<"enumeration cannot be a template">;
 
+def err_missing_dependent_template_keyword : Error<
+  "use 'template' keyword to treat '%0' as a dependent template name">;
+
 // Constructor template diagnostics.
 def err_out_of_line_constructor_template_id : Error<
   "out-of-line constructor for %0 cannot have template arguments">;

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Fri May 21 18:18:07 2010
@@ -286,13 +286,18 @@
   /// \param Template if the name does refer to a template, the declaration
   /// of the template that the name refers to.
   ///
+  /// \param MemberOfUnknownSpecialization Will be set true if the resulting 
+  /// member would be a member of an unknown specialization, in which case this
+  /// lookup cannot possibly pass at this time.
+  ///
   /// \returns the kind of template that this name refers to.
   virtual TemplateNameKind isTemplateName(Scope *S,
                                           CXXScopeSpec &SS,
                                           UnqualifiedId &Name,
                                           TypeTy *ObjectType,
                                           bool EnteringContext,
-                                          TemplateTy &Template) = 0;
+                                          TemplateTy &Template,
+                                      bool &MemberOfUnknownSpecialization) = 0;
 
   /// \brief Action called as part of error recovery when the parser has 
   /// determined that the given name must refer to a template, but 
@@ -3027,7 +3032,9 @@
                                           UnqualifiedId &Name,
                                           TypeTy *ObjectType,
                                           bool EnteringContext,
-                                          TemplateTy &Template);
+                                          TemplateTy &Template,
+                                          bool &MemberOfUnknownSpecialization);
+
   
   /// ActOnDeclarator - If this is a typedef declarator, we modify the
   /// IdentifierInfo::FETokenInfo field to keep track of this fact, until S is

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Fri May 21 18:18:07 2010
@@ -1463,6 +1463,7 @@
                                SourceLocation TemplateKWLoc = SourceLocation(),
                                bool AllowTypeAnnotation = true);
   void AnnotateTemplateIdTokenAsType(const CXXScopeSpec *SS = 0);
+  bool IsTemplateArgumentList();
   bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs);
   ParsedTemplateArgument ParseTemplateTemplateArgument();
   ParsedTemplateArgument ParseTemplateArgument();

Modified: cfe/trunk/lib/Parse/MinimalAction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/MinimalAction.cpp?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/MinimalAction.cpp (original)
+++ cfe/trunk/lib/Parse/MinimalAction.cpp Fri May 21 18:18:07 2010
@@ -194,7 +194,9 @@
                               UnqualifiedId &Name,
                               TypeTy *ObjectType,
                               bool EnteringScope,
-                              TemplateTy &TemplateDecl) {
+                              TemplateTy &TemplateDecl,
+                              bool &MemberOfUnknownSpecialization) {
+  MemberOfUnknownSpecialization = false;
   return TNK_Non_template;
 }
 

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Fri May 21 18:18:07 2010
@@ -289,11 +289,13 @@
       TemplateTy Template;
       UnqualifiedId TemplateName;
       TemplateName.setIdentifier(&II, Tok.getLocation());
+      bool MemberOfUnknownSpecialization;
       if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, SS, 
                                                         TemplateName,
                                                         ObjectType,
                                                         EnteringContext,
-                                                        Template)) {
+                                                        Template,
+                                              MemberOfUnknownSpecialization)) {
         // We have found a template name, so annotate this this token
         // with a template-id annotation. We do not permit the
         // template-id to be translated into a type annotation,
@@ -990,21 +992,54 @@
       TNK = TNK_Dependent_template_name;
       if (!Template.get())
         return true;      
-    } else 
+    } else {
+      bool MemberOfUnknownSpecialization;
       TNK = Actions.isTemplateName(CurScope, SS, Id, ObjectType, 
-                                   EnteringContext, Template);
+                                   EnteringContext, Template,
+                                   MemberOfUnknownSpecialization);
+      
+      if (TNK == TNK_Non_template && MemberOfUnknownSpecialization &&
+          ObjectType && IsTemplateArgumentList()) {
+        // 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.
+        std::string Name;
+        if (Id.getKind() == UnqualifiedId::IK_Identifier)
+          Name = Id.Identifier->getName();
+        else {
+          Name = "operator ";
+          if (Id.getKind() == UnqualifiedId::IK_OperatorFunctionId)
+            Name += getOperatorSpelling(Id.OperatorFunctionId.Operator);
+          else
+            Name += Id.Identifier->getName();
+        }
+        Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
+          << Name
+          << FixItHint::CreateInsertion(Id.StartLocation, "template ");
+        Template = Actions.ActOnDependentTemplateName(TemplateKWLoc, SS, 
+                                                      Id, ObjectType,
+                                                      EnteringContext);
+        TNK = TNK_Dependent_template_name;
+        if (!Template.get())
+          return true;              
+      }
+    }
     break;
       
   case UnqualifiedId::IK_ConstructorName: {
     UnqualifiedId TemplateName;
+    bool MemberOfUnknownSpecialization;
     TemplateName.setIdentifier(Name, NameLoc);
     TNK = Actions.isTemplateName(CurScope, SS, TemplateName, ObjectType, 
-                                 EnteringContext, Template);
+                                 EnteringContext, Template,
+                                 MemberOfUnknownSpecialization);
     break;
   }
       
   case UnqualifiedId::IK_DestructorName: {
     UnqualifiedId TemplateName;
+    bool MemberOfUnknownSpecialization;
     TemplateName.setIdentifier(Name, NameLoc);
     if (ObjectType) {
       Template = Actions.ActOnDependentTemplateName(TemplateKWLoc, SS, 
@@ -1015,7 +1050,8 @@
         return true;
     } else {
       TNK = Actions.isTemplateName(CurScope, SS, TemplateName, ObjectType, 
-                                   EnteringContext, Template);
+                                   EnteringContext, Template,
+                                   MemberOfUnknownSpecialization);
       
       if (TNK == TNK_Non_template && Id.DestructorName == 0) {
         Diag(NameLoc, diag::err_destructor_template_id)

Modified: cfe/trunk/lib/Parse/ParseTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTemplate.cpp?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseTemplate.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTemplate.cpp Fri May 21 18:18:07 2010
@@ -902,10 +902,12 @@
     ConsumeToken(); // the identifier
     
     if (isEndOfTemplateArgument(Tok)) {
+      bool MemberOfUnknownSpecialization;
       TemplateNameKind TNK = Actions.isTemplateName(CurScope, SS, Name, 
                                                     /*ObjectType=*/0, 
                                                     /*EnteringContext=*/false, 
-                                                    Template);
+                                                    Template,
+                                                MemberOfUnknownSpecialization);
       if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template) {
         // We have an id-expression that refers to a class template or
         // (C++0x) template alias. 
@@ -966,6 +968,32 @@
                                 ExprArg.release(), Loc);
 }
 
+/// \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() {
+  struct AlwaysRevertAction : TentativeParsingAction {
+    AlwaysRevertAction(Parser &P) : TentativeParsingAction(P) { }
+    ~AlwaysRevertAction() { Revert(); }
+  } Tentative(*this);
+  
+  // '<'
+  if (!Tok.is(tok::less))
+    return false;
+  ConsumeToken();
+
+  // An empty template argument list.
+  if (Tok.is(tok::greater))
+    return true;
+  
+  // See whether we have declaration specifiers, which indicate a type.
+  while (isCXXDeclarationSpecifier() == TPResult::True())
+    ConsumeToken();
+  
+  // If we have a '>' or a ',' then this is a template argument list.
+  return Tok.is(tok::greater) || Tok.is(tok::comma);
+}
+
 /// ParseTemplateArgumentList - Parse a C++ template-argument-list
 /// (C++ [temp.names]). Returns true if there was an error.
 ///

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Fri May 21 18:18:07 2010
@@ -980,10 +980,11 @@
       TemplateTy Template;
       UnqualifiedId TemplateName;
       TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+      bool MemberOfUnknownSpecialization;
       if (TemplateNameKind TNK
             = Actions.isTemplateName(CurScope, SS, TemplateName, 
                                      /*ObjectType=*/0, EnteringContext,
-                                     Template)) {
+                                     Template, MemberOfUnknownSpecialization)) {
         // Consume the identifier.
         ConsumeToken();
         if (AnnotateTemplateIdToken(Template, TNK, &SS, TemplateName)) {

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Fri May 21 18:18:07 2010
@@ -2800,14 +2800,16 @@
   // C++ Templates [C++ 14]
   //
   void LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS,
-                          QualType ObjectType, bool EnteringContext);
+                          QualType ObjectType, bool EnteringContext,
+                          bool &MemberOfUnknownSpecialization);
 
   virtual TemplateNameKind isTemplateName(Scope *S,
                                           CXXScopeSpec &SS,
                                           UnqualifiedId &Name,
                                           TypeTy *ObjectType,
                                           bool EnteringContext,
-                                          TemplateTy &Template);
+                                          TemplateTy &Template,
+                                          bool &MemberOfUnknownSpecialization);
 
   virtual bool DiagnoseUnknownTemplateName(const IdentifierInfo &II,
                                            SourceLocation IILoc,

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri May 21 18:18:07 2010
@@ -287,8 +287,9 @@
     Name.setIdentifier(&II, IILoc);
     CXXScopeSpec EmptySS;
     TemplateTy TemplateResult;
-    if (isTemplateName(S, SS ? *SS : EmptySS, Name, 0, true, TemplateResult)
-        == TNK_Type_template) {
+    bool MemberOfUnknownSpecialization;
+    if (isTemplateName(S, SS ? *SS : EmptySS, Name, 0, true, TemplateResult,
+                       MemberOfUnknownSpecialization) == TNK_Type_template) {
       TemplateName TplName = TemplateResult.getAsVal<TemplateName>();
       Diag(IILoc, diag::err_template_missing_args) << TplName;
       if (TemplateDecl *TplDecl = TplName.getAsTemplateDecl()) {

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Fri May 21 18:18:07 2010
@@ -1055,7 +1055,9 @@
     // lookup to determine that it was a template name in the first place. If
     // this becomes a performance hit, we can work harder to preserve those
     // results until we get here but it's likely not worth it.
-    LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false);
+    bool MemberOfUnknownSpecialization;
+    LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false,
+                       MemberOfUnknownSpecialization);
   } else {
     bool IvarLookupFollowUp = (!SS.isSet() && II && getCurMethodDecl());
     LookupParsedName(R, S, &SS, !IvarLookupFollowUp);

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=104406&r1=104405&r2=104406&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Fri May 21 18:18:07 2010
@@ -100,10 +100,12 @@
                                       UnqualifiedId &Name,
                                       TypeTy *ObjectTypePtr,
                                       bool EnteringContext,
-                                      TemplateTy &TemplateResult) {
+                                      TemplateTy &TemplateResult,
+                                      bool &MemberOfUnknownSpecialization) {
   assert(getLangOptions().CPlusPlus && "No template names in C!");
 
   DeclarationName TName;
+  MemberOfUnknownSpecialization = false;
   
   switch (Name.getKind()) {
   case UnqualifiedId::IK_Identifier:
@@ -128,7 +130,8 @@
   LookupResult R(*this, TName, Name.getSourceRange().getBegin(), 
                  LookupOrdinaryName);
   R.suppressDiagnostics();
-  LookupTemplateName(R, S, SS, ObjectType, EnteringContext);
+  LookupTemplateName(R, S, SS, ObjectType, EnteringContext,
+                     MemberOfUnknownSpecialization);
   if (R.empty() || R.isAmbiguous())
     return TNK_Non_template;
 
@@ -191,8 +194,10 @@
 void Sema::LookupTemplateName(LookupResult &Found,
                               Scope *S, CXXScopeSpec &SS,
                               QualType ObjectType,
-                              bool EnteringContext) {
+                              bool EnteringContext,
+                              bool &MemberOfUnknownSpecialization) {
   // Determine where to perform name lookup
+  MemberOfUnknownSpecialization = false;
   DeclContext *LookupCtx = 0;
   bool isDependent = false;
   if (!ObjectType.isNull()) {
@@ -241,6 +246,7 @@
   } else if (isDependent) {
     // We cannot look into a dependent object type or nested nme
     // specifier.
+    MemberOfUnknownSpecialization = true;
     return;
   } else {
     // Perform unqualified name lookup in the current scope.
@@ -1641,8 +1647,10 @@
       RequireCompleteDeclContext(SS, DC))
     return BuildDependentDeclRefExpr(SS, Name, NameLoc, &TemplateArgs);
 
+  bool MemberOfUnknownSpecialization;
   LookupResult R(*this, Name, NameLoc, LookupOrdinaryName);
-  LookupTemplateName(R, (Scope*) 0, SS, QualType(), /*Entering*/ false);
+  LookupTemplateName(R, (Scope*) 0, SS, QualType(), /*Entering*/ false,
+                     MemberOfUnknownSpecialization);
 
   if (R.isAmbiguous())
     return ExprError();
@@ -1699,8 +1707,10 @@
     // "template" keyword is now permitted). We follow the C++0x
     // rules, even in C++03 mode, retroactively applying the DR.
     TemplateTy Template;
+    bool MemberOfUnknownSpecialization;
     TemplateNameKind TNK = isTemplateName(0, SS, Name, ObjectType,
-                                          EnteringContext, Template);
+                                          EnteringContext, Template,
+                                          MemberOfUnknownSpecialization);
     if (TNK == TNK_Non_template && LookupCtx->isDependentContext() &&
         isa<CXXRecordDecl>(LookupCtx) &&
         cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()) {

Added: cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp?rev=104406&view=auto
==============================================================================
--- cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp (added)
+++ cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp Fri May 21 18:18:07 2010
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+template<typename T, typename U, int N>
+struct X {
+  void f(T* t) {
+    t->f0<U>(); // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
+    t->f0<int>(); // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
+
+    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}}
+
+    // FIXME: We can't recover from these yet
+    (*t).f2<N>(); // expected-error{{expected expression}}
+    (*t).f2<0>(); // expected-error{{expected expression}}
+  }
+};





More information about the cfe-commits mailing list