aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2010-05-21 23:18:07 +0000
committerDouglas Gregor <dgregor@apple.com>2010-05-21 23:18:07 +0000
commit1fd6d44d7ca97631497551bbf98866263143d706 (patch)
treed55684a3629ba44cddbe0b9d29897c19e6663f3f
parentdb3f847cb883fdb19d79c7223fa032e7266c0ee5 (diff)
Improve parser recovery when we encounter a dependent template name
that is missing the 'template' keyword, e.g., t->getAs<T>() where getAs is a member of an unknown specialization. C++ requires that we treat "getAs" as a value, but that would fail to parse since T is the name of a type. We would then fail at the '>', since a type cannot be followed by a '>'. This is a very common error for C++ programmers to make, especially since GCC occasionally allows it when it shouldn't (as does Visual C++). So, when we are in this case, we use tentative parsing to see if the tokens starting at "<" can only be parsed as a template argument list. If so, we produce a diagnostic with a fix-it that states that the 'template' keyword is needed: test/SemaTemplate/dependent-template-recover.cpp:5:8: error: 'template' keyword is required to treat 'getAs' as a dependent template name t->getAs<T>(); ^ template This is just a start of this patch; I'd like to apply the same approach to everywhere that a template-id with dependent template name can be parsed. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@104406 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticParseKinds.td3
-rw-r--r--include/clang/Parse/Action.h11
-rw-r--r--include/clang/Parse/Parser.h1
-rw-r--r--lib/Parse/MinimalAction.cpp4
-rw-r--r--lib/Parse/ParseExprCXX.cpp46
-rw-r--r--lib/Parse/ParseTemplate.cpp30
-rw-r--r--lib/Parse/Parser.cpp3
-rw-r--r--lib/Sema/Sema.h6
-rw-r--r--lib/Sema/SemaDecl.cpp5
-rw-r--r--lib/Sema/SemaExpr.cpp4
-rw-r--r--lib/Sema/SemaTemplate.cpp20
-rw-r--r--test/SemaTemplate/dependent-template-recover.cpp15
12 files changed, 128 insertions, 20 deletions
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index 162398dfd2..b8ed0ceb08 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -313,6 +313,9 @@ def err_explicit_instantiation_with_definition : Error<
"'template' keyword">;
def err_enum_template : Error<"enumeration cannot be a template">;
+def err_missing_dependent_template_keyword : Error<
+ "use 'template' keyword to treat '%0' as a dependent template name">;
+
// Constructor template diagnostics.
def err_out_of_line_constructor_template_id : Error<
"out-of-line constructor for %0 cannot have template arguments">;
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h
index 69105685ba..f9aef9b0e9 100644
--- a/include/clang/Parse/Action.h
+++ b/include/clang/Parse/Action.h
@@ -286,13 +286,18 @@ public:
/// \param Template if the name does refer to a template, the declaration
/// of the template that the name refers to.
///
+ /// \param MemberOfUnknownSpecialization Will be set true if the resulting
+ /// member would be a member of an unknown specialization, in which case this
+ /// lookup cannot possibly pass at this time.
+ ///
/// \returns the kind of template that this name refers to.
virtual TemplateNameKind isTemplateName(Scope *S,
CXXScopeSpec &SS,
UnqualifiedId &Name,
TypeTy *ObjectType,
bool EnteringContext,
- TemplateTy &Template) = 0;
+ TemplateTy &Template,
+ bool &MemberOfUnknownSpecialization) = 0;
/// \brief Action called as part of error recovery when the parser has
/// determined that the given name must refer to a template, but
@@ -3027,7 +3032,9 @@ public:
UnqualifiedId &Name,
TypeTy *ObjectType,
bool EnteringContext,
- TemplateTy &Template);
+ TemplateTy &Template,
+ bool &MemberOfUnknownSpecialization);
+
/// ActOnDeclarator - If this is a typedef declarator, we modify the
/// IdentifierInfo::FETokenInfo field to keep track of this fact, until S is
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index b640d36525..ea263f94e1 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1463,6 +1463,7 @@ private:
SourceLocation TemplateKWLoc = SourceLocation(),
bool AllowTypeAnnotation = true);
void AnnotateTemplateIdTokenAsType(const CXXScopeSpec *SS = 0);
+ bool IsTemplateArgumentList();
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs);
ParsedTemplateArgument ParseTemplateTemplateArgument();
ParsedTemplateArgument ParseTemplateArgument();
diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp
index 5a03767811..b7205160c1 100644
--- a/lib/Parse/MinimalAction.cpp
+++ b/lib/Parse/MinimalAction.cpp
@@ -194,7 +194,9 @@ MinimalAction::isTemplateName(Scope *S,
UnqualifiedId &Name,
TypeTy *ObjectType,
bool EnteringScope,
- TemplateTy &TemplateDecl) {
+ TemplateTy &TemplateDecl,
+ bool &MemberOfUnknownSpecialization) {
+ MemberOfUnknownSpecialization = false;
return TNK_Non_template;
}
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index b7ccea53d5..1588b69bda 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -289,11 +289,13 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
TemplateTy Template;
UnqualifiedId TemplateName;
TemplateName.setIdentifier(&II, Tok.getLocation());
+ bool MemberOfUnknownSpecialization;
if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, SS,
TemplateName,
ObjectType,
EnteringContext,
- Template)) {
+ Template,
+ MemberOfUnknownSpecialization)) {
// We have found a template name, so annotate this this token
// with a template-id annotation. We do not permit the
// template-id to be translated into a type annotation,
@@ -990,21 +992,54 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
TNK = TNK_Dependent_template_name;
if (!Template.get())
return true;
- } else
+ } else {
+ bool MemberOfUnknownSpecialization;
TNK = Actions.isTemplateName(CurScope, SS, Id, ObjectType,
- EnteringContext, Template);
+ EnteringContext, Template,
+ MemberOfUnknownSpecialization);
+
+ if (TNK == TNK_Non_template && MemberOfUnknownSpecialization &&
+ ObjectType && IsTemplateArgumentList()) {
+ // We have something like t->getAs<T>(), where getAs is a
+ // member of an unknown specialization. However, this will only
+ // parse correctly as a template, so suggest the keyword 'template'
+ // before 'getAs' and treat this as a dependent template name.
+ std::string Name;
+ if (Id.getKind() == UnqualifiedId::IK_Identifier)
+ Name = Id.Identifier->getName();
+ else {
+ Name = "operator ";
+ if (Id.getKind() == UnqualifiedId::IK_OperatorFunctionId)
+ Name += getOperatorSpelling(Id.OperatorFunctionId.Operator);
+ else
+ Name += Id.Identifier->getName();
+ }
+ Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
+ << Name
+ << FixItHint::CreateInsertion(Id.StartLocation, "template ");
+ Template = Actions.ActOnDependentTemplateName(TemplateKWLoc, SS,
+ Id, ObjectType,
+ EnteringContext);
+ TNK = TNK_Dependent_template_name;
+ if (!Template.get())
+ return true;
+ }
+ }
break;
case UnqualifiedId::IK_ConstructorName: {
UnqualifiedId TemplateName;
+ bool MemberOfUnknownSpecialization;
TemplateName.setIdentifier(Name, NameLoc);
TNK = Actions.isTemplateName(CurScope, SS, TemplateName, ObjectType,
- EnteringContext, Template);
+ EnteringContext, Template,
+ MemberOfUnknownSpecialization);
break;
}
case UnqualifiedId::IK_DestructorName: {
UnqualifiedId TemplateName;
+ bool MemberOfUnknownSpecialization;
TemplateName.setIdentifier(Name, NameLoc);
if (ObjectType) {
Template = Actions.ActOnDependentTemplateName(TemplateKWLoc, SS,
@@ -1015,7 +1050,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
return true;
} else {
TNK = Actions.isTemplateName(CurScope, SS, TemplateName, ObjectType,
- EnteringContext, Template);
+ EnteringContext, Template,
+ MemberOfUnknownSpecialization);
if (TNK == TNK_Non_template && Id.DestructorName == 0) {
Diag(NameLoc, diag::err_destructor_template_id)
diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp
index 93197816ce..8b9142ce9b 100644
--- a/lib/Parse/ParseTemplate.cpp
+++ b/lib/Parse/ParseTemplate.cpp
@@ -902,10 +902,12 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
ConsumeToken(); // the identifier
if (isEndOfTemplateArgument(Tok)) {
+ bool MemberOfUnknownSpecialization;
TemplateNameKind TNK = Actions.isTemplateName(CurScope, SS, Name,
/*ObjectType=*/0,
/*EnteringContext=*/false,
- Template);
+ Template,
+ MemberOfUnknownSpecialization);
if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template) {
// We have an id-expression that refers to a class template or
// (C++0x) template alias.
@@ -966,6 +968,32 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
ExprArg.release(), Loc);
}
+/// \brief Determine whether the current tokens can only be parsed as a
+/// template argument list (starting with the '<') and never as a '<'
+/// expression.
+bool Parser::IsTemplateArgumentList() {
+ struct AlwaysRevertAction : TentativeParsingAction {
+ AlwaysRevertAction(Parser &P) : TentativeParsingAction(P) { }
+ ~AlwaysRevertAction() { Revert(); }
+ } Tentative(*this);
+
+ // '<'
+ if (!Tok.is(tok::less))
+ return false;
+ ConsumeToken();
+
+ // An empty template argument list.
+ if (Tok.is(tok::greater))
+ return true;
+
+ // See whether we have declaration specifiers, which indicate a type.
+ while (isCXXDeclarationSpecifier() == TPResult::True())
+ ConsumeToken();
+
+ // If we have a '>' or a ',' then this is a template argument list.
+ return Tok.is(tok::greater) || Tok.is(tok::comma);
+}
+
/// ParseTemplateArgumentList - Parse a C++ template-argument-list
/// (C++ [temp.names]). Returns true if there was an error.
///
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index bfcb6e7ee7..37475d23fe 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -980,10 +980,11 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
TemplateTy Template;
UnqualifiedId TemplateName;
TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ bool MemberOfUnknownSpecialization;
if (TemplateNameKind TNK
= Actions.isTemplateName(CurScope, SS, TemplateName,
/*ObjectType=*/0, EnteringContext,
- Template)) {
+ Template, MemberOfUnknownSpecialization)) {
// Consume the identifier.
ConsumeToken();
if (AnnotateTemplateIdToken(Template, TNK, &SS, TemplateName)) {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 0870431a0d..1b61800c2a 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -2800,14 +2800,16 @@ public:
// C++ Templates [C++ 14]
//
void LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS,
- QualType ObjectType, bool EnteringContext);
+ QualType ObjectType, bool EnteringContext,
+ bool &MemberOfUnknownSpecialization);
virtual TemplateNameKind isTemplateName(Scope *S,
CXXScopeSpec &SS,
UnqualifiedId &Name,
TypeTy *ObjectType,
bool EnteringContext,
- TemplateTy &Template);
+ TemplateTy &Template,
+ bool &MemberOfUnknownSpecialization);
virtual bool DiagnoseUnknownTemplateName(const IdentifierInfo &II,
SourceLocation IILoc,
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index b94f3d7540..19e31da6ca 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -287,8 +287,9 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II,
Name.setIdentifier(&II, IILoc);
CXXScopeSpec EmptySS;
TemplateTy TemplateResult;
- if (isTemplateName(S, SS ? *SS : EmptySS, Name, 0, true, TemplateResult)
- == TNK_Type_template) {
+ bool MemberOfUnknownSpecialization;
+ if (isTemplateName(S, SS ? *SS : EmptySS, Name, 0, true, TemplateResult,
+ MemberOfUnknownSpecialization) == TNK_Type_template) {
TemplateName TplName = TemplateResult.getAsVal<TemplateName>();
Diag(IILoc, diag::err_template_missing_args) << TplName;
if (TemplateDecl *TplDecl = TplName.getAsTemplateDecl()) {
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 933a696bdc..8c854ab77a 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -1055,7 +1055,9 @@ Sema::OwningExprResult Sema::ActOnIdExpression(Scope *S,
// lookup to determine that it was a template name in the first place. If
// this becomes a performance hit, we can work harder to preserve those
// results until we get here but it's likely not worth it.
- LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false);
+ bool MemberOfUnknownSpecialization;
+ LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false,
+ MemberOfUnknownSpecialization);
} else {
bool IvarLookupFollowUp = (!SS.isSet() && II && getCurMethodDecl());
LookupParsedName(R, S, &SS, !IvarLookupFollowUp);
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 6a4f34731e..b4f9c3df8b 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -100,10 +100,12 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
UnqualifiedId &Name,
TypeTy *ObjectTypePtr,
bool EnteringContext,
- TemplateTy &TemplateResult) {
+ TemplateTy &TemplateResult,
+ bool &MemberOfUnknownSpecialization) {
assert(getLangOptions().CPlusPlus && "No template names in C!");
DeclarationName TName;
+ MemberOfUnknownSpecialization = false;
switch (Name.getKind()) {
case UnqualifiedId::IK_Identifier:
@@ -128,7 +130,8 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
LookupResult R(*this, TName, Name.getSourceRange().getBegin(),
LookupOrdinaryName);
R.suppressDiagnostics();
- LookupTemplateName(R, S, SS, ObjectType, EnteringContext);
+ LookupTemplateName(R, S, SS, ObjectType, EnteringContext,
+ MemberOfUnknownSpecialization);
if (R.empty() || R.isAmbiguous())
return TNK_Non_template;
@@ -191,8 +194,10 @@ bool Sema::DiagnoseUnknownTemplateName(const IdentifierInfo &II,
void Sema::LookupTemplateName(LookupResult &Found,
Scope *S, CXXScopeSpec &SS,
QualType ObjectType,
- bool EnteringContext) {
+ bool EnteringContext,
+ bool &MemberOfUnknownSpecialization) {
// Determine where to perform name lookup
+ MemberOfUnknownSpecialization = false;
DeclContext *LookupCtx = 0;
bool isDependent = false;
if (!ObjectType.isNull()) {
@@ -241,6 +246,7 @@ void Sema::LookupTemplateName(LookupResult &Found,
} else if (isDependent) {
// We cannot look into a dependent object type or nested nme
// specifier.
+ MemberOfUnknownSpecialization = true;
return;
} else {
// Perform unqualified name lookup in the current scope.
@@ -1641,8 +1647,10 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS,
RequireCompleteDeclContext(SS, DC))
return BuildDependentDeclRefExpr(SS, Name, NameLoc, &TemplateArgs);
+ bool MemberOfUnknownSpecialization;
LookupResult R(*this, Name, NameLoc, LookupOrdinaryName);
- LookupTemplateName(R, (Scope*) 0, SS, QualType(), /*Entering*/ false);
+ LookupTemplateName(R, (Scope*) 0, SS, QualType(), /*Entering*/ false,
+ MemberOfUnknownSpecialization);
if (R.isAmbiguous())
return ExprError();
@@ -1699,8 +1707,10 @@ Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
// "template" keyword is now permitted). We follow the C++0x
// rules, even in C++03 mode, retroactively applying the DR.
TemplateTy Template;
+ bool MemberOfUnknownSpecialization;
TemplateNameKind TNK = isTemplateName(0, SS, Name, ObjectType,
- EnteringContext, Template);
+ EnteringContext, Template,
+ MemberOfUnknownSpecialization);
if (TNK == TNK_Non_template && LookupCtx->isDependentContext() &&
isa<CXXRecordDecl>(LookupCtx) &&
cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()) {
diff --git a/test/SemaTemplate/dependent-template-recover.cpp b/test/SemaTemplate/dependent-template-recover.cpp
new file mode 100644
index 0000000000..632a11b2e0
--- /dev/null
+++ b/test/SemaTemplate/dependent-template-recover.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+template<typename T, typename U, int N>
+struct X {
+ void f(T* t) {
+ t->f0<U>(); // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
+ t->f0<int>(); // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
+
+ t->operator+<U const, 1>(); // expected-error{{use 'template' keyword to treat 'operator +' as a dependent template name}}
+ t->f1<int const, 2>(); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}}
+
+ // FIXME: We can't recover from these yet
+ (*t).f2<N>(); // expected-error{{expected expression}}
+ (*t).f2<0>(); // expected-error{{expected expression}}
+ }
+};