From: Douglas Gregor Date: Tue, 12 Jan 2010 21:28:44 +0000 (+0000) Subject: Improve recovery for template-ids whose template-name doesn't actually X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=84d0a19828599e8623223632d59447fd498999cf;p=clang Improve recovery for template-ids whose template-name doesn't actually name a template, when they occur in a base-specifier. This is one of the (few) places where we know for sure that an identifier followed by a '<' must be a template name, so we can diagnose and recover well: test/SemaTemplate/dependent-base-classes.cpp:9:16: error: missing 'template' keyword prior to dependent template name 'T::apply' struct X1 : T::apply { }; // expected-error{{missing 'template' ... ^ template test/SemaTemplate/dependent-base-classes.cpp:12:13: error: unknown template name 'vector' struct X2 : vector { }; // expected-error{{unknown template name 'vector'}} ^ 2 diagnostics generated. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@93257 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 98a74a5a03..51b6bffe00 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -261,6 +261,8 @@ def err_attributes_not_allowed : Error<"an attribute list cannot appear here">; /// C++ Templates def err_expected_template : Error<"expected template">; +def err_unknown_template_name : Error< + "unknown template name %0">; def err_expected_comma_greater : Error< "expected ',' or '>' in template-parameter-list">; def err_expected_type_id_after : Error<"expected type-id after '%0'">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 82c5544e9f..8fcc97b42c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1309,6 +1309,8 @@ def err_template_kw_refers_to_class_template : Error< "'%0%1' instantiated to a class template, not a function template">; def note_referenced_class_template : Error< "class template declared here">; +def err_template_kw_missing : Error< + "missing 'template' keyword prior to dependent template name '%0%1'">; // C++0x Variadic Templates def err_template_param_pack_default_arg : Error< diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index be46851fe1..292a2df91c 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -291,6 +291,42 @@ public: bool EnteringContext, TemplateTy &Template) = 0; + /// \brief Action called as part of error recovery when the parser has + /// determined that the given name must refer to a template, but + /// \c isTemplateName() did not return a result. + /// + /// This callback permits the action to give a detailed diagnostic when an + /// unknown template name is encountered and, potentially, to try to recover + /// by producing a new template in \p SuggestedTemplate. + /// + /// \param II the name that should be a template. + /// + /// \param IILoc the location of the name in the source. + /// + /// \param S the scope in which name lookup was performed. + /// + /// \param SS the C++ scope specifier that preceded the name. + /// + /// \param SuggestedTemplate if the action sets this template to a non-NULL, + /// template, the parser will recover by consuming the template name token + /// and the template argument list that follows. + /// + /// \param SuggestedTemplateKind as input, the kind of template that we + /// expect (e.g., \c TNK_Type_template or \c TNK_Function_template). If the + /// action provides a suggested template, this should be set to the kind of + /// template. + /// + /// \returns true if a diagnostic was emitted, false otherwise. When false, + /// the parser itself will emit a generic "unknown template name" diagnostic. + virtual bool DiagnoseUnknownTemplateName(const IdentifierInfo &II, + SourceLocation IILoc, + Scope *S, + const CXXScopeSpec *SS, + TemplateTy &SuggestedTemplate, + TemplateNameKind &SuggestedKind) { + return false; + } + /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 61a494193c..90040c54bf 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -490,18 +490,57 @@ Parser::TypeResult Parser::ParseClassName(SourceLocation &EndLocation, return true; } + IdentifierInfo *Id = Tok.getIdentifierInfo(); + SourceLocation IdLoc = ConsumeToken(); + + if (Tok.is(tok::less)) { + // It looks the user intended to write a template-id here, but the + // template-name was wrong. Try to fix that. + TemplateNameKind TNK = TNK_Type_template; + TemplateTy Template; + if (!Actions.DiagnoseUnknownTemplateName(*Id, IdLoc, CurScope, + SS, Template, TNK)) { + Diag(IdLoc, diag::err_unknown_template_name) + << Id; + } + + if (!Template) + return true; + + // Form the template name + UnqualifiedId TemplateName; + TemplateName.setIdentifier(Id, IdLoc); + + // Parse the full template-id, then turn it into a type. + if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateName, + SourceLocation(), true)) + return true; + if (TNK == TNK_Dependent_template_name) + AnnotateTemplateIdTokenAsType(SS); + + // If we didn't end up with a typename token, there's nothing more we + // can do. + if (Tok.isNot(tok::annot_typename)) + return true; + + // Retrieve the type from the annotation token, consume that token, and + // return. + EndLocation = Tok.getAnnotationEndLoc(); + TypeTy *Type = Tok.getAnnotationValue(); + ConsumeToken(); + return Type; + } + // We have an identifier; check whether it is actually a type. - TypeTy *Type = Actions.getTypeName(*Tok.getIdentifierInfo(), - Tok.getLocation(), CurScope, SS, - true); - if (!Type) { - Diag(Tok, DestrExpected ? diag::err_destructor_class_name + TypeTy *Type = Actions.getTypeName(*Id, IdLoc, CurScope, SS, true); + if (!Type) { + Diag(IdLoc, DestrExpected ? diag::err_destructor_class_name : diag::err_expected_class_name); return true; } // Consume the identifier. - EndLocation = ConsumeToken(); + EndLocation = IdLoc; return Type; } diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index d46bfc6cff..d06cecc467 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2367,6 +2367,14 @@ public: TypeTy *ObjectType, bool EnteringContext, TemplateTy &Template); + + virtual bool DiagnoseUnknownTemplateName(const IdentifierInfo &II, + SourceLocation IILoc, + Scope *S, + const CXXScopeSpec *SS, + TemplateTy &SuggestedTemplate, + TemplateNameKind &SuggestedKind); + bool DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl); TemplateDecl *AdjustDeclIfTemplate(DeclPtrTy &Decl); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index d11bf3c064..e75277e823 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -143,6 +143,30 @@ TemplateNameKind Sema::isTemplateName(Scope *S, return TemplateKind; } +bool Sema::DiagnoseUnknownTemplateName(const IdentifierInfo &II, + SourceLocation IILoc, + Scope *S, + const CXXScopeSpec *SS, + TemplateTy &SuggestedTemplate, + TemplateNameKind &SuggestedKind) { + // We can't recover unless there's a dependent scope specifier preceding the + // template name. + if (!SS || !SS->isSet() || !isDependentScopeSpecifier(*SS) || + computeDeclContext(*SS)) + return false; + + // The code is missing a 'template' keyword prior to the dependent template + // name. + NestedNameSpecifier *Qualifier = (NestedNameSpecifier*)SS->getScopeRep(); + Diag(IILoc, diag::err_template_kw_missing) + << Qualifier << II.getName() + << CodeModificationHint::CreateInsertion(IILoc, "template "); + SuggestedTemplate + = TemplateTy::make(Context.getDependentTemplateName(Qualifier, &II)); + SuggestedKind = TNK_Dependent_template_name; + return true; +} + void Sema::LookupTemplateName(LookupResult &Found, Scope *S, const CXXScopeSpec &SS, QualType ObjectType, diff --git a/test/SemaTemplate/dependent-base-classes.cpp b/test/SemaTemplate/dependent-base-classes.cpp index 56979209ea..242765894f 100644 --- a/test/SemaTemplate/dependent-base-classes.cpp +++ b/test/SemaTemplate/dependent-base-classes.cpp @@ -4,3 +4,9 @@ template struct X0 : T::template apply { X0(U u) : T::template apply(u) { } }; + +template +struct X1 : T::apply { }; // expected-error{{missing 'template' keyword prior to dependent template name 'T::apply'}} + +template +struct X2 : vector { }; // expected-error{{unknown template name 'vector'}}