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. llvm-svn: 104406
This commit is contained in:
parent
4dc833c607
commit
786123dc48
|
@ -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">;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue