[clang] b3f6e3d - Improve recovery from invalid template-ids.

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


Author: Richard Smith
Date: 2020-03-27T17:11:04-07:00
New Revision: b3f6e3d6d64d77a9c840b8407b7e3c49b62b46dd

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

LOG: Improve recovery from invalid template-ids.

Instead of bailing out of parsing when we encounter an invalid
template-name or template arguments in a template-id, produce an
annotation token describing the invalid construct.

This avoids duplicate errors and generally allows us to recover better.
In principle we should be able to extend this to store some kinds of
invalid template-id in the AST for tooling use, but that isn't handled
as part of this change.

Added: 
    

Modified: 
    clang/include/clang/Sema/Ownership.h
    clang/include/clang/Sema/ParsedTemplate.h
    clang/lib/Parse/ParseCXXInlineMethods.cpp
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Parse/ParseDeclCXX.cpp
    clang/lib/Parse/ParseExprCXX.cpp
    clang/lib/Parse/ParseTemplate.cpp
    clang/lib/Parse/ParseTentative.cpp
    clang/lib/Parse/Parser.cpp
    clang/lib/Sema/DeclSpec.cpp
    clang/test/CXX/drs/dr3xx.cpp
    clang/test/CXX/temp/p3.cpp
    clang/test/Parser/cxx-ambig-init-templ.cpp
    clang/test/Parser/cxx-class.cpp
    clang/test/Parser/cxx-member-initializers.cpp
    clang/test/Parser/cxx-template-decl.cpp
    clang/test/Parser/eof2.cpp
    clang/test/Parser/recovery.cpp
    clang/test/SemaCXX/PR20705.cpp
    clang/test/SemaCXX/PR9459.cpp
    clang/test/SemaCXX/builtins.cpp
    clang/test/SemaCXX/implicit-exception-spec.cpp
    clang/test/SemaCXX/injected-class-name-crash.cpp
    clang/test/SemaCXX/invalid-member-expr.cpp
    clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
    clang/test/SemaTemplate/temp_arg.cpp
    clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Ownership.h b/clang/include/clang/Sema/Ownership.h
index 4de3397d51b4..7c7b1d35c9fd 100644
--- a/clang/include/clang/Sema/Ownership.h
+++ b/clang/include/clang/Sema/Ownership.h
@@ -278,6 +278,7 @@ namespace clang {
 
   inline ExprResult ExprError() { return ExprResult(true); }
   inline StmtResult StmtError() { return StmtResult(true); }
+  inline TypeResult TypeError() { return TypeResult(true); }
 
   inline ExprResult ExprError(const DiagnosticBuilder&) { return ExprError(); }
   inline StmtResult StmtError(const DiagnosticBuilder&) { return StmtError(); }

diff  --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h
index 82d00494b0d6..dd77cbf3b9fe 100644
--- a/clang/include/clang/Sema/ParsedTemplate.h
+++ b/clang/include/clang/Sema/ParsedTemplate.h
@@ -169,7 +169,9 @@ namespace clang {
     /// template-name.
     ParsedTemplateTy Template;
 
-    /// The kind of template that Template refers to.
+    /// The kind of template that Template refers to. If this is
+    /// TNK_Non_template, an error was encountered and diagnosed
+    /// when parsing or looking up the template name.
     TemplateNameKind Kind;
 
     /// The location of the '<' before the template argument
@@ -183,6 +185,10 @@ namespace clang {
     /// NumArgs - The number of template arguments.
     unsigned NumArgs;
 
+    /// Whether an error was encountered in the template arguments.
+    /// If so, NumArgs and the trailing argumentst are best-effort.
+    bool ArgsInvalid;
+
     /// Retrieves a pointer to the template arguments
     ParsedTemplateArgument *getTemplateArgs() {
       return getTrailingObjects<ParsedTemplateArgument>();
@@ -195,13 +201,13 @@ namespace clang {
            IdentifierInfo *Name, OverloadedOperatorKind OperatorKind,
            ParsedTemplateTy OpaqueTemplateName, TemplateNameKind TemplateKind,
            SourceLocation LAngleLoc, SourceLocation RAngleLoc,
-           ArrayRef<ParsedTemplateArgument> TemplateArgs,
+           ArrayRef<ParsedTemplateArgument> TemplateArgs, bool ArgsInvalid,
            SmallVectorImpl<TemplateIdAnnotation *> &CleanupList) {
       TemplateIdAnnotation *TemplateId = new (llvm::safe_malloc(
           totalSizeToAlloc<ParsedTemplateArgument>(TemplateArgs.size())))
           TemplateIdAnnotation(TemplateKWLoc, TemplateNameLoc, Name,
                                OperatorKind, OpaqueTemplateName, TemplateKind,
-                               LAngleLoc, RAngleLoc, TemplateArgs);
+                               LAngleLoc, RAngleLoc, TemplateArgs, ArgsInvalid);
       CleanupList.push_back(TemplateId);
       return TemplateId;
     }
@@ -213,6 +219,20 @@ namespace clang {
       this->~TemplateIdAnnotation();
       free(this);
     }
+
+    /// Determine whether this might be a type template.
+    bool mightBeType() const {
+      return Kind == TNK_Non_template ||
+             Kind == TNK_Type_template ||
+             Kind == TNK_Dependent_template_name ||
+             Kind == TNK_Undeclared_template;
+    }
+
+    bool hasInvalidName() const { return Kind == TNK_Non_template; }
+    bool hasInvalidArgs() const { return ArgsInvalid; }
+
+    bool isInvalid() const { return hasInvalidName() || hasInvalidArgs(); }
+
   private:
     TemplateIdAnnotation(const TemplateIdAnnotation &) = delete;
 
@@ -222,11 +242,12 @@ namespace clang {
                          ParsedTemplateTy OpaqueTemplateName,
                          TemplateNameKind TemplateKind,
                          SourceLocation LAngleLoc, SourceLocation RAngleLoc,
-                         ArrayRef<ParsedTemplateArgument> TemplateArgs) noexcept
+                         ArrayRef<ParsedTemplateArgument> TemplateArgs,
+                         bool ArgsInvalid) noexcept
         : TemplateKWLoc(TemplateKWLoc), TemplateNameLoc(TemplateNameLoc),
           Name(Name), Operator(OperatorKind), Template(OpaqueTemplateName),
           Kind(TemplateKind), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc),
-          NumArgs(TemplateArgs.size()) {
+          NumArgs(TemplateArgs.size()), ArgsInvalid(ArgsInvalid) {
 
       std::uninitialized_copy(TemplateArgs.begin(), TemplateArgs.end(),
                               getTemplateArgs());

diff  --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index a75965784168..06e02ed0e11c 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -1114,17 +1114,14 @@ bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks,
           break;
         }
 
+        // Put the token stream back and undo any annotations we performed
+        // after the comma. They may reflect a 
diff erent parse than the one
+        // we will actually perform at the end of the class.
+        PA.RevertAnnotations();
+
         // If what follows could be a declaration, it is a declaration.
-        if (Result != TPResult::False && Result != TPResult::Error) {
-          PA.Revert();
+        if (Result != TPResult::False && Result != TPResult::Error)
           return true;
-        }
-
-        // In the uncommon case that we decide the following tokens are part
-        // of a template argument, revert any annotations we've performed in
-        // those tokens. We're not going to look them up until we've parsed
-        // the rest of the class, and that might add more declarations.
-        PA.RevertAnnotations();
       }
 
       // Keep going. We know we're inside a template argument list now.

diff  --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index e3c784c1b61c..95706fb999c4 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3162,9 +3162,19 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
 
       // We are looking for a qualified typename.
       Token Next = NextToken();
-      if (Next.is(tok::annot_template_id) &&
-          static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
-            ->Kind == TNK_Type_template) {
+
+      TemplateIdAnnotation *TemplateId = Next.is(tok::annot_template_id)
+                                             ? takeTemplateIdAnnotation(Next)
+                                             : nullptr;
+      if (TemplateId && TemplateId->hasInvalidName()) {
+        // We found something like 'T::U<Args> x', but U is not a template.
+        // Assume it was supposed to be a type.
+        DS.SetTypeSpecError();
+        ConsumeAnnotationToken();
+        break;
+      }
+
+      if (TemplateId && TemplateId->Kind == TNK_Type_template) {
         // We have a qualified template-id, e.g., N::A<int>
 
         // If this would be a valid constructor declaration with template
@@ -3174,7 +3184,6 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
         //
         // To improve diagnostics for this case, parse the declaration as a
         // constructor (and reject the extra template arguments later).
-        TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next);
         if ((DSContext == DeclSpecContext::DSC_top_level ||
              DSContext == DeclSpecContext::DSC_class) &&
             TemplateId->Name &&
@@ -3195,9 +3204,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
         continue;
       }
 
-      if (Next.is(tok::annot_template_id) &&
-          static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
-            ->Kind == TNK_Concept_template &&
+      if (TemplateId && TemplateId->Kind == TNK_Concept_template &&
           GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype)) {
         DS.getTypeSpecScope() = SS;
         // This is a qualified placeholder-specifier, e.g., ::C<int> auto ...
@@ -3459,7 +3466,18 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
       // type-name or placeholder-specifier
     case tok::annot_template_id: {
       TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+
+      if (TemplateId->hasInvalidName()) {
+        DS.SetTypeSpecError();
+        break;
+      }
+
       if (TemplateId->Kind == TNK_Concept_template) {
+        // If we've already diagnosed that this type-constraint has invalid
+        // arguemnts, drop it and just form 'auto' or 'decltype(auto)'.
+        if (TemplateId->hasInvalidArgs())
+          TemplateId = nullptr;
+
         if (NextToken().is(tok::identifier)) {
           Diag(Loc, diag::err_placeholder_expected_auto_or_decltype_auto)
               << FixItHint::CreateInsertion(NextToken().getLocation(), "auto");
@@ -5197,14 +5215,30 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
 
     // placeholder-type-specifier
   case tok::annot_template_id: {
+    TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+    if (TemplateId->hasInvalidName())
+      return true;
+    // FIXME: What about type templates that have only been annotated as
+    // annot_template_id, not as annot_typename?
     return isTypeConstraintAnnotation() &&
-        (NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype));
+           (NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype));
   }
-  case tok::annot_cxxscope:
+
+  case tok::annot_cxxscope: {
+    TemplateIdAnnotation *TemplateId =
+        NextToken().is(tok::annot_template_id)
+            ? takeTemplateIdAnnotation(NextToken())
+            : nullptr;
+    if (TemplateId && TemplateId->hasInvalidName())
+      return true;
+    // FIXME: What about type templates that have only been annotated as
+    // annot_template_id, not as annot_typename?
     if (NextToken().is(tok::identifier) && TryAnnotateTypeConstraint())
       return true;
     return isTypeConstraintAnnotation() &&
         GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype);
+  }
+
   case tok::kw___declspec:
   case tok::kw___cdecl:
   case tok::kw___stdcall:

diff  --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 86a9a8208b2e..98918efaf295 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1147,9 +1147,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
   // Check whether we have a template-id that names a type.
   if (Tok.is(tok::annot_template_id)) {
     TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
-    if (TemplateId->Kind == TNK_Type_template ||
-        TemplateId->Kind == TNK_Dependent_template_name ||
-        TemplateId->Kind == TNK_Undeclared_template) {
+    if (TemplateId->mightBeType()) {
       AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true);
 
       assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
@@ -1176,7 +1174,9 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
   if (Tok.is(tok::less)) {
     // It looks the user intended to write a template-id here, but the
     // template-name was wrong. Try to fix that.
-    TemplateNameKind TNK = TNK_Type_template;
+    // FIXME: Invoke ParseOptionalCXXScopeSpecifier in a "'template' is neither
+    // required nor permitted" mode, and do this there.
+    TemplateNameKind TNK = TNK_Non_template;
     TemplateTy Template;
     if (!Actions.DiagnoseUnknownTemplateName(*Id, IdLoc, getCurScope(),
                                              &SS, Template, TNK)) {
@@ -1184,14 +1184,6 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
         << Id;
     }
 
-    if (!Template) {
-      TemplateArgList TemplateArgs;
-      SourceLocation LAngleLoc, RAngleLoc;
-      ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs,
-                                       RAngleLoc);
-      return true;
-    }
-
     // Form the template name
     UnqualifiedId TemplateName;
     TemplateName.setIdentifier(Id, IdLoc);
@@ -1200,7 +1192,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
     if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
                                 TemplateName))
       return true;
-    if (TNK == TNK_Type_template || TNK == TNK_Dependent_template_name)
+    if (Tok.is(tok::annot_template_id) &&
+        takeTemplateIdAnnotation(Tok)->mightBeType())
       AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true);
 
     // If we didn't end up with a typename token, there's nothing more we
@@ -1630,9 +1623,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
     NameLoc = ConsumeAnnotationToken();
 
     if (TemplateId->Kind == TNK_Undeclared_template) {
-      // Try to resolve the template name to a type template.
-      Actions.ActOnUndeclaredTypeTemplateName(getCurScope(), TemplateId->Template,
-                                              TemplateId->Kind, NameLoc, Name);
+      // Try to resolve the template name to a type template. May update Kind.
+      Actions.ActOnUndeclaredTypeTemplateName(
+          getCurScope(), TemplateId->Template, TemplateId->Kind, NameLoc, Name);
       if (TemplateId->Kind == TNK_Undeclared_template) {
         RecoverFromUndeclaredTemplateName(
             Name, NameLoc,
@@ -1641,10 +1634,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
       }
     }
 
-    if (TemplateId && TemplateId->Kind != TNK_Type_template &&
-        TemplateId->Kind != TNK_Dependent_template_name) {
+    if (TemplateId && !TemplateId->mightBeType()) {
       // The template-name in the simple-template-id refers to
-      // something other than a class template. Give an appropriate
+      // something other than a type template. Give an appropriate
       // error message and skip to the ';'.
       SourceRange Range(NameLoc);
       if (SS.isNotEmpty())
@@ -1816,7 +1808,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
     // or explicit instantiation.
     ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
                                        TemplateId->NumArgs);
-    if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
+    if (TemplateId->isInvalid()) {
+      // Can't build the declaration.
+    } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
         TUK == Sema::TUK_Declaration) {
       // This is an explicit instantiation of a class template.
       ProhibitAttributes(attrs);
@@ -3533,9 +3527,7 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
     TemplateIdAnnotation *TemplateId = Tok.is(tok::annot_template_id)
                                            ? takeTemplateIdAnnotation(Tok)
                                            : nullptr;
-    if (TemplateId && (TemplateId->Kind == TNK_Type_template ||
-                       TemplateId->Kind == TNK_Dependent_template_name ||
-                       TemplateId->Kind == TNK_Undeclared_template)) {
+    if (TemplateId && TemplateId->mightBeType()) {
       AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true);
       assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
       TemplateTypeTy = getTypeAnnotation(Tok);

diff  --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index a0b97ea7514d..1f0d37154328 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -134,7 +134,7 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
 /// \param MayBePseudoDestructor When non-NULL, points to a flag that
 /// indicates whether this nested-name-specifier may be part of a
 /// pseudo-destructor name. In this case, the flag will be set false
-/// if we don't actually end up parsing a destructor name. Moreorover,
+/// if we don't actually end up parsing a destructor name. Moreover,
 /// if we do end up determining that we are parsing a destructor name,
 /// the last component of the nested-name-specifier is not parsed as
 /// part of the scope specifier.
@@ -356,7 +356,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
       ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
                                          TemplateId->NumArgs);
 
-      if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
+      if (TemplateId->isInvalid() ||
+          Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
                                               SS,
                                               TemplateId->TemplateKWLoc,
                                               TemplateId->Template,
@@ -512,7 +513,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
       if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) &&
           (IsTypename || isTemplateArgumentList(1) == TPResult::True)) {
         // If we had errors before, ObjectType can be dependent even without any
-        // templates, do not report missing template keyword in that case.
+        // templates. Do not report missing template keyword in that case.
         if (!ObjectHadErrors) {
           // We have something like t::getAs<T>, where getAs is a
           // member of an unknown specialization. However, this will only
@@ -1738,8 +1739,11 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
     assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
     CCLoc = ConsumeToken();
   } else if (Tok.is(tok::annot_template_id)) {
-    FirstTypeName.setTemplateId(
-                              (TemplateIdAnnotation *)Tok.getAnnotationValue());
+    TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+    // FIXME: Carry on and build an AST representation for tooling.
+    if (TemplateId->isInvalid())
+      return ExprError();
+    FirstTypeName.setTemplateId(TemplateId);
     ConsumeAnnotationToken();
     assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
     CCLoc = ConsumeToken();
@@ -2306,8 +2310,6 @@ bool Parser::ParseUnqualifiedIdTemplateId(
       TNK = Actions.ActOnDependentTemplateName(
           getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
           Template, /*AllowInjectedClassName*/ true);
-      if (TNK == TNK_Non_template)
-        return true;
     } else {
       bool MemberOfUnknownSpecialization;
       TNK = Actions.isTemplateName(getCurScope(), SS,
@@ -2347,8 +2349,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(
         TNK = Actions.ActOnDependentTemplateName(
             getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
             Template, /*AllowInjectedClassName*/ true);
-        if (TNK == TNK_Non_template)
-          return true;
+      } else if (TNK == TNK_Non_template) {
+        return false;
       }
     }
     break;
@@ -2361,6 +2363,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(
                                  TemplateName, ObjectType,
                                  EnteringContext, Template,
                                  MemberOfUnknownSpecialization);
+    if (TNK == TNK_Non_template)
+      return false;
     break;
   }
 
@@ -2372,8 +2376,6 @@ bool Parser::ParseUnqualifiedIdTemplateId(
       TNK = Actions.ActOnDependentTemplateName(
           getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
           EnteringContext, Template, /*AllowInjectedClassName*/ true);
-      if (TNK == TNK_Non_template)
-        return true;
     } else {
       TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(),
                                    TemplateName, ObjectType,
@@ -2383,7 +2385,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(
       if (TNK == TNK_Non_template && !Id.DestructorName.get()) {
         Diag(NameLoc, diag::err_destructor_template_id)
           << Name << SS.getRange();
-        return true;
+        // Carry on to parse the template arguments before bailing out.
       }
     }
     break;
@@ -2393,9 +2395,6 @@ bool Parser::ParseUnqualifiedIdTemplateId(
     return false;
   }
 
-  if (TNK == TNK_Non_template)
-    return false;
-
   // Parse the enclosed template argument list.
   SourceLocation LAngleLoc, RAngleLoc;
   TemplateArgList TemplateArgs;
@@ -2403,6 +2402,10 @@ bool Parser::ParseUnqualifiedIdTemplateId(
                                        RAngleLoc))
     return true;
 
+  // If this is a non-template, we already issued a diagnostic.
+  if (TNK == TNK_Non_template)
+    return true;
+
   if (Id.getKind() == UnqualifiedIdKind::IK_Identifier ||
       Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId ||
       Id.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) {
@@ -2420,7 +2423,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(
 
     TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create(
         TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK,
-        LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds);
+        LAngleLoc, RAngleLoc, TemplateArgs, /*ArgsInvalid*/false, TemplateIds);
 
     Id.setTemplateId(TemplateId);
     return false;
@@ -2800,6 +2803,13 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
   if (Tok.is(tok::annot_template_id)) {
     TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
 
+    // FIXME: Consider passing invalid template-ids on to callers; they may
+    // be able to recover better than we can.
+    if (TemplateId->isInvalid()) {
+      ConsumeAnnotationToken();
+      return true;
+    }
+
     // If the template-name names the current class, then this is a constructor
     if (AllowConstructorName && TemplateId->Name &&
         Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS)) {
@@ -3541,6 +3551,8 @@ ExprResult Parser::ParseRequiresExpression() {
             } else {
               TemplateId = takeTemplateIdAnnotation(Tok);
               ConsumeAnnotationToken();
+              if (TemplateId->isInvalid())
+                break;
             }
 
             if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS,

diff  --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 802fe35d4f62..9b49a27b731e 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1220,16 +1220,17 @@ Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
 
     if (Invalid) {
       // Try to find the closing '>'.
+      // FIXME: Handle `>>`, `>>>`.
       if (ConsumeLastToken)
         SkipUntil(tok::greater, StopAtSemi);
       else
         SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
-      return true;
     }
   }
 
   return ParseGreaterThanInTemplateList(RAngleLoc, ConsumeLastToken,
-                                        /*ObjCGenericList=*/false);
+                                        /*ObjCGenericList=*/false) ||
+         Invalid;
 }
 
 /// Replace the tokens that form a simple-template-id with an
@@ -1280,12 +1281,13 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
                                      bool AllowTypeAnnotation,
                                      bool TypeConstraint) {
   assert(getLangOpts().CPlusPlus && "Can only annotate template-ids in C++");
-  assert(Template && (Tok.is(tok::less) || TypeConstraint) &&
+  assert((Tok.is(tok::less) || TypeConstraint) &&
          "Parser isn't at the beginning of a template-id");
   assert(!(TypeConstraint && AllowTypeAnnotation) && "type-constraint can't be "
                                                      "a type annotation");
   assert((!TypeConstraint || TNK == TNK_Concept_template) && "type-constraint "
          "must accompany a concept name");
+  assert((Template || TNK == TNK_Non_template) && "missing template name");
 
   // Consume the template-name.
   SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin();
@@ -1293,40 +1295,31 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
   // Parse the enclosed template argument list.
   SourceLocation LAngleLoc, RAngleLoc;
   TemplateArgList TemplateArgs;
+  bool ArgsInvalid = false;
   if (!TypeConstraint || Tok.is(tok::less)) {
-    bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
-                                                    TemplateArgs,
-                                                    RAngleLoc);
-
-    if (Invalid) {
-      // If we failed to parse the template ID but skipped ahead to a >, we're not
-      // going to be able to form a token annotation.  Eat the '>' if present.
-      TryConsumeToken(tok::greater);
-      // FIXME: Annotate the token stream so we don't produce the same errors
-      // again if we're doing this annotation as part of a tentative parse.
+    ArgsInvalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
+                                                   TemplateArgs, RAngleLoc);
+    // If we couldn't recover from invalid arguments, don't form an annotation
+    // token -- we don't know how much to annotate.
+    // FIXME: This can lead to duplicate diagnostics if we retry parsing this
+    // template-id in another context. Try to annotate anyway?
+    if (RAngleLoc.isInvalid())
       return true;
-    }
   }
 
   ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs);
 
   // Build the annotation token.
   if (TNK == TNK_Type_template && AllowTypeAnnotation) {
-    TypeResult Type = Actions.ActOnTemplateIdType(
-        getCurScope(), SS, TemplateKWLoc, Template, TemplateName.Identifier,
-        TemplateNameLoc, LAngleLoc, TemplateArgsPtr, RAngleLoc);
-    if (Type.isInvalid()) {
-      // If we failed to parse the template ID but skipped ahead to a >, we're
-      // not going to be able to form a token annotation.  Eat the '>' if
-      // present.
-      TryConsumeToken(tok::greater);
-      // FIXME: Annotate the token stream so we don't produce the same errors
-      // again if we're doing this annotation as part of a tentative parse.
-      return true;
-    }
+    TypeResult Type = ArgsInvalid
+                          ? TypeError()
+                          : Actions.ActOnTemplateIdType(
+                                getCurScope(), SS, TemplateKWLoc, Template,
+                                TemplateName.Identifier, TemplateNameLoc,
+                                LAngleLoc, TemplateArgsPtr, RAngleLoc);
 
     Tok.setKind(tok::annot_typename);
-    setTypeAnnotation(Tok, Type.get());
+    setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get());
     if (SS.isNotEmpty())
       Tok.setLocation(SS.getBeginLoc());
     else if (TemplateKWLoc.isValid())
@@ -1350,7 +1343,7 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
 
     TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create(
         TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK,
-        LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds);
+        LAngleLoc, RAngleLoc, TemplateArgs, ArgsInvalid, TemplateIds);
 
     Tok.setAnnotationValue(TemplateId);
     if (TemplateKWLoc.isValid())
@@ -1386,26 +1379,21 @@ void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
   assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens");
 
   TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
-  assert((TemplateId->Kind == TNK_Type_template ||
-          TemplateId->Kind == TNK_Dependent_template_name ||
-          TemplateId->Kind == TNK_Undeclared_template) &&
+  assert(TemplateId->mightBeType() &&
          "Only works for type and dependent templates");
 
   ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
                                      TemplateId->NumArgs);
 
-  TypeResult Type
-    = Actions.ActOnTemplateIdType(getCurScope(),
-                                  SS,
-                                  TemplateId->TemplateKWLoc,
-                                  TemplateId->Template,
-                                  TemplateId->Name,
-                                  TemplateId->TemplateNameLoc,
-                                  TemplateId->LAngleLoc,
-                                  TemplateArgsPtr,
-                                  TemplateId->RAngleLoc,
-                                  /*IsCtorOrDtorName*/false,
-                                  IsClassName);
+  TypeResult Type =
+      TemplateId->isInvalid()
+          ? TypeError()
+          : Actions.ActOnTemplateIdType(
+                getCurScope(), SS, TemplateId->TemplateKWLoc,
+                TemplateId->Template, TemplateId->Name,
+                TemplateId->TemplateNameLoc, TemplateId->LAngleLoc,
+                TemplateArgsPtr, TemplateId->RAngleLoc,
+                /*IsCtorOrDtorName*/ false, IsClassName);
   // Create the new "type" annotation token.
   Tok.setKind(tok::annot_typename);
   setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get());
@@ -1420,6 +1408,7 @@ 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);
 }
 
@@ -1572,10 +1561,8 @@ Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) {
     if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
       Arg = Actions.ActOnPackExpansion(Arg, EllipsisLoc);
 
-    if (Arg.isInvalid()) {
-      SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch);
+    if (Arg.isInvalid())
       return true;
-    }
 
     // Save this template argument.
     TemplateArgs.push_back(Arg);

diff  --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index bca7d021da0a..0c3824c3c984 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -984,10 +984,16 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
                                         NextToken().is(tok::kw_operator)))) &&
       mayHaveIdentifier) {
     // declarator-id
-    if (Tok.is(tok::annot_cxxscope))
+    if (Tok.is(tok::annot_cxxscope)) {
+      CXXScopeSpec SS;
+      Actions.RestoreNestedNameSpecifierAnnotation(
+          Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS);
+      if (SS.isInvalid())
+        return TPResult::Error;
       ConsumeAnnotationToken();
-    else if (Tok.is(tok::identifier))
+    } else if (Tok.is(tok::identifier)) {
       TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo());
+    }
     if (Tok.is(tok::kw_operator)) {
       if (TryParseOperatorId() == TPResult::Error)
         return TPResult::Error;
@@ -1547,6 +1553,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
       *InvalidAsDeclSpec = NextToken().is(tok::l_paren);
       return TPResult::Ambiguous;
     }
+    if (TemplateId->hasInvalidName())
+      return TPResult::Error;
     if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/0))
       return TPResult::True;
     if (TemplateId->Kind != TNK_Type_template)
@@ -1566,6 +1574,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
           NextToken().is(tok::annot_template_id)) {
         TemplateIdAnnotation *TemplateId =
             takeTemplateIdAnnotation(NextToken());
+        if (TemplateId->hasInvalidName())
+          return TPResult::Error;
         if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/1))
           return TPResult::True;
       }
@@ -2012,17 +2022,14 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
     //   (a) the previous parameter did, and
     //   (b) this must be the first declaration of the function, so we can't
     //       inherit any default arguments from elsewhere.
-    // If we see an ')', then we've reached the end of a
-    // parameter-declaration-clause, and the last param is missing its default
-    // argument.
+    // FIXME: If we reach a ')' without consuming any '>'s, then this must
+    // also be a function parameter (that's missing its default argument).
     if (VersusTemplateArgument)
-      return Tok.isOneOf(tok::equal, tok::r_paren) ? TPResult::True
-                                                   : TPResult::False;
+      return Tok.is(tok::equal) ? TPResult::True : TPResult::False;
 
     if (Tok.is(tok::equal)) {
       // '=' assignment-expression
       // Parse through assignment-expression.
-      // FIXME: assignment-expression may contain an unparenthesized comma.
       if (!SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch))
         return TPResult::Error;
     }

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 0a63ac2d5e1b..47b5de320ebf 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1878,6 +1878,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
                                      Tok.getLocation());
     } else if (Tok.is(tok::annot_template_id)) {
       TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+      if (TemplateId->isInvalid())
+        return true;
       if (TemplateId->Kind != TNK_Type_template &&
           TemplateId->Kind != TNK_Dependent_template_name &&
           TemplateId->Kind != TNK_Undeclared_template) {

diff  --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 2da300a62dc3..ae4a78a4556d 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -30,6 +30,9 @@ using namespace clang;
 
 void UnqualifiedId::setTemplateId(TemplateIdAnnotation *TemplateId) {
   assert(TemplateId && "NULL template-id annotation?");
+  assert(!TemplateId->isInvalid() &&
+         "should not convert invalid template-ids to unqualified-ids");
+
   Kind = UnqualifiedIdKind::IK_TemplateId;
   this->TemplateId = TemplateId;
   StartLocation = TemplateId->TemplateNameLoc;
@@ -38,6 +41,9 @@ void UnqualifiedId::setTemplateId(TemplateIdAnnotation *TemplateId) {
 
 void UnqualifiedId::setConstructorTemplateId(TemplateIdAnnotation *TemplateId) {
   assert(TemplateId && "NULL template-id annotation?");
+  assert(!TemplateId->isInvalid() &&
+         "should not convert invalid template-ids to unqualified-ids");
+
   Kind = UnqualifiedIdKind::IK_ConstructorTemplateId;
   this->TemplateId = TemplateId;
   StartLocation = TemplateId->TemplateNameLoc;

diff  --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp
index a5edc32fa48c..fad91c6a584d 100644
--- a/clang/test/CXX/drs/dr3xx.cpp
+++ b/clang/test/CXX/drs/dr3xx.cpp
@@ -123,7 +123,7 @@ namespace dr305 { // dr305: no
     template<typename T> using T2 = T;
   };
   void k(Z *z) {
-    z->~T1<int>(); // expected-error {{no member named 'T1' in 'dr305::Z'}} expected-error +{{}}
+    z->~T1<int>(); // expected-error {{no member named 'T1' in 'dr305::Z'}}
     z->~T2<int>(); // expected-error {{no member named '~int'}}
     z->~T2<Z>();
   }

diff  --git a/clang/test/CXX/temp/p3.cpp b/clang/test/CXX/temp/p3.cpp
index e9fd8a3090e8..90f6397857ae 100644
--- a/clang/test/CXX/temp/p3.cpp
+++ b/clang/test/CXX/temp/p3.cpp
@@ -7,9 +7,7 @@ template<typename T> struct S {
 template<typename T> int S<T>::a, S<T>::b; // expected-error {{can only declare a single entity}}
 
 template<typename T> struct A { static A a; } A<T>::a; // expected-error {{expected ';' after struct}} \
-                                                          expected-error {{use of undeclared identifier 'T'}} \
-                                                          expected-error {{no member named 'a'}} \
-                                                          expected-warning {{extra qualification}}
+                                                          expected-error {{use of undeclared identifier 'T'}}
 
 template<typename T> struct B { } f(); // expected-error {{expected ';' after struct}} \
                                           expected-error {{requires a type specifier}}

diff  --git a/clang/test/Parser/cxx-ambig-init-templ.cpp b/clang/test/Parser/cxx-ambig-init-templ.cpp
index a64760f3f447..eb90c0e02b8f 100644
--- a/clang/test/Parser/cxx-ambig-init-templ.cpp
+++ b/clang/test/Parser/cxx-ambig-init-templ.cpp
@@ -2,6 +2,7 @@
 
 template<int> struct c { c(int) = delete; typedef void val; operator int() const; };
 
+int f;
 int val;
 int foobar;
 struct S {
@@ -29,6 +30,12 @@ struct S {
     int k6 = a < b, c < d > (n) // expected-error {{undeclared identifier 'n'}}
   );
 
+  static void f1b(
+    int k6 = a < b, c < d > (f)
+  );
+  using f1b_T = decltype(f1b(0)); // only one parameter, because second param
+                                  // would be missing its default argument
+
   void f2a(
     // T3<int> here is a parameter type, so must be declared before it is used.
     int k1 = c < b, T3 < int > x = 0 // expected-error {{no template named 'T3'}}
@@ -56,14 +63,21 @@ struct S {
     int missing_default // expected-error {{missing default argument on parameter}}
   );
 
+  // FIXME: We should ideally disambiguate this as two parameters.
   void f6(
-    int k = b < c,
-    unsigned int (missing_default) // expected-error {{missing default argument on parameter}}
+    int k = b < c, // expected-error {{unexpected end of default argument}}
+    unsigned int (missing_default)
   );
 
-  template<int, int=0> struct a { static const int val = 0; operator int(); }; // expected-note {{here}}
+  template<int, int = 0> struct a { // expected-note {{here}}
+    a();
+    a(int);
+    static const int val = 0;
+    operator int();
+  };
   static const int b = 0, c = 1, d = 2, goobar = 3;
   template<int, typename> struct e { operator int(); };
+  static const int f = 0;
 
   int mp1 = 0 < 1,
       a<b<c,b<c>::*mp2,

diff  --git a/clang/test/Parser/cxx-class.cpp b/clang/test/Parser/cxx-class.cpp
index e73c8740a3ef..ec56d4bee8dd 100644
--- a/clang/test/Parser/cxx-class.cpp
+++ b/clang/test/Parser/cxx-class.cpp
@@ -211,9 +211,9 @@ namespace DtorErrors {
   };
 
   struct T {};
-  T t1 = t1.T::~T<int>; // expected-error {{destructor name 'T' does not refer to a template}} expected-error {{expected '(' for function-style cast or type construction}} expected-error {{expected expression}}
+  T t1 = t1.T::~T<int>; // expected-error {{destructor name 'T' does not refer to a template}}
   // Emit the same diagnostic as for the previous case, plus something about ~.
-  T t2 = t2.~T::T<int>; // expected-error {{'~' in destructor name should be after nested name specifier}} expected-error {{destructor name 'T' does not refer to a template}} expected-error {{expected '(' for function-style cast or type construction}} expected-error {{expected expression}}
+  T t2 = t2.~T::T<int>; // expected-error {{'~' in destructor name should be after nested name specifier}} expected-error {{destructor name 'T' does not refer to a template}}
 }
 
 namespace BadFriend {

diff  --git a/clang/test/Parser/cxx-member-initializers.cpp b/clang/test/Parser/cxx-member-initializers.cpp
index 8e52adbbfd33..bd74af1946b6 100644
--- a/clang/test/Parser/cxx-member-initializers.cpp
+++ b/clang/test/Parser/cxx-member-initializers.cpp
@@ -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}}
+  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 '>'}}
 };

diff  --git a/clang/test/Parser/cxx-template-decl.cpp b/clang/test/Parser/cxx-template-decl.cpp
index 0d52ad8fb50f..fffe75255c24 100644
--- a/clang/test/Parser/cxx-template-decl.cpp
+++ b/clang/test/Parser/cxx-template-decl.cpp
@@ -235,11 +235,11 @@ struct base { };
 
 struct t1 : base<int,
   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 '>'}}
 // expected-error at -1 {{expected '{' after base class list}}
 
 }

diff  --git a/clang/test/Parser/eof2.cpp b/clang/test/Parser/eof2.cpp
index 6a8b862e6099..44e2b58bb6d1 100644
--- a/clang/test/Parser/eof2.cpp
+++ b/clang/test/Parser/eof2.cpp
@@ -1,11 +1,12 @@
 // RUN: not %clang_cc1 %s -fsyntax-only 2>&1 | FileCheck %s
 
 // CHECK: error: expected expression
+// CHECK: error: expected '>'
 // CHECK: error: expected member name or ';' after declaration specifiers
 // CHECK: error: expected '}'
 // CHECK: note: to match this '{'
 // CHECK: error: expected ';' after class
-// CHECK: 4 errors generated.
+// CHECK: 5 errors generated.
 
 // Do not add anything to the end of this file.  This requires the whitespace
 // plus EOF after the '<' token.

diff  --git a/clang/test/Parser/recovery.cpp b/clang/test/Parser/recovery.cpp
index 2d5b518c691f..28c8817ea027 100644
--- a/clang/test/Parser/recovery.cpp
+++ b/clang/test/Parser/recovery.cpp
@@ -133,7 +133,10 @@ Foo<int> missingSemiBeforeFunctionReturningTemplateId2();
 namespace PR17084 {
 enum class EnumID {};
 template <typename> struct TempID;
-template <> struct TempID<BadType> : BadType, EnumID::Garbage; // expected-error{{use of undeclared identifier 'BadType'}}
+template <> struct TempID<BadType> // expected-error{{use of undeclared identifier 'BadType'}}
+  : BadType, // expected-error {{expected class name}}
+    EnumID::Garbage // expected-error {{expected class name}}
+  ; // expected-error at -1 {{expected '{' after base class list}}
 }
 
 namespace pr15133 {

diff  --git a/clang/test/SemaCXX/PR20705.cpp b/clang/test/SemaCXX/PR20705.cpp
index be2676e56356..6b558b893d24 100644
--- a/clang/test/SemaCXX/PR20705.cpp
+++ b/clang/test/SemaCXX/PR20705.cpp
@@ -4,8 +4,7 @@ template <typename T>
 struct X {};
 auto b = []() {
   struct S {
-    static typename X<decltype(int)>::type Run(){};
-    // expected-error at -1 4{{}}
+    static typename X<decltype(int)>::type Run(){}; // expected-error {{expected '('}}
   };
   return 5;
 }();
@@ -16,6 +15,5 @@ class PC {
 
 template <typename T>
 class P {
-  static typename PC<T, Invalid>::Type Foo();
-  // expected-error at -1 4{{}}
+  static typename PC<T, Invalid>::Type Foo(); // expected-error {{undeclared identifier 'Invalid'}}
 };

diff  --git a/clang/test/SemaCXX/PR9459.cpp b/clang/test/SemaCXX/PR9459.cpp
index 2b96f34e8a72..010e228f6762 100644
--- a/clang/test/SemaCXX/PR9459.cpp
+++ b/clang/test/SemaCXX/PR9459.cpp
@@ -2,6 +2,6 @@
 
 // Don't crash.
 
-template<typename>struct ae_same;
+template<typename>struct ae_same; // expected-note {{}}
 template<typename>struct ts{}ap() // expected-error {{expected ';' after struct}} expected-error {{requires a type specifier}}
-{ts<a>::ap<ae_same<int>::&ae_same<>>::p(a); }; // expected-error {{use of undeclared identifier 'a'}}
+{ts<a>::ap<ae_same<int>::&ae_same<>>::p(a); }; // expected-error {{use of undeclared identifier 'a'}} expected-error 5{{}}

diff  --git a/clang/test/SemaCXX/builtins.cpp b/clang/test/SemaCXX/builtins.cpp
index fbe2c457dad9..d08e673c04b7 100644
--- a/clang/test/SemaCXX/builtins.cpp
+++ b/clang/test/SemaCXX/builtins.cpp
@@ -14,8 +14,7 @@ template<int (*Compare)(const char *s1, const char *s2)>
 int equal(const char *s1, const char *s2) {
   return Compare(s1, s2) == 0;
 }
-// FIXME: Our error recovery here sucks
-template int equal<&__builtin_strcmp>(const char*, const char*); // expected-error {{builtin functions must be directly called}} expected-error {{expected unqualified-id}} expected-error {{expected ')'}} expected-note {{to match this '('}}
+template int equal<&__builtin_strcmp>(const char*, const char*); // expected-error {{builtin functions must be directly called}}
 
 // PR13195
 void f2() {

diff  --git a/clang/test/SemaCXX/implicit-exception-spec.cpp b/clang/test/SemaCXX/implicit-exception-spec.cpp
index 19d645d6d3d1..483ed3ca9440 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}}
+      T(int = ExceptionIf<noexcept(Default())::f()); // expected-error {{call to implicitly-deleted default constructor}} expected-error {{expected '>'}}
     } 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 2996a7cdb48c..7743717c85fc 100644
--- a/clang/test/SemaCXX/injected-class-name-crash.cpp
+++ b/clang/test/SemaCXX/injected-class-name-crash.cpp
@@ -3,7 +3,7 @@
 template <class T>
 struct X : public Foo<Bar { // expected-error {{unknown template name 'Foo'}} expected-error {{use of undeclared identifier 'Bar'}}
   X();
-}; // expected-error {{expected '{' after base class list}}
+}; // expected-error {{expected '>'}} expected-error {{expected '{' after base class list}}
 
 
 template <class T>

diff  --git a/clang/test/SemaCXX/invalid-member-expr.cpp b/clang/test/SemaCXX/invalid-member-expr.cpp
index e0b40794a0ce..6ef33ea575a1 100644
--- a/clang/test/SemaCXX/invalid-member-expr.cpp
+++ b/clang/test/SemaCXX/invalid-member-expr.cpp
@@ -54,10 +54,7 @@ namespace test3 {
 namespace rdar11293995 {
 
 struct Length {
-  // FIXME: We try to annotate the template-id here during tentative parsing,
-  // and fail, then try again during the actual parse. This results in the same
-  // diagnostic being produced twice. :(
-  explicit Length(PassRefPtr<CalculationValue>); // expected-error 2{{undeclared identifier 'CalculationValue'}}
+  explicit Length(PassRefPtr<CalculationValue>); // expected-error {{undeclared identifier 'CalculationValue'}}
 };
 
 struct LengthSize {

diff  --git a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
index b2350c900e63..008a9c317fe9 100644
--- a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
+++ b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
@@ -50,7 +50,7 @@ template <typename T = Bar<Weber>>  // expected-error {{use of undeclared identi
 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 ',' or '>' in template-parameter-list}} expected-error {{expected '>'}}
 // expected-warning at -1 {{does not declare anything}}
 typedef int Weber;
 }

diff  --git a/clang/test/SemaTemplate/temp_arg.cpp b/clang/test/SemaTemplate/temp_arg.cpp
index df5bf875f3be..72c172f001bd 100644
--- a/clang/test/SemaTemplate/temp_arg.cpp
+++ b/clang/test/SemaTemplate/temp_arg.cpp
@@ -15,6 +15,6 @@ A a4; // expected-error{{use of class template 'A' requires template arguments}}
 namespace test0 {
   template <class t> class foo {};
   template <class t> class bar {
-    bar(::test0::foo<tee> *ptr) {} // FIXME(redundant): expected-error 2 {{use of undeclared identifier 'tee'}}
+    bar(::test0::foo<tee> *ptr) {} // expected-error {{use of undeclared identifier 'tee'}}
   };
 }

diff  --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
index 253ad7bc5606..fdcc5006f589 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
@@ -102,7 +102,7 @@ namespace PtrMem {
   static_assert(!is_same<Ab, Abce>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
   static_assert(!is_same<Ab, Abde>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
   static_assert(!is_same<Abce, Abde>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}}
-  static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
+  static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
 
   using Ae = A<int E::*, e>;
   using Ae = A<int E::*, &E::e>;
@@ -111,7 +111,7 @@ namespace PtrMem {
   static_assert(!is_same<Ae, Aecb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
   static_assert(!is_same<Ae, Aedb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
   static_assert(!is_same<Aecb, Aedb>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}}
-  static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
+  static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
 
   using An = A<int E::*, nullptr>;
   using A0 = A<int E::*, (int E::*)0>;


        


More information about the cfe-commits mailing list