[PATCH] D43331: [WIP][Parser][FixIt] Better diagnostics for omitted template keyword (type dependent template names)

Jan Korous via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 15 03:09:31 PST 2018


jkorous-apple created this revision.

What I would love to have is FixIt for this kind of situation:

  template <class T> void foo() {
      T::bar<int*>(); /* missing template keyword */
  }

which currently gets not that helpful diagnostics:

  > clang misleading.cpp
  misleading.cpp:2:15: error: expected '(' for function-style cast or type construction
      T::bar<int*>();
             ~~~^
  misleading.cpp:2:16: error: expected expression
      T::bar<int*>();
                 ^
  misleading.cpp:2:18: error: expected expression
      T::bar<int*>();
                   ^
  3 errors generated.

while

  template <class T> void foo() {
      T::bar<int>();
  }

gets a good one:

  good.cpp:2:8: error: use 'template' keyword to treat 'bar' as a dependent template name
      T::bar<int>();
         ^
         template
  1 error generated.

If I understand it correctly than current implementation of

  bool Parser::IsTemplateArgumentList(unsigned Skip)

relies on the fact that if there's a '<' token followed by declaration specifier it has to be a template. Which is true but produces false negatives for non-type parameters (array<1>) or pointer type parameters (vector<int*>) (among others).

One particular thing to keep on mind is that there's the [over.over] rule about resolving names to function pointers - best explained by test test/SemaTemplate/dependent-template-recover.cpp (look for snippet below):

  // Note: no diagnostic here, this is actually valid as a comparison between
  // the decayed pointer to Y::g<> and mb!
  T::g<mb>(0);

What I did so far is that in case the simple approach doesn't validate there's a template I try to use speculative parsing. The only problem remaining is diagnostics - I am able to use TentativeParsingAction which is later reverted but I still get all the diagnostics which is incorrect in case my speculation about how to parse the code was wrong.


https://reviews.llvm.org/D43331

Files:
  FixIt/fixit-template-for-dependent-name.cpp
  Parse/ParseTemplate.cpp
  SemaTemplate/dependent-template-recover.cpp


Index: SemaTemplate/dependent-template-recover.cpp
===================================================================
--- SemaTemplate/dependent-template-recover.cpp
+++ SemaTemplate/dependent-template-recover.cpp
@@ -6,6 +6,7 @@
     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->operator+<1, U const>(); // 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}}
Index: FixIt/fixit-template-for-dependent-name.cpp
===================================================================
--- /dev/null
+++ FixIt/fixit-template-for-dependent-name.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template <class T> void foo() {
+    T::bar<int>(); // expected-error {{use 'template' keyword to treat 'bar' as a dependent template name}}
+    T::baz<int*>(); // expected-error {{use 'template' keyword to treat 'baz' as a dependent template name}}
+}
+
+// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// CHECK: fix-it:{{.*}}:{4:8-4:8}:"template "
+// CHECK: fix-it:{{.*}}:{5:8-5:8}:"template "
Index: Parse/ParseTemplate.cpp
===================================================================
--- Parse/ParseTemplate.cpp
+++ Parse/ParseTemplate.cpp
@@ -1247,30 +1247,45 @@
 /// template argument list (starting with the '<') and never as a '<' 
 /// expression.
 bool Parser::IsTemplateArgumentList(unsigned Skip) {
-  struct AlwaysRevertAction : TentativeParsingAction {
-    AlwaysRevertAction(Parser &P) : TentativeParsingAction(P) { }
-    ~AlwaysRevertAction() { Revert(); }
-  } Tentative(*this);
-  
-  while (Skip) {
-    ConsumeAnyToken();
-    --Skip;
+  {
+    RevertingTentativeParsingAction AutoRevertScopeGuard(*this);
+
+    for (; Skip > 0; --Skip) {
+      ConsumeToken();
+    }
+
+    if (!TryConsumeToken(tok::less))
+      return false;
+
+    // If the first wannabe template argument consists only of decl specs
+    // it's a template argument list indeed.
+
+    // See whether we have declaration specifiers, which indicate a type.
+    while (isCXXDeclarationSpecifier() == TPResult::True)
+      ConsumeAnyToken();
+
+    // If we have a '>' or a ',' then this is a template argument list.
+    if (Tok.isOneOf(tok::greater, tok::comma))
+      return true;
   }
-  
-  // '<'
+
+  RevertingTentativeParsingAction AutoRevertScopeGuard(*this);
+
+  for (; Skip > 0; --Skip) {
+    ConsumeToken();
+  }
+
   if (!TryConsumeToken(tok::less))
     return false;
 
-  // 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)
-    ConsumeAnyToken();
-  
-  // If we have a '>' or a ',' then this is a template argument list.
-  return Tok.isOneOf(tok::greater, tok::comma);
+  TemplateArgList ignore;
+  if (ParseTemplateArgumentList(ignore))
+    return false;
+
+  if (!TryConsumeToken(tok::greater))
+    return false;
+
+  return true;
 }
 
 /// ParseTemplateArgumentList - Parse a C++ template-argument-list


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D43331.134397.patch
Type: text/x-patch
Size: 3511 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20180215/4a91f703/attachment.bin>


More information about the cfe-commits mailing list