r335699 - Diagnose missing 'template' keywords in contexts where a comma is not a

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 26 18:32:04 PDT 2018


Author: rsmith
Date: Tue Jun 26 18:32:04 2018
New Revision: 335699

URL: http://llvm.org/viewvc/llvm-project?rev=335699&view=rev
Log:
Diagnose missing 'template' keywords in contexts where a comma is not a
binary operator.

Factor out the checking for a comma within potential angle brackets and
also call it from contexts where we parse a comma-separated list of
arguments or initializers.

Modified:
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseExpr.cpp
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/lib/Parse/ParseTemplate.cpp
    cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=335699&r1=335698&r2=335699&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Tue Jun 26 18:32:04 2018
@@ -1607,6 +1607,14 @@ private:
   }
 
   bool diagnoseUnknownTemplateId(ExprResult TemplateName, SourceLocation Less);
+  void checkPotentialAngleBracket(ExprResult &PotentialTemplateName);
+  bool checkPotentialAngleBracketDelimiter(const AngleBracketTracker::Loc &,
+                                           const Token &OpToken);
+  bool checkPotentialAngleBracketDelimiter(const Token &OpToken) {
+    if (auto *Info = AngleBrackets.getCurrent(*this))
+      return checkPotentialAngleBracketDelimiter(*Info, OpToken);
+    return false;
+  }
 
   ExprResult ParsePostfixExpressionSuffix(ExprResult LHS);
   ExprResult ParseUnaryExprOrTypeTraitExpression();

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=335699&r1=335698&r2=335699&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Tue Jun 26 18:32:04 2018
@@ -1830,23 +1830,19 @@ public:
   getTemplateNameKindForDiagnostics(TemplateName Name);
 
   /// Determine whether it's plausible that E was intended to be a
-  /// template-name. Updates E to denote the template name expression.
-  bool mightBeIntendedToBeTemplateName(ExprResult &ER, bool &Dependent) {
-    if (!getLangOpts().CPlusPlus || ER.isInvalid())
+  /// template-name.
+  bool mightBeIntendedToBeTemplateName(ExprResult E, bool &Dependent) {
+    if (!getLangOpts().CPlusPlus || E.isInvalid())
       return false;
-    Expr *E = ER.get()->IgnoreImplicit();
-    while (auto *BO = dyn_cast<BinaryOperator>(E))
-      E = BO->getRHS()->IgnoreImplicit();
-    ER = E;
     Dependent = false;
-    if (auto *DRE = dyn_cast<DeclRefExpr>(E))
+    if (auto *DRE = dyn_cast<DeclRefExpr>(E.get()))
       return !DRE->hasExplicitTemplateArgs();
-    if (auto *ME = dyn_cast<MemberExpr>(E))
+    if (auto *ME = dyn_cast<MemberExpr>(E.get()))
       return !ME->hasExplicitTemplateArgs();
     Dependent = true;
-    if (auto *DSDRE = dyn_cast<DependentScopeDeclRefExpr>(E))
+    if (auto *DSDRE = dyn_cast<DependentScopeDeclRefExpr>(E.get()))
       return !DSDRE->hasExplicitTemplateArgs();
-    if (auto *DSME = dyn_cast<CXXDependentScopeMemberExpr>(E))
+    if (auto *DSME = dyn_cast<CXXDependentScopeMemberExpr>(E.get()))
       return !DSME->hasExplicitTemplateArgs();
     // Any additional cases recognized here should also be handled by
     // diagnoseExprIntendedAsTemplateName.

Modified: cfe/trunk/lib/Parse/ParseExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExpr.cpp?rev=335699&r1=335698&r2=335699&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExpr.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExpr.cpp Tue Jun 26 18:32:04 2018
@@ -246,30 +246,6 @@ bool Parser::isNotExpressionStart() {
   return isKnownToBeDeclarationSpecifier();
 }
 
-/// We've parsed something that could plausibly be intended to be a template
-/// name (\p LHS) followed by a '<' token, and the following code can't possibly
-/// be an expression. Determine if this is likely to be a template-id and if so,
-/// diagnose it.
-bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) {
-  TentativeParsingAction TPA(*this);
-  // FIXME: We could look at the token sequence in a lot more detail here.
-  if (SkipUntil(tok::greater, tok::greatergreater, tok::greatergreatergreater,
-                StopAtSemi | StopBeforeMatch)) {
-    TPA.Commit();
-
-    SourceLocation Greater;
-    ParseGreaterThanInTemplateList(Greater, true, false);
-    Actions.diagnoseExprIntendedAsTemplateName(getCurScope(), LHS,
-                                               Less, Greater);
-    return true;
-  }
-
-  // There's no matching '>' token, this probably isn't supposed to be
-  // interpreted as a template-id. Parse it as an (ill-formed) comparison.
-  TPA.Revert();
-  return false;
-}
-
 bool Parser::isFoldOperator(prec::Level Level) const {
   return Level > prec::Unknown && Level != prec::Conditional &&
          Level != prec::Spaceship;
@@ -303,57 +279,12 @@ Parser::ParseRHSOfBinaryExpression(ExprR
       return ExprError(Diag(Tok, diag::err_opencl_logical_exclusive_or));
     }
 
-    // If we have a name-shaped expression followed by '<', track it in case we
-    // later find we're probably supposed to be in a template-id.
-    ExprResult TemplateName = LHS;
-    bool DependentTemplateName = false;
-    if (OpToken.is(tok::less) && Actions.mightBeIntendedToBeTemplateName(
-                                     TemplateName, DependentTemplateName)) {
-      AngleBracketTracker::Priority Priority =
-          (DependentTemplateName ? AngleBracketTracker::DependentName
-                                 : AngleBracketTracker::PotentialTypo) |
-          (OpToken.hasLeadingSpace() ? AngleBracketTracker::SpaceBeforeLess
-                                     : AngleBracketTracker::NoSpaceBeforeLess);
-      AngleBrackets.add(*this, TemplateName.get(), OpToken.getLocation(),
-                        Priority);
-    }
-
     // If we're potentially in a template-id, we may now be able to determine
     // whether we're actually in one or not.
-    if (auto *Info = AngleBrackets.getCurrent(*this)) {
-      // If an operator is followed by a type that can be a template argument
-      // and cannot be an expression, then this is ill-formed, but might be
-      // intended to be part of a template-id. Likewise if this is <>.
-      if ((OpToken.isOneOf(tok::less, tok::comma) &&
-            isKnownToBeDeclarationSpecifier()) ||
-           (OpToken.is(tok::less) &&
-            Tok.isOneOf(tok::greater, tok::greatergreater,
-                        tok::greatergreatergreater))) {
-        if (diagnoseUnknownTemplateId(Info->TemplateName, Info->LessLoc)) {
-          AngleBrackets.clear(*this);
-          return ExprError();
-        }
-      }
-
-      // If a context that looks like a template-id is followed by '()', then
-      // this is ill-formed, but might be intended to be a template-id followed
-      // by '()'.
-      if (OpToken.is(tok::greater) && Tok.is(tok::l_paren) &&
-          NextToken().is(tok::r_paren)) {
-        Actions.diagnoseExprIntendedAsTemplateName(
-            getCurScope(), Info->TemplateName, Info->LessLoc,
-            OpToken.getLocation());
-        AngleBrackets.clear(*this);
-        return ExprError();
-      }
-    }
-
-    // After a '>' (etc), we're no longer potentially in a construct that's
-    // intended to be treated as a template-id.
-    if (OpToken.is(tok::greater) ||
-        (getLangOpts().CPlusPlus11 &&
-         OpToken.isOneOf(tok::greatergreater, tok::greatergreatergreater)))
-      AngleBrackets.clear(*this);
+    if (OpToken.isOneOf(tok::comma, tok::greater, tok::greatergreater,
+                        tok::greatergreatergreater) &&
+        checkPotentialAngleBracketDelimiter(OpToken))
+      return ExprError();
 
     // Bail out when encountering a comma followed by a token which can't
     // possibly be the start of an expression. For instance:
@@ -879,6 +810,8 @@ ExprResult Parser::ParseCastExpression(b
     assert(Res.get() == nullptr && "Stray primary-expression annotation?");
     Res = getExprAnnotation(Tok);
     ConsumeAnnotationToken();
+    if (!Res.isInvalid() && Tok.is(tok::less))
+      checkPotentialAngleBracket(Res);
     break;
 
   case tok::kw___super:
@@ -1098,11 +1031,13 @@ ExprResult Parser::ParseCastExpression(b
         isAddressOfOperand, std::move(Validator),
         /*IsInlineAsmIdentifier=*/false,
         Tok.is(tok::r_paren) ? nullptr : &Replacement);
-    if (!Res.isInvalid() && !Res.get()) {
+    if (!Res.isInvalid() && Res.isUnset()) {
       UnconsumeToken(Replacement);
       return ParseCastExpression(isUnaryExpression, isAddressOfOperand,
                                  NotCastExpr, isTypeCast);
     }
+    if (!Res.isInvalid() && Tok.is(tok::less))
+      checkPotentialAngleBracket(Res);
     break;
   }
   case tok::char_constant:     // constant: character-constant
@@ -1841,6 +1776,8 @@ Parser::ParsePostfixExpressionSuffix(Exp
                                             OpKind, SS, TemplateKWLoc, Name,
                                  CurParsedObjCImpl ? CurParsedObjCImpl->Dcl
                                                    : nullptr);
+      if (!LHS.isInvalid() && Tok.is(tok::less))
+        checkPotentialAngleBracket(LHS);
       break;
     }
     case tok::plusplus:    // postfix-expression: postfix-expression '++'
@@ -2878,7 +2815,10 @@ bool Parser::ParseExpressionList(SmallVe
     if (Tok.isNot(tok::comma))
       break;
     // Move to the next argument, remember where the comma was.
+    Token Comma = Tok;
     CommaLocs.push_back(ConsumeToken());
+
+    checkPotentialAngleBracketDelimiter(Comma);
   }
   if (SawError) {
     // Ensure typos get diagnosed when errors were encountered while parsing the
@@ -2913,7 +2853,10 @@ Parser::ParseSimpleExpressionList(SmallV
       return false;
 
     // Move to the next argument, remember where the comma was.
+    Token Comma = Tok;
     CommaLocs.push_back(ConsumeToken());
+
+    checkPotentialAngleBracketDelimiter(Comma);
   }
 }
 

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=335699&r1=335698&r2=335699&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Tue Jun 26 18:32:04 2018
@@ -561,10 +561,13 @@ ExprResult Parser::tryParseCXXIdExpressi
   if (isAddressOfOperand && isPostfixExpressionSuffixStart())
     isAddressOfOperand = false;
 
-  return Actions.ActOnIdExpression(getCurScope(), SS, TemplateKWLoc, Name,
-                                   Tok.is(tok::l_paren), isAddressOfOperand,
-                                   nullptr, /*IsInlineAsmIdentifier=*/false,
-                                   &Replacement);
+  ExprResult E = Actions.ActOnIdExpression(
+      getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
+      isAddressOfOperand, nullptr, /*IsInlineAsmIdentifier=*/false,
+      &Replacement);
+  if (!E.isInvalid() && !E.isUnset() && Tok.is(tok::less))
+    checkPotentialAngleBracket(E);
+  return E;
 }
 
 /// ParseCXXIdExpression - Handle id-expression.

Modified: cfe/trunk/lib/Parse/ParseTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTemplate.cpp?rev=335699&r1=335698&r2=335699&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseTemplate.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTemplate.cpp Tue Jun 26 18:32:04 2018
@@ -1474,3 +1474,111 @@ void Parser::LexTemplateFunctionForLateP
     }
   }
 }
+
+/// We've parsed something that could plausibly be intended to be a template
+/// name (\p LHS) followed by a '<' token, and the following code can't possibly
+/// be an expression. Determine if this is likely to be a template-id and if so,
+/// diagnose it.
+bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) {
+  TentativeParsingAction TPA(*this);
+  // FIXME: We could look at the token sequence in a lot more detail here.
+  if (SkipUntil(tok::greater, tok::greatergreater, tok::greatergreatergreater,
+                StopAtSemi | StopBeforeMatch)) {
+    TPA.Commit();
+
+    SourceLocation Greater;
+    ParseGreaterThanInTemplateList(Greater, true, false);
+    Actions.diagnoseExprIntendedAsTemplateName(getCurScope(), LHS,
+                                               Less, Greater);
+    return true;
+  }
+
+  // There's no matching '>' token, this probably isn't supposed to be
+  // interpreted as a template-id. Parse it as an (ill-formed) comparison.
+  TPA.Revert();
+  return false;
+}
+
+void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) {
+  assert(Tok.is(tok::less) && "not at a potential angle bracket");
+
+  bool DependentTemplateName = false;
+  if (!Actions.mightBeIntendedToBeTemplateName(PotentialTemplateName,
+                                               DependentTemplateName))
+    return;
+
+  // OK, this might be a name that the user intended to be parsed as a
+  // template-name, followed by a '<' token. Check for some easy cases.
+
+  // If we have potential_template<>, then it's supposed to be a template-name.
+  if (NextToken().is(tok::greater) ||
+      (getLangOpts().CPlusPlus11 &&
+       NextToken().isOneOf(tok::greatergreater, tok::greatergreatergreater))) {
+    SourceLocation Less = ConsumeToken();
+    SourceLocation Greater;
+    ParseGreaterThanInTemplateList(Greater, true, false);
+    Actions.diagnoseExprIntendedAsTemplateName(
+        getCurScope(), PotentialTemplateName, Less, Greater);
+    // FIXME: Perform error recovery.
+    PotentialTemplateName = ExprError();
+    return;
+  }
+
+  // If we have 'potential_template<type-id', assume it's supposed to be a
+  // template-name if there's a matching '>' later on.
+  {
+    // FIXME: Avoid the tentative parse when NextToken() can't begin a type.
+    TentativeParsingAction TPA(*this);
+    SourceLocation Less = ConsumeToken();
+    if (isTypeIdUnambiguously() &&
+        diagnoseUnknownTemplateId(PotentialTemplateName, Less)) {
+      TPA.Commit();
+      // FIXME: Perform error recovery.
+      PotentialTemplateName = ExprError();
+      return;
+    }
+    TPA.Revert();
+  }
+
+  // Otherwise, remember that we saw this in case we see a potentially-matching
+  // '>' token later on.
+  AngleBracketTracker::Priority Priority =
+      (DependentTemplateName ? AngleBracketTracker::DependentName
+                             : AngleBracketTracker::PotentialTypo) |
+      (Tok.hasLeadingSpace() ? AngleBracketTracker::SpaceBeforeLess
+                             : AngleBracketTracker::NoSpaceBeforeLess);
+  AngleBrackets.add(*this, PotentialTemplateName.get(), Tok.getLocation(),
+                    Priority);
+}
+
+bool Parser::checkPotentialAngleBracketDelimiter(
+    const AngleBracketTracker::Loc &LAngle, const Token &OpToken) {
+  // If a comma in an expression context is followed by a type that can be a
+  // template argument and cannot be an expression, then this is ill-formed,
+  // but might be intended to be part of a template-id.
+  if (OpToken.is(tok::comma) && isTypeIdUnambiguously() &&
+      diagnoseUnknownTemplateId(LAngle.TemplateName, LAngle.LessLoc)) {
+    AngleBrackets.clear(*this);
+    return true;
+  }
+
+  // If a context that looks like a template-id is followed by '()', then
+  // this is ill-formed, but might be intended to be a template-id
+  // followed by '()'.
+  if (OpToken.is(tok::greater) && Tok.is(tok::l_paren) &&
+      NextToken().is(tok::r_paren)) {
+    Actions.diagnoseExprIntendedAsTemplateName(
+        getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
+        OpToken.getLocation());
+    AngleBrackets.clear(*this);
+    return true;
+  }
+
+  // After a '>' (etc), we're no longer potentially in a construct that's
+  // intended to be treated as a template-id.
+  if (OpToken.is(tok::greater) ||
+      (getLangOpts().CPlusPlus11 &&
+       OpToken.isOneOf(tok::greatergreater, tok::greatergreatergreater)))
+    AngleBrackets.clear(*this);
+  return false;
+}

Modified: cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp?rev=335699&r1=335698&r2=335699&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp (original)
+++ cfe/trunk/test/SemaTemplate/dependent-template-recover.cpp Tue Jun 26 18:32:04 2018
@@ -32,6 +32,16 @@ struct X {
     // FIXME: Is this the right heuristic?
     xyz<T::foo < 1>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
     T::foo < xyz<1>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
+
+    sizeof T::foo < 123 > (); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
+    f(t->foo<1, 2>(), // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
+      t->bar<3, 4>()); // expected-error{{missing 'template' keyword prior to dependent template name 'bar'}}
+
+    int arr[] = {
+      t->baz<1, 2>(1 + 1), // ok, two comparisons
+      t->foo<1, 2>(), // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
+      t->bar<3, 4>()  // FIXME: we don't recover from the previous error so don't diagnose this
+    };
   }
 
   int xyz;




More information about the cfe-commits mailing list