Skip to content

Commit c2dead4

Browse files
committedJun 27, 2018
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. llvm-svn: 335699
1 parent 5b45106 commit c2dead4

File tree

6 files changed

+157
-89
lines changed

6 files changed

+157
-89
lines changed
 

‎clang/include/clang/Parse/Parser.h

+8
Original file line numberDiff line numberDiff line change
@@ -1607,6 +1607,14 @@ class Parser : public CodeCompletionHandler {
16071607
}
16081608

16091609
bool diagnoseUnknownTemplateId(ExprResult TemplateName, SourceLocation Less);
1610+
void checkPotentialAngleBracket(ExprResult &PotentialTemplateName);
1611+
bool checkPotentialAngleBracketDelimiter(const AngleBracketTracker::Loc &,
1612+
const Token &OpToken);
1613+
bool checkPotentialAngleBracketDelimiter(const Token &OpToken) {
1614+
if (auto *Info = AngleBrackets.getCurrent(*this))
1615+
return checkPotentialAngleBracketDelimiter(*Info, OpToken);
1616+
return false;
1617+
}
16101618

16111619
ExprResult ParsePostfixExpressionSuffix(ExprResult LHS);
16121620
ExprResult ParseUnaryExprOrTypeTraitExpression();

‎clang/include/clang/Sema/Sema.h

+7-11
Original file line numberDiff line numberDiff line change
@@ -1830,23 +1830,19 @@ class Sema {
18301830
getTemplateNameKindForDiagnostics(TemplateName Name);
18311831

18321832
/// Determine whether it's plausible that E was intended to be a
1833-
/// template-name. Updates E to denote the template name expression.
1834-
bool mightBeIntendedToBeTemplateName(ExprResult &ER, bool &Dependent) {
1835-
if (!getLangOpts().CPlusPlus || ER.isInvalid())
1833+
/// template-name.
1834+
bool mightBeIntendedToBeTemplateName(ExprResult E, bool &Dependent) {
1835+
if (!getLangOpts().CPlusPlus || E.isInvalid())
18361836
return false;
1837-
Expr *E = ER.get()->IgnoreImplicit();
1838-
while (auto *BO = dyn_cast<BinaryOperator>(E))
1839-
E = BO->getRHS()->IgnoreImplicit();
1840-
ER = E;
18411837
Dependent = false;
1842-
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
1838+
if (auto *DRE = dyn_cast<DeclRefExpr>(E.get()))
18431839
return !DRE->hasExplicitTemplateArgs();
1844-
if (auto *ME = dyn_cast<MemberExpr>(E))
1840+
if (auto *ME = dyn_cast<MemberExpr>(E.get()))
18451841
return !ME->hasExplicitTemplateArgs();
18461842
Dependent = true;
1847-
if (auto *DSDRE = dyn_cast<DependentScopeDeclRefExpr>(E))
1843+
if (auto *DSDRE = dyn_cast<DependentScopeDeclRefExpr>(E.get()))
18481844
return !DSDRE->hasExplicitTemplateArgs();
1849-
if (auto *DSME = dyn_cast<CXXDependentScopeMemberExpr>(E))
1845+
if (auto *DSME = dyn_cast<CXXDependentScopeMemberExpr>(E.get()))
18501846
return !DSME->hasExplicitTemplateArgs();
18511847
// Any additional cases recognized here should also be handled by
18521848
// diagnoseExprIntendedAsTemplateName.

‎clang/lib/Parse/ParseExpr.cpp

+17-74
Original file line numberDiff line numberDiff line change
@@ -246,30 +246,6 @@ bool Parser::isNotExpressionStart() {
246246
return isKnownToBeDeclarationSpecifier();
247247
}
248248

249-
/// We've parsed something that could plausibly be intended to be a template
250-
/// name (\p LHS) followed by a '<' token, and the following code can't possibly
251-
/// be an expression. Determine if this is likely to be a template-id and if so,
252-
/// diagnose it.
253-
bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) {
254-
TentativeParsingAction TPA(*this);
255-
// FIXME: We could look at the token sequence in a lot more detail here.
256-
if (SkipUntil(tok::greater, tok::greatergreater, tok::greatergreatergreater,
257-
StopAtSemi | StopBeforeMatch)) {
258-
TPA.Commit();
259-
260-
SourceLocation Greater;
261-
ParseGreaterThanInTemplateList(Greater, true, false);
262-
Actions.diagnoseExprIntendedAsTemplateName(getCurScope(), LHS,
263-
Less, Greater);
264-
return true;
265-
}
266-
267-
// There's no matching '>' token, this probably isn't supposed to be
268-
// interpreted as a template-id. Parse it as an (ill-formed) comparison.
269-
TPA.Revert();
270-
return false;
271-
}
272-
273249
bool Parser::isFoldOperator(prec::Level Level) const {
274250
return Level > prec::Unknown && Level != prec::Conditional &&
275251
Level != prec::Spaceship;
@@ -303,57 +279,12 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
303279
return ExprError(Diag(Tok, diag::err_opencl_logical_exclusive_or));
304280
}
305281

306-
// If we have a name-shaped expression followed by '<', track it in case we
307-
// later find we're probably supposed to be in a template-id.
308-
ExprResult TemplateName = LHS;
309-
bool DependentTemplateName = false;
310-
if (OpToken.is(tok::less) && Actions.mightBeIntendedToBeTemplateName(
311-
TemplateName, DependentTemplateName)) {
312-
AngleBracketTracker::Priority Priority =
313-
(DependentTemplateName ? AngleBracketTracker::DependentName
314-
: AngleBracketTracker::PotentialTypo) |
315-
(OpToken.hasLeadingSpace() ? AngleBracketTracker::SpaceBeforeLess
316-
: AngleBracketTracker::NoSpaceBeforeLess);
317-
AngleBrackets.add(*this, TemplateName.get(), OpToken.getLocation(),
318-
Priority);
319-
}
320-
321282
// If we're potentially in a template-id, we may now be able to determine
322283
// whether we're actually in one or not.
323-
if (auto *Info = AngleBrackets.getCurrent(*this)) {
324-
// If an operator is followed by a type that can be a template argument
325-
// and cannot be an expression, then this is ill-formed, but might be
326-
// intended to be part of a template-id. Likewise if this is <>.
327-
if ((OpToken.isOneOf(tok::less, tok::comma) &&
328-
isKnownToBeDeclarationSpecifier()) ||
329-
(OpToken.is(tok::less) &&
330-
Tok.isOneOf(tok::greater, tok::greatergreater,
331-
tok::greatergreatergreater))) {
332-
if (diagnoseUnknownTemplateId(Info->TemplateName, Info->LessLoc)) {
333-
AngleBrackets.clear(*this);
334-
return ExprError();
335-
}
336-
}
337-
338-
// If a context that looks like a template-id is followed by '()', then
339-
// this is ill-formed, but might be intended to be a template-id followed
340-
// by '()'.
341-
if (OpToken.is(tok::greater) && Tok.is(tok::l_paren) &&
342-
NextToken().is(tok::r_paren)) {
343-
Actions.diagnoseExprIntendedAsTemplateName(
344-
getCurScope(), Info->TemplateName, Info->LessLoc,
345-
OpToken.getLocation());
346-
AngleBrackets.clear(*this);
347-
return ExprError();
348-
}
349-
}
350-
351-
// After a '>' (etc), we're no longer potentially in a construct that's
352-
// intended to be treated as a template-id.
353-
if (OpToken.is(tok::greater) ||
354-
(getLangOpts().CPlusPlus11 &&
355-
OpToken.isOneOf(tok::greatergreater, tok::greatergreatergreater)))
356-
AngleBrackets.clear(*this);
284+
if (OpToken.isOneOf(tok::comma, tok::greater, tok::greatergreater,
285+
tok::greatergreatergreater) &&
286+
checkPotentialAngleBracketDelimiter(OpToken))
287+
return ExprError();
357288

358289
// Bail out when encountering a comma followed by a token which can't
359290
// possibly be the start of an expression. For instance:
@@ -879,6 +810,8 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
879810
assert(Res.get() == nullptr && "Stray primary-expression annotation?");
880811
Res = getExprAnnotation(Tok);
881812
ConsumeAnnotationToken();
813+
if (!Res.isInvalid() && Tok.is(tok::less))
814+
checkPotentialAngleBracket(Res);
882815
break;
883816

884817
case tok::kw___super:
@@ -1098,11 +1031,13 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
10981031
isAddressOfOperand, std::move(Validator),
10991032
/*IsInlineAsmIdentifier=*/false,
11001033
Tok.is(tok::r_paren) ? nullptr : &Replacement);
1101-
if (!Res.isInvalid() && !Res.get()) {
1034+
if (!Res.isInvalid() && Res.isUnset()) {
11021035
UnconsumeToken(Replacement);
11031036
return ParseCastExpression(isUnaryExpression, isAddressOfOperand,
11041037
NotCastExpr, isTypeCast);
11051038
}
1039+
if (!Res.isInvalid() && Tok.is(tok::less))
1040+
checkPotentialAngleBracket(Res);
11061041
break;
11071042
}
11081043
case tok::char_constant: // constant: character-constant
@@ -1841,6 +1776,8 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
18411776
OpKind, SS, TemplateKWLoc, Name,
18421777
CurParsedObjCImpl ? CurParsedObjCImpl->Dcl
18431778
: nullptr);
1779+
if (!LHS.isInvalid() && Tok.is(tok::less))
1780+
checkPotentialAngleBracket(LHS);
18441781
break;
18451782
}
18461783
case tok::plusplus: // postfix-expression: postfix-expression '++'
@@ -2878,7 +2815,10 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
28782815
if (Tok.isNot(tok::comma))
28792816
break;
28802817
// Move to the next argument, remember where the comma was.
2818+
Token Comma = Tok;
28812819
CommaLocs.push_back(ConsumeToken());
2820+
2821+
checkPotentialAngleBracketDelimiter(Comma);
28822822
}
28832823
if (SawError) {
28842824
// Ensure typos get diagnosed when errors were encountered while parsing the
@@ -2913,7 +2853,10 @@ Parser::ParseSimpleExpressionList(SmallVectorImpl<Expr*> &Exprs,
29132853
return false;
29142854

29152855
// Move to the next argument, remember where the comma was.
2856+
Token Comma = Tok;
29162857
CommaLocs.push_back(ConsumeToken());
2858+
2859+
checkPotentialAngleBracketDelimiter(Comma);
29172860
}
29182861
}
29192862

‎clang/lib/Parse/ParseExprCXX.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -561,10 +561,13 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOpe
561561
if (isAddressOfOperand && isPostfixExpressionSuffixStart())
562562
isAddressOfOperand = false;
563563

564-
return Actions.ActOnIdExpression(getCurScope(), SS, TemplateKWLoc, Name,
565-
Tok.is(tok::l_paren), isAddressOfOperand,
566-
nullptr, /*IsInlineAsmIdentifier=*/false,
567-
&Replacement);
564+
ExprResult E = Actions.ActOnIdExpression(
565+
getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
566+
isAddressOfOperand, nullptr, /*IsInlineAsmIdentifier=*/false,
567+
&Replacement);
568+
if (!E.isInvalid() && !E.isUnset() && Tok.is(tok::less))
569+
checkPotentialAngleBracket(E);
570+
return E;
568571
}
569572

570573
/// ParseCXXIdExpression - Handle id-expression.

‎clang/lib/Parse/ParseTemplate.cpp

+108
Original file line numberDiff line numberDiff line change
@@ -1474,3 +1474,111 @@ void Parser::LexTemplateFunctionForLateParsing(CachedTokens &Toks) {
14741474
}
14751475
}
14761476
}
1477+
1478+
/// We've parsed something that could plausibly be intended to be a template
1479+
/// name (\p LHS) followed by a '<' token, and the following code can't possibly
1480+
/// be an expression. Determine if this is likely to be a template-id and if so,
1481+
/// diagnose it.
1482+
bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) {
1483+
TentativeParsingAction TPA(*this);
1484+
// FIXME: We could look at the token sequence in a lot more detail here.
1485+
if (SkipUntil(tok::greater, tok::greatergreater, tok::greatergreatergreater,
1486+
StopAtSemi | StopBeforeMatch)) {
1487+
TPA.Commit();
1488+
1489+
SourceLocation Greater;
1490+
ParseGreaterThanInTemplateList(Greater, true, false);
1491+
Actions.diagnoseExprIntendedAsTemplateName(getCurScope(), LHS,
1492+
Less, Greater);
1493+
return true;
1494+
}
1495+
1496+
// There's no matching '>' token, this probably isn't supposed to be
1497+
// interpreted as a template-id. Parse it as an (ill-formed) comparison.
1498+
TPA.Revert();
1499+
return false;
1500+
}
1501+
1502+
void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) {
1503+
assert(Tok.is(tok::less) && "not at a potential angle bracket");
1504+
1505+
bool DependentTemplateName = false;
1506+
if (!Actions.mightBeIntendedToBeTemplateName(PotentialTemplateName,
1507+
DependentTemplateName))
1508+
return;
1509+
1510+
// OK, this might be a name that the user intended to be parsed as a
1511+
// template-name, followed by a '<' token. Check for some easy cases.
1512+
1513+
// If we have potential_template<>, then it's supposed to be a template-name.
1514+
if (NextToken().is(tok::greater) ||
1515+
(getLangOpts().CPlusPlus11 &&
1516+
NextToken().isOneOf(tok::greatergreater, tok::greatergreatergreater))) {
1517+
SourceLocation Less = ConsumeToken();
1518+
SourceLocation Greater;
1519+
ParseGreaterThanInTemplateList(Greater, true, false);
1520+
Actions.diagnoseExprIntendedAsTemplateName(
1521+
getCurScope(), PotentialTemplateName, Less, Greater);
1522+
// FIXME: Perform error recovery.
1523+
PotentialTemplateName = ExprError();
1524+
return;
1525+
}
1526+
1527+
// If we have 'potential_template<type-id', assume it's supposed to be a
1528+
// template-name if there's a matching '>' later on.
1529+
{
1530+
// FIXME: Avoid the tentative parse when NextToken() can't begin a type.
1531+
TentativeParsingAction TPA(*this);
1532+
SourceLocation Less = ConsumeToken();
1533+
if (isTypeIdUnambiguously() &&
1534+
diagnoseUnknownTemplateId(PotentialTemplateName, Less)) {
1535+
TPA.Commit();
1536+
// FIXME: Perform error recovery.
1537+
PotentialTemplateName = ExprError();
1538+
return;
1539+
}
1540+
TPA.Revert();
1541+
}
1542+
1543+
// Otherwise, remember that we saw this in case we see a potentially-matching
1544+
// '>' token later on.
1545+
AngleBracketTracker::Priority Priority =
1546+
(DependentTemplateName ? AngleBracketTracker::DependentName
1547+
: AngleBracketTracker::PotentialTypo) |
1548+
(Tok.hasLeadingSpace() ? AngleBracketTracker::SpaceBeforeLess
1549+
: AngleBracketTracker::NoSpaceBeforeLess);
1550+
AngleBrackets.add(*this, PotentialTemplateName.get(), Tok.getLocation(),
1551+
Priority);
1552+
}
1553+
1554+
bool Parser::checkPotentialAngleBracketDelimiter(
1555+
const AngleBracketTracker::Loc &LAngle, const Token &OpToken) {
1556+
// If a comma in an expression context is followed by a type that can be a
1557+
// template argument and cannot be an expression, then this is ill-formed,
1558+
// but might be intended to be part of a template-id.
1559+
if (OpToken.is(tok::comma) && isTypeIdUnambiguously() &&
1560+
diagnoseUnknownTemplateId(LAngle.TemplateName, LAngle.LessLoc)) {
1561+
AngleBrackets.clear(*this);
1562+
return true;
1563+
}
1564+
1565+
// If a context that looks like a template-id is followed by '()', then
1566+
// this is ill-formed, but might be intended to be a template-id
1567+
// followed by '()'.
1568+
if (OpToken.is(tok::greater) && Tok.is(tok::l_paren) &&
1569+
NextToken().is(tok::r_paren)) {
1570+
Actions.diagnoseExprIntendedAsTemplateName(
1571+
getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
1572+
OpToken.getLocation());
1573+
AngleBrackets.clear(*this);
1574+
return true;
1575+
}
1576+
1577+
// After a '>' (etc), we're no longer potentially in a construct that's
1578+
// intended to be treated as a template-id.
1579+
if (OpToken.is(tok::greater) ||
1580+
(getLangOpts().CPlusPlus11 &&
1581+
OpToken.isOneOf(tok::greatergreater, tok::greatergreatergreater)))
1582+
AngleBrackets.clear(*this);
1583+
return false;
1584+
}

‎clang/test/SemaTemplate/dependent-template-recover.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ struct X {
3232
// FIXME: Is this the right heuristic?
3333
xyz<T::foo < 1>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
3434
T::foo < xyz<1>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
35+
36+
sizeof T::foo < 123 > (); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
37+
f(t->foo<1, 2>(), // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
38+
t->bar<3, 4>()); // expected-error{{missing 'template' keyword prior to dependent template name 'bar'}}
39+
40+
int arr[] = {
41+
t->baz<1, 2>(1 + 1), // ok, two comparisons
42+
t->foo<1, 2>(), // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
43+
t->bar<3, 4>() // FIXME: we don't recover from the previous error so don't diagnose this
44+
};
3545
}
3646

3747
int xyz;

0 commit comments

Comments
 (0)
Please sign in to comment.