diff options
author | Douglas Gregor <dgregor@apple.com> | 2010-05-21 23:18:07 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2010-05-21 23:18:07 +0000 |
commit | 1fd6d44d7ca97631497551bbf98866263143d706 (patch) | |
tree | d55684a3629ba44cddbe0b9d29897c19e6663f3f | |
parent | db3f847cb883fdb19d79c7223fa032e7266c0ee5 (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.td | 3 | ||||
-rw-r--r-- | include/clang/Parse/Action.h | 11 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 1 | ||||
-rw-r--r-- | lib/Parse/MinimalAction.cpp | 4 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 46 | ||||
-rw-r--r-- | lib/Parse/ParseTemplate.cpp | 30 | ||||
-rw-r--r-- | lib/Parse/Parser.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 6 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 5 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 20 | ||||
-rw-r--r-- | test/SemaTemplate/dependent-template-recover.cpp | 15 |
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}} + } +}; |