From: John McCall Date: Wed, 10 Nov 2010 02:40:36 +0000 (+0000) Subject: Diagnose attempst to template using declarations and using directives. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=78b810559d89e996e00684335407443936ce34a1;p=clang Diagnose attempst to template using declarations and using directives. Recover from the latter and fail early for the former. Fixes PR8022. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@118669 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 93f5bbcba8..bff6a38bcb 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -200,6 +200,10 @@ def err_unknown_typename : Error< "unknown type name %0">; def err_use_of_tag_name_without_tag : Error< "must use '%1' tag to refer to type %0%select{| in this scope}2">; +def err_templated_using_directive : Error< + "cannot template a using directive">; +def err_templated_using_declaration : Error< + "cannot template a using declaration">; def err_expected_ident_in_using : Error< "expected an identifier in using directive">; def err_unexected_colon_in_nested_name_spec : Error< diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 89e49c33ba..f72cf63d47 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -892,6 +892,8 @@ private: /// \brief Whether the last template parameter list was empty. bool LastParameterListWasEmpty; + + SourceRange getSourceRange() const; }; void PushParsingClass(Decl *TagOrTemplate, bool TopLevelClass); @@ -1508,11 +1510,15 @@ private: SourceLocation InlineLoc = SourceLocation()); Decl *ParseLinkage(ParsingDeclSpec &DS, unsigned Context); Decl *ParseUsingDirectiveOrDeclaration(unsigned Context, + const ParsedTemplateInfo &TemplateInfo, SourceLocation &DeclEnd, CXX0XAttributeList Attrs); - Decl *ParseUsingDirective(unsigned Context, SourceLocation UsingLoc, + Decl *ParseUsingDirective(unsigned Context, + SourceLocation UsingLoc, SourceLocation &DeclEnd, AttributeList *Attr); - Decl *ParseUsingDeclaration(unsigned Context, SourceLocation UsingLoc, + Decl *ParseUsingDeclaration(unsigned Context, + const ParsedTemplateInfo &TemplateInfo, + SourceLocation UsingLoc, SourceLocation &DeclEnd, AccessSpecifier AS = AS_none); Decl *ParseStaticAssertDeclaration(SourceLocation &DeclEnd); diff --git a/include/clang/Sema/ParsedTemplate.h b/include/clang/Sema/ParsedTemplate.h index da68a494bf..9d814c712d 100644 --- a/include/clang/Sema/ParsedTemplate.h +++ b/include/clang/Sema/ParsedTemplate.h @@ -161,7 +161,10 @@ namespace clang { void Destroy() { free(this); } }; - + + /// Retrieves the range of the given template parameter lists. + SourceRange getTemplateParamsRange(TemplateParameterList const *const *Params, + unsigned NumParams); inline const ParsedTemplateArgument & ASTTemplateArgsPtr::operator[](unsigned Arg) const { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7e01bacf5c..2ba47641a0 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -354,7 +354,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, SingleDecl = ParseNamespace(Context, DeclEnd); break; case tok::kw_using: - SingleDecl = ParseUsingDirectiveOrDeclaration(Context, DeclEnd, Attr); + SingleDecl = ParseUsingDirectiveOrDeclaration(Context, ParsedTemplateInfo(), + DeclEnd, Attr); break; case tok::kw_static_assert: if (Attr.HasAttr) diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index a1e67d7699..743442a39c 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -239,8 +239,9 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, unsigned Context) { /// ParseUsingDirectiveOrDeclaration - Parse C++ using using-declaration or /// using-directive. Assumes that current token is 'using'. Decl *Parser::ParseUsingDirectiveOrDeclaration(unsigned Context, - SourceLocation &DeclEnd, - CXX0XAttributeList Attr) { + const ParsedTemplateInfo &TemplateInfo, + SourceLocation &DeclEnd, + CXX0XAttributeList Attr) { assert(Tok.is(tok::kw_using) && "Not using token"); // Eat 'using'. @@ -251,17 +252,26 @@ Decl *Parser::ParseUsingDirectiveOrDeclaration(unsigned Context, ConsumeCodeCompletionToken(); } - if (Tok.is(tok::kw_namespace)) - // Next token after 'using' is 'namespace' so it must be using-directive + // 'using namespace' means this is a using-directive. + if (Tok.is(tok::kw_namespace)) { + // Template parameters are always an error here. + if (TemplateInfo.Kind) { + SourceRange R = TemplateInfo.getSourceRange(); + Diag(UsingLoc, diag::err_templated_using_directive) + << R << FixItHint::CreateRemoval(R); + } + return ParseUsingDirective(Context, UsingLoc, DeclEnd, Attr.AttrList); + } + // Otherwise, it must be a using-declaration. + + // Using declarations can't have attributes. if (Attr.HasAttr) Diag(Attr.Range.getBegin(), diag::err_attributes_not_allowed) << Attr.Range; - // Otherwise, it must be using-declaration. - // Ignore illegal attributes (the caller should already have issued an error. - return ParseUsingDeclaration(Context, UsingLoc, DeclEnd); + return ParseUsingDeclaration(Context, TemplateInfo, UsingLoc, DeclEnd); } /// ParseUsingDirective - Parse C++ using-directive, assumes @@ -275,9 +285,9 @@ Decl *Parser::ParseUsingDirectiveOrDeclaration(unsigned Context, /// namespace-name attributes[opt] ; /// Decl *Parser::ParseUsingDirective(unsigned Context, - SourceLocation UsingLoc, - SourceLocation &DeclEnd, - AttributeList *Attr) { + SourceLocation UsingLoc, + SourceLocation &DeclEnd, + AttributeList *Attr) { assert(Tok.is(tok::kw_namespace) && "Not 'namespace' token"); // Eat 'namespace'. @@ -335,13 +345,18 @@ Decl *Parser::ParseUsingDirective(unsigned Context, /// 'using' :: unqualified-id /// Decl *Parser::ParseUsingDeclaration(unsigned Context, - SourceLocation UsingLoc, - SourceLocation &DeclEnd, - AccessSpecifier AS) { + const ParsedTemplateInfo &TemplateInfo, + SourceLocation UsingLoc, + SourceLocation &DeclEnd, + AccessSpecifier AS) { CXXScopeSpec SS; SourceLocation TypenameLoc; bool IsTypeName; + // TODO: in C++0x, if we have template parameters this must be a + // template alias: + // template <...> using id = type; + // Ignore optional 'typename'. // FIXME: This is wrong; we should parse this as a typename-specifier. if (Tok.is(tok::kw_typename)) { @@ -386,6 +401,18 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context, AttrList ? "attributes list" : "using declaration", tok::semi); + // Diagnose an attempt to declare a templated using-declaration. + if (TemplateInfo.Kind) { + SourceRange R = TemplateInfo.getSourceRange(); + Diag(UsingLoc, diag::err_templated_using_declaration) + << R << FixItHint::CreateRemoval(R); + + // Unfortunately, we have to bail out instead of recovering by + // ignoring the parameters, just in case the nested name specifier + // depends on the parameters. + return 0; + } + return Actions.ActOnUsingDeclaration(getCurScope(), AS, true, UsingLoc, SS, Name, AttrList.get(), IsTypeName, TypenameLoc); } @@ -1360,7 +1387,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, } else { SourceLocation DeclEnd; // Otherwise, it must be using-declaration. - ParseUsingDeclaration(Declarator::MemberContext, UsingLoc, DeclEnd, AS); + ParseUsingDeclaration(Declarator::MemberContext, TemplateInfo, + UsingLoc, DeclEnd, AS); } return; } diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index c472972e5c..333d72a754 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -196,12 +196,20 @@ Parser::ParseSingleDeclarationAfterTemplate( return 0; } + CXX0XAttributeList PrefixAttrs; + if (getLang().CPlusPlus0x && isCXX0XAttributeSpecifier()) + PrefixAttrs = ParseCXX0XAttributes(); + + if (Tok.is(tok::kw_using)) + return ParseUsingDirectiveOrDeclaration(Context, TemplateInfo, DeclEnd, + PrefixAttrs); + // Parse the declaration specifiers, stealing the accumulated // diagnostics from the template parameters. ParsingDeclSpec DS(DiagsFromTParams); - if (getLang().CPlusPlus0x && isCXX0XAttributeSpecifier()) - DS.AddAttributes(ParseCXX0XAttributes().AttrList); + if (PrefixAttrs.HasAttr) + DS.AddAttributes(PrefixAttrs.AttrList); ParseDeclarationSpecifiers(DS, TemplateInfo, AS, getDeclSpecContextFromDeclaratorContext(Context)); @@ -1075,3 +1083,14 @@ Decl *Parser::ParseExplicitInstantiation(SourceLocation ExternLoc, ParsingTemplateParams, DeclEnd, AS_none); } + +SourceRange Parser::ParsedTemplateInfo::getSourceRange() const { + if (TemplateParams) + return getTemplateParamsRange(TemplateParams->data(), + TemplateParams->size()); + + SourceRange R(TemplateLoc); + if (ExternLoc.isValid()) + R.setBegin(ExternLoc); + return R; +} diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 55dd66f073..33b3124090 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3402,6 +3402,10 @@ Decl *Sema::ActOnUsingDirective(Scope *S, assert(!SS.isInvalid() && "Invalid CXXScopeSpec."); assert(NamespcName && "Invalid NamespcName."); assert(IdentLoc.isValid() && "Invalid NamespceName location."); + + // This can only happen along a recovery path. + while (S->getFlags() & Scope::TemplateParamScope) + S = S->getParent(); assert(S->getFlags() & Scope::DeclScope && "Invalid Scope."); UsingDirectiveDecl *UDir = 0; @@ -3497,14 +3501,14 @@ void Sema::PushUsingDirective(Scope *S, UsingDirectiveDecl *UDir) { Decl *Sema::ActOnUsingDeclaration(Scope *S, - AccessSpecifier AS, - bool HasUsingKeyword, - SourceLocation UsingLoc, - CXXScopeSpec &SS, - UnqualifiedId &Name, - AttributeList *AttrList, - bool IsTypeName, - SourceLocation TypenameLoc) { + AccessSpecifier AS, + bool HasUsingKeyword, + SourceLocation UsingLoc, + CXXScopeSpec &SS, + UnqualifiedId &Name, + AttributeList *AttrList, + bool IsTypeName, + SourceLocation TypenameLoc) { assert(S->getFlags() & Scope::DeclScope && "Invalid Scope."); switch (Name.getKind()) { diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index d7c809f35d..3243903863 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -30,6 +30,14 @@ using namespace clang; using namespace sema; +// Exported for use by Parser. +SourceRange +clang::getTemplateParamsRange(TemplateParameterList const * const *Ps, + unsigned N) { + if (!N) return SourceRange(); + return SourceRange(Ps[0]->getTemplateLoc(), Ps[N-1]->getRAngleLoc()); +} + /// \brief Determine whether the declaration found is acceptable as the name /// of a template and, if so, return that template declaration. Otherwise, /// returns NULL. diff --git a/test/SemaCXX/using-decl-1.cpp b/test/SemaCXX/using-decl-1.cpp index 30c4cfd997..65be0bc3aa 100644 --- a/test/SemaCXX/using-decl-1.cpp +++ b/test/SemaCXX/using-decl-1.cpp @@ -95,3 +95,16 @@ namespace test1 { foo(p); // expected-error {{no matching function}} } } + +namespace test2 { + namespace ns { int foo; } + template using ns::foo; // expected-error {{cannot template a using declaration}} + + // PR8022 + struct A { + template void f(T); + }; + class B : A { + template using A::f; // expected-error {{cannot template a using declaration}} + }; +} diff --git a/test/SemaCXX/using-directive.cpp b/test/SemaCXX/using-directive.cpp index 162f7fa07a..22c6e14ce5 100644 --- a/test/SemaCXX/using-directive.cpp +++ b/test/SemaCXX/using-directive.cpp @@ -126,3 +126,10 @@ void f4() { f2(1); } using namespace std; // expected-warning{{using directive refers to implicitly-defined namespace 'std'}} using namespace ::std; // expected-warning{{using directive refers to implicitly-defined namespace 'std'}} +namespace test1 { + namespace ns { typedef int test1; } + template using namespace ns; // expected-error {{cannot template a using directive}} + + // Test that we recovered okay. + test1 x; +}