[clang] 0c42539 - Improve error recovery from missing '>' in template argument list.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 27 18:59:25 PDT 2020


Author: Richard Smith
Date: 2020-03-27T18:59:01-07:00
New Revision: 0c42539df3d4c697fa3bf6fc88e94b127d334a57

URL: https://github.com/llvm/llvm-project/commit/0c42539df3d4c697fa3bf6fc88e94b127d334a57
DIFF: https://github.com/llvm/llvm-project/commit/0c42539df3d4c697fa3bf6fc88e94b127d334a57.diff

LOG: Improve error recovery from missing '>' in template argument list.

Produce the conventional "to match this '<'" note, so that the user
knows why we expected a '>', and properly handle '>>' in C++11 onwards.

Added: 
    

Modified: 
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/ParsedTemplate.h
    clang/lib/Parse/ParseObjc.cpp
    clang/lib/Parse/ParseTemplate.cpp
    clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp
    clang/test/CXX/drs/dr3xx.cpp
    clang/test/Parser/cxx-member-initializers.cpp
    clang/test/Parser/cxx-template-argument.cpp
    clang/test/Parser/cxx-template-decl.cpp
    clang/test/Parser/objc-error-qualified-implementation.m
    clang/test/SemaCXX/decltype.cpp
    clang/test/SemaCXX/implicit-exception-spec.cpp
    clang/test/SemaCXX/injected-class-name-crash.cpp
    clang/test/SemaObjC/crash-on-type-args-protocols.m
    clang/test/SemaTemplate/ms-delayed-default-template-args.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 6ebceee006e6..333c96e8cd27 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3160,7 +3160,8 @@ class Parser : public CodeCompletionHandler {
   // C++ 14.3: Template arguments [temp.arg]
   typedef SmallVector<ParsedTemplateArgument, 16> TemplateArgList;
 
-  bool ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc,
+  bool ParseGreaterThanInTemplateList(SourceLocation LAngleLoc,
+                                      SourceLocation &RAngleLoc,
                                       bool ConsumeLastToken,
                                       bool ObjCGenericList);
   bool ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,

diff  --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h
index dd77cbf3b9fe..f0245b93c7eb 100644
--- a/clang/include/clang/Sema/ParsedTemplate.h
+++ b/clang/include/clang/Sema/ParsedTemplate.h
@@ -186,7 +186,7 @@ namespace clang {
     unsigned NumArgs;
 
     /// Whether an error was encountered in the template arguments.
-    /// If so, NumArgs and the trailing argumentst are best-effort.
+    /// If so, NumArgs and the trailing arguments are best-effort.
     bool ArgsInvalid;
 
     /// Retrieves a pointer to the template arguments

diff  --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index 1c6163151386..f0a2a482f063 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -523,10 +523,9 @@ ObjCTypeParamList *Parser::parseObjCTypeParamListOrProtocolRefs(
     SkipUntil(tok::greater, tok::at, StopBeforeMatch);
     if (Tok.is(tok::greater))
       ConsumeToken();
-  } else if (ParseGreaterThanInTemplateList(rAngleLoc,
+  } else if (ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc,
                                             /*ConsumeLastToken=*/true,
                                             /*ObjCGenericList=*/true)) {
-    Diag(lAngleLoc, diag::note_matching) << "'<'";
     SkipUntil({tok::greater, tok::greaterequal, tok::at, tok::minus,
                tok::minus, tok::plus, tok::colon, tok::l_paren, tok::l_brace,
                tok::comma, tok::semi },
@@ -1551,7 +1550,7 @@ ParseObjCProtocolReferences(SmallVectorImpl<Decl *> &Protocols,
   }
 
   // Consume the '>'.
-  if (ParseGreaterThanInTemplateList(EndLoc, consumeLastToken,
+  if (ParseGreaterThanInTemplateList(LAngleLoc, EndLoc, consumeLastToken,
                                      /*ObjCGenericList=*/false))
     return true;
 
@@ -1649,7 +1648,7 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers(
   if (allSingleIdentifiers) {
     // Parse the closing '>'.
     SourceLocation rAngleLoc;
-    (void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken,
+    (void)ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc, consumeLastToken,
                                          /*ObjCGenericList=*/true);
 
     // Let Sema figure out what we parsed.
@@ -1755,7 +1754,7 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers(
 
   // Parse the closing '>'.
   SourceLocation rAngleLoc;
-  (void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken,
+  (void)ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc, consumeLastToken,
                                        /*ObjCGenericList=*/true);
 
   if (invalid) {

diff  --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 9b49a27b731e..518167321f18 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1031,7 +1031,8 @@ void Parser::DiagnoseMisplacedEllipsisInDeclarator(SourceLocation EllipsisLoc,
 /// or argument list.
 ///
 /// \returns true, if current token does not start with '>', false otherwise.
-bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc,
+bool Parser::ParseGreaterThanInTemplateList(SourceLocation LAngleLoc,
+                                            SourceLocation &RAngleLoc,
                                             bool ConsumeLastToken,
                                             bool ObjCGenericList) {
   // What will be left once we've consumed the '>'.
@@ -1041,7 +1042,8 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc,
 
   switch (Tok.getKind()) {
   default:
-    Diag(Tok.getLocation(), diag::err_expected) << tok::greater;
+    Diag(getEndOfPreviousToken(), diag::err_expected) << tok::greater;
+    Diag(LAngleLoc, diag::note_matching) << tok::less;
     return true;
 
   case tok::greater:
@@ -1220,15 +1222,15 @@ Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
 
     if (Invalid) {
       // Try to find the closing '>'.
-      // FIXME: Handle `>>`, `>>>`.
-      if (ConsumeLastToken)
-        SkipUntil(tok::greater, StopAtSemi);
+      if (getLangOpts().CPlusPlus11)
+        SkipUntil(tok::greater, tok::greatergreater,
+                  tok::greatergreatergreater, StopAtSemi | StopBeforeMatch);
       else
         SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
     }
   }
 
-  return ParseGreaterThanInTemplateList(RAngleLoc, ConsumeLastToken,
+  return ParseGreaterThanInTemplateList(LAngleLoc, RAngleLoc, ConsumeLastToken,
                                         /*ObjCGenericList=*/false) ||
          Invalid;
 }
@@ -1409,7 +1411,8 @@ void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
 /// Determine whether the given token can end a template argument.
 static bool isEndOfTemplateArgument(Token Tok) {
   // FIXME: Handle '>>>'.
-  return Tok.isOneOf(tok::comma, tok::greater, tok::greatergreater);
+  return Tok.isOneOf(tok::comma, tok::greater, tok::greatergreater,
+                     tok::greatergreatergreater);
 }
 
 /// Parse a C++ template template argument.
@@ -1742,7 +1745,7 @@ bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) {
     TPA.Commit();
 
     SourceLocation Greater;
-    ParseGreaterThanInTemplateList(Greater, true, false);
+    ParseGreaterThanInTemplateList(Less, Greater, true, false);
     Actions.diagnoseExprIntendedAsTemplateName(getCurScope(), LHS,
                                                Less, Greater);
     return true;
@@ -1771,7 +1774,7 @@ void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) {
        NextToken().isOneOf(tok::greatergreater, tok::greatergreatergreater))) {
     SourceLocation Less = ConsumeToken();
     SourceLocation Greater;
-    ParseGreaterThanInTemplateList(Greater, true, false);
+    ParseGreaterThanInTemplateList(Less, Greater, true, false);
     Actions.diagnoseExprIntendedAsTemplateName(
         getCurScope(), PotentialTemplateName, Less, Greater);
     // FIXME: Perform error recovery.

diff  --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp
index ef3b127ef82a..8ac4e9733ebb 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp
@@ -60,7 +60,7 @@ namespace AnnexD_example {
     A a;
     f < a;
 #if __cplusplus > 201703L
-    // expected-error at -2 {{expected '>'}}
+    // expected-error at -2 {{expected '>'}} expected-note at -2 {{to match this '<'}}
 #endif
     (f) < a;
   }

diff  --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp
index fad91c6a584d..a1501fa563b1 100644
--- a/clang/test/CXX/drs/dr3xx.cpp
+++ b/clang/test/CXX/drs/dr3xx.cpp
@@ -20,7 +20,7 @@ namespace dr301 { // dr301: yes
              (void(*)(S, S))operator+<S>;
     bool b = (void(*)(S, S))operator- <
              (void(*)(S, S))operator-;
-    bool c = (void(*)(S, S))operator+ <
+    bool c = (void(*)(S, S))operator+ < // expected-note {{to match this '<'}}
              (void(*)(S, S))operator-; // expected-error {{expected '>'}}
   }
 

diff  --git a/clang/test/Parser/cxx-member-initializers.cpp b/clang/test/Parser/cxx-member-initializers.cpp
index bd74af1946b6..f5b09752d72b 100644
--- a/clang/test/Parser/cxx-member-initializers.cpp
+++ b/clang/test/Parser/cxx-member-initializers.cpp
@@ -62,7 +62,7 @@ namespace PR16480 {
 
   // FIXME: This should be valid in the union of C99 and C++11.
   struct F : X<0> {
-    F() : X<A<T>().n + (T){}.n>{} {} // expected-error +{{}}
+    F() : X<A<T>().n + (T){}.n>{} {} // expected-error +{{}} expected-note {{to match}}
 
     struct T { int n; };
     template<typename> struct A { int n; };
@@ -70,7 +70,7 @@ namespace PR16480 {
 
   // FIXME: This is valid now, but may be made ill-formed by DR1607.
   struct G : X<0> {
-    G() : X<0 && [](){return 0;}()>{} // expected-error +{{}}
+    G() : X<0 && [](){return 0;}()>{} // expected-error +{{}} expected-note {{to match}}
   };
 
   struct Errs : X<0> {
@@ -103,5 +103,5 @@ class G {
   void l(int x = C<int, C<int, int>::C1>().f()) {}
 
   // This isn't, but it shouldn't crash. The diagnostics don't matter much.
-  void m(int x = C<int, union int>().f()) {} // expected-error {{declaration of anonymous union must be a definition}} expected-error {{expected a type}} expected-error {{expected '>'}}
+  void m(int x = C<int, union int>().f()) {} // expected-error {{declaration of anonymous union must be a definition}} expected-error {{expected a type}} expected-error {{expected '>'}} expected-note {{to match}}
 };

diff  --git a/clang/test/Parser/cxx-template-argument.cpp b/clang/test/Parser/cxx-template-argument.cpp
index 70945e3aa746..0e72cbfbfb17 100644
--- a/clang/test/Parser/cxx-template-argument.cpp
+++ b/clang/test/Parser/cxx-template-argument.cpp
@@ -9,8 +9,8 @@ template<typename T> struct A {};
 
 // Check for template argument lists followed by junk
 // FIXME: The diagnostics here aren't great...
-A<int+> int x; // expected-error {{expected '>'}} expected-error {{expected unqualified-id}}
-A<int x; // expected-error {{expected '>'}}
+A<int+> int x; // expected-error {{expected '>'}} expected-note {{to match this '<'}} expected-error {{expected unqualified-id}}
+A<int x; // expected-error {{expected '>'}} expected-note {{to match this '<'}}
 
 // PR8912
 template <bool> struct S {};

diff  --git a/clang/test/Parser/cxx-template-decl.cpp b/clang/test/Parser/cxx-template-decl.cpp
index fffe75255c24..14b97f12e357 100644
--- a/clang/test/Parser/cxx-template-decl.cpp
+++ b/clang/test/Parser/cxx-template-decl.cpp
@@ -233,13 +233,13 @@ namespace broken_baseclause {
 template<typename T>
 struct base { };
 
-struct t1 : base<int,
-  public:  // expected-error {{expected expression}}
-}; // expected-error {{expected '>'}}
+struct t1 : base<int, // expected-note {{to match this '<'}}
+  public:  // expected-error {{expected expression}} expected-error {{expected '>'}}
+};
 // expected-error at -1 {{expected '{' after base class list}}
-struct t2 : base<int,
-  public  // expected-error {{expected expression}}
-}; // expected-error {{expected '>'}}
+struct t2 : base<int, // expected-note {{to match this '<'}}
+  public  // expected-error {{expected expression}} expected-error {{expected '>'}}
+};
 // expected-error at -1 {{expected '{' after base class list}}
 
 }

diff  --git a/clang/test/Parser/objc-error-qualified-implementation.m b/clang/test/Parser/objc-error-qualified-implementation.m
index 179e2d27479b..684c388a584e 100644
--- a/clang/test/Parser/objc-error-qualified-implementation.m
+++ b/clang/test/Parser/objc-error-qualified-implementation.m
@@ -17,13 +17,13 @@ @implementation J < P,P > // expected-error {{@implementation declaration cannot
 
 @interface K @end
 
- at implementation K <P // expected-error {{@implementation declaration cannot be protocol qualified}} expected-note{{to match this '<'}}
- at end // expected-error {{expected '>'}}
+ at implementation K <P // expected-error {{@implementation declaration cannot be protocol qualified}} expected-error {{expected '>'}} expected-note {{to match this '<'}}
+ at end
 
 // rdar://13920026
 @implementation I (Cat) <P>  // expected-error {{@implementation declaration cannot be protocol qualified}}
 - (void) Meth {}
 @end
 
- at implementation I (Cat1) <P // expected-error {{@implementation declaration cannot be protocol qualified}}
- at end // expected-error {{expected '>'}}
+ at implementation I (Cat1) <P // expected-error {{@implementation declaration cannot be protocol qualified}} expected-error {{expected '>'}} expected-note {{to match this '<'}}
+ at end

diff  --git a/clang/test/SemaCXX/decltype.cpp b/clang/test/SemaCXX/decltype.cpp
index f06ca226bca2..32c61bbccc84 100644
--- a/clang/test/SemaCXX/decltype.cpp
+++ b/clang/test/SemaCXX/decltype.cpp
@@ -105,5 +105,6 @@ template<typename>
 class conditional {
 };
 
-void foo(conditional<decltype((1),int>) {  // expected-note 2 {{to match this '('}} expected-error {{expected ')'}}
+// FIXME: The diagnostics here are produced twice.
+void foo(conditional<decltype((1),int>) {  // expected-note 2 {{to match this '('}} expected-error {{expected ')'}} expected-note 2{{to match this '<'}}
 } // expected-error {{expected function body after function declarator}} expected-error 2 {{expected '>'}} expected-error {{expected ')'}}

diff  --git a/clang/test/SemaCXX/implicit-exception-spec.cpp b/clang/test/SemaCXX/implicit-exception-spec.cpp
index 483ed3ca9440..8545e8422b2a 100644
--- a/clang/test/SemaCXX/implicit-exception-spec.cpp
+++ b/clang/test/SemaCXX/implicit-exception-spec.cpp
@@ -56,7 +56,7 @@ namespace ExceptionSpecification {
 namespace DefaultArgument {
   struct Default {
     struct T {
-      T(int = ExceptionIf<noexcept(Default())::f()); // expected-error {{call to implicitly-deleted default constructor}} expected-error {{expected '>'}}
+      T(int = ExceptionIf<noexcept(Default())::f()); // expected-error {{call to implicitly-deleted default constructor}} expected-error {{expected '>'}} expected-note {{to match this '<'}}
     } t; // expected-note {{has no default constructor}}
   };
 }

diff  --git a/clang/test/SemaCXX/injected-class-name-crash.cpp b/clang/test/SemaCXX/injected-class-name-crash.cpp
index 7743717c85fc..a044ba064b58 100644
--- a/clang/test/SemaCXX/injected-class-name-crash.cpp
+++ b/clang/test/SemaCXX/injected-class-name-crash.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 
 template <class T>
-struct X : public Foo<Bar { // expected-error {{unknown template name 'Foo'}} expected-error {{use of undeclared identifier 'Bar'}}
+struct X : public Foo<Bar { // expected-error {{unknown template name 'Foo'}} expected-error {{use of undeclared identifier 'Bar'}} expected-note {{to match this '<'}}
   X();
 }; // expected-error {{expected '>'}} expected-error {{expected '{' after base class list}}
 

diff  --git a/clang/test/SemaObjC/crash-on-type-args-protocols.m b/clang/test/SemaObjC/crash-on-type-args-protocols.m
index f7d847851faf..0d902631a28c 100644
--- a/clang/test/SemaObjC/crash-on-type-args-protocols.m
+++ b/clang/test/SemaObjC/crash-on-type-args-protocols.m
@@ -14,27 +14,27 @@ @interface X : NSObject <X>
 @class A;
 
 #ifdef FIRST
-id<X> F1(id<[P> v) { // expected-error {{expected a type}} // expected-error {{use of undeclared identifier 'P'}} // expected-error {{use of undeclared identifier 'v'}} // expected-note {{to match this '('}}
+id<X> F1(id<[P> v) { // expected-error {{expected a type}} expected-error {{use of undeclared identifier 'P'}} expected-error {{use of undeclared identifier 'v'}} expected-note {{to match this '('}} expected-note {{to match this '<'}}
   return 0;
-}
+} // expected-error {{expected '>'}}
 #endif
 
 #ifdef SECOND
-id<X> F2(id<P[X> v) { // expected-error {{unknown type name 'P'}} // expected-error {{unexpected interface name 'X': expected expression}} // expected-error {{use of undeclared identifier 'v'}} // expected-note {{to match this '('}}
+id<X> F2(id<P[X> v) { // expected-error {{unknown type name 'P'}} expected-error {{unexpected interface name 'X': expected expression}} expected-error {{use of undeclared identifier 'v'}} expected-note {{to match this '('}} expected-note {{to match this '<'}}
   return 0;
-}
+} // expected-error {{expected '>'}}
 #endif
 
 #ifdef THIRD
-id<X> F3(id<P, P *[> v) { // expected-error {{unknown type name 'P'}} // expected-error {{expected expression}} // expected-error {{use of undeclared identifier 'v'}} // expected-note {{to match this '('}}
+id<X> F3(id<P, P *[> v) { // expected-error {{unknown type name 'P'}} expected-error {{expected expression}} expected-error {{use of undeclared identifier 'v'}} expected-note {{to match this '('}} expected-note {{to match this '<'}}
   return 0;
-}
+} // expected-error {{expected '>'}}
 #endif
 
 #ifdef FOURTH
-id<X> F4(id<P, P *(> v { // expected-error {{unknown type name 'P'}} // expected-error {{expected ')'}} // expected-note {{to match this '('}} // expected-note {{to match this '('}}
+id<X> F4(id<P, P *(> v { // expected-error {{unknown type name 'P'}} expected-error {{expected ')'}} expected-note {{to match this '('}} expected-note {{to match this '('}} expected-note {{to match this '<'}}
   return 0;
-}
+} // expected-error {{expected '>'}}
 #endif
 
-// expected-error {{expected '>'}} // expected-error {{expected parameter declarator}} // expected-error {{expected ')'}} // expected-error {{expected function body after function declarator}}
+// expected-error {{expected parameter declarator}} // expected-error {{expected ')'}} // expected-error {{expected function body after function declarator}}

diff  --git a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
index 008a9c317fe9..3c4690595524 100644
--- a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
+++ b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
@@ -49,9 +49,7 @@ template <typename T> struct Bar { T x; };
 template <typename T = Bar<Weber>>  // expected-error {{use of undeclared identifier 'Weber'}}
 struct Foo {
   static_assert(sizeof(T) == 4, "Bar should have gotten int");
-  // FIXME: These diagnostics are bad.
-}; // expected-error {{expected ',' or '>' in template-parameter-list}} expected-error {{expected '>'}}
-// expected-warning at -1 {{does not declare anything}}
+};
 typedef int Weber;
 }
 


        


More information about the cfe-commits mailing list