Diagnose missing 'template' keywords in more cases.
We track when we see a name-shaped expression followed by a '<' token
and parse the '<' as a comparison. Then:
* if we see a token sequence that cannot possibly be an expression but
can be a template argument (in particular, a type-id) that follows
either a ',' or the '<', diagnose that the '<' was supposed to start
a template argument list, and
* if we see '>()', diagnose that the '<' was supposed to start a
template argument list.
This only changes the diagnostic for error cases, and in practice
appears to catch the most common cases where a missing 'template'
keyword leads to parse errors within a template.
Differential Revision: https://reviews.llvm.org/D48571
llvm-svn: 335687
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 8d51bc8..94e56e9 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -302,6 +302,59 @@
if (OpToken.is(tok::caretcaret)) {
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);
+
// Bail out when encountering a comma followed by a token which can't
// possibly be the start of an expression. For instance:
// int f() { return 1, }
@@ -313,16 +366,6 @@
return LHS;
}
- // If a '<' token 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 a template-id.
- if (OpToken.is(tok::less) && Actions.mightBeIntendedToBeTemplateName(LHS) &&
- (isKnownToBeDeclarationSpecifier() ||
- Tok.isOneOf(tok::greater, tok::greatergreater,
- tok::greatergreatergreater)) &&
- diagnoseUnknownTemplateId(LHS, OpToken.getLocation()))
- return ExprError();
-
// If the next token is an ellipsis, then this is a fold-expression. Leave
// it alone so we can handle it in the paren expression.
if (isFoldOperator(NextTokPrec) && Tok.is(tok::ellipsis)) {