From f780abc21c39cd4731b9e38f2d2d9f7d1510bd7b Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 30 Dec 2008 03:27:21 +0000 Subject: [PATCH] Parser support for C++ using directives, from Piotr Rak git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61486 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticKinds.def | 4 ++ include/clang/Parse/Action.h | 9 +++ include/clang/Parse/Parser.h | 3 + lib/Parse/ParseDecl.cpp | 4 ++ lib/Parse/ParseDeclCXX.cpp | 85 +++++++++++++++++++++++++ lib/Parse/Parser.cpp | 16 +++++ lib/Sema/Sema.h | 24 ++++++- lib/Sema/SemaDecl.cpp | 50 ++++++++++----- lib/Sema/SemaDeclCXX.cpp | 28 ++++++++ test/Parser/cxx-using-directive.cpp | 32 ++++++++++ www/cxx_status.html | 9 ++- 11 files changed, 245 insertions(+), 19 deletions(-) create mode 100644 test/Parser/cxx-using-directive.cpp diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 2ffad9e8e4..d2c82dafc8 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1462,6 +1462,10 @@ DIAG(err_expected_class_name, ERROR, DIAG(err_anon_type_definition, ERROR, "declaration of anonymous %0 must be a definition") +// Namespaces. +DIAG(err_expected_namespace_name, ERROR, + "expected namespace name") + // C++ member initializers. DIAG(err_mem_init_not_member_or_class, ERROR, "member initializer %0 does not name a non-static data member or base " diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index b55106a9c6..fb1d9d4c0e 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -731,6 +731,15 @@ public: return; } + /// ActOnUsingDirective - This is called when using-directive is parsed. + virtual DeclTy *ActOnUsingDirective(Scope *CurScope, + SourceLocation UsingLoc, + SourceLocation NamespcLoc, + const CXXScopeSpec &SS, + SourceLocation IdentLoc, + IdentifierInfo *NamespcName, + AttributeList *AttrList); + /// ActOnParamDefaultArgument - Parse default argument for function parameter virtual void ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc, diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 28aed8b58a..e23f72b5ca 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -931,6 +931,9 @@ private: DeclTy *ParseNamespace(unsigned Context); DeclTy *ParseLinkage(unsigned Context); + DeclTy *ParseUsingDirectiveOrDeclaration(unsigned Context); + DeclTy *ParseUsingDirective(unsigned Context, SourceLocation UsingLoc); + DeclTy *ParseUsingDeclaration(unsigned Context, SourceLocation UsingLoc); //===--------------------------------------------------------------------===// // C++ 9: classes [class] and C structs/unions. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 23c0c61517..a410dfeda3 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -216,6 +216,8 @@ void Parser::FuzzyParseMicrosoftDeclSpec() { /// others [FIXME] /// [C++] template-declaration /// [C++] namespace-definition +/// [C++] using-directive +/// [C++] using-declaration [TODO] /// others... [FIXME] /// Parser::DeclTy *Parser::ParseDeclaration(unsigned Context) { @@ -225,6 +227,8 @@ Parser::DeclTy *Parser::ParseDeclaration(unsigned Context) { return ParseTemplateDeclaration(Context); case tok::kw_namespace: return ParseNamespace(Context); + case tok::kw_using: + return ParseUsingDirectiveOrDeclaration(Context); default: return ParseSimpleDeclaration(Context); } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 1d3de90060..3236763021 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -131,6 +131,91 @@ Parser::DeclTy *Parser::ParseLinkage(unsigned Context) { &InnerDecls.front(), InnerDecls.size()); } +/// ParseUsingDirectiveOrDeclaration - Parse C++ using using-declaration or +/// using-directive. Assumes that current token is 'using'. +Parser::DeclTy *Parser::ParseUsingDirectiveOrDeclaration(unsigned Context) +{ + assert(Tok.is(tok::kw_using) && "Not using token"); + + // Eat 'using'. + SourceLocation UsingLoc = ConsumeToken(); + + if (Tok.is(tok::kw_namespace)) { + // Next token after 'using' is 'namespace' so it must be using-directive + return ParseUsingDirective(Context, UsingLoc); + } else { + // Otherwise, it must be using-declaration. + return ParseUsingDeclaration(Context, UsingLoc); //FIXME: It is just stub. + } +} + +/// ParseUsingDirective - Parse C++ using-directive, assumes +/// that current token is 'namespace' and 'using' was already parsed. +/// +/// using-directive: [C++ 7.3.p4: namespace.udir] +/// 'using' 'namespace' ::[opt] nested-name-specifier[opt] +/// namespace-name ; +/// [GNU] using-directive: +/// 'using' 'namespace' ::[opt] nested-name-specifier[opt] +/// namespace-name attributes[opt] ; +/// +Parser::DeclTy *Parser::ParseUsingDirective(unsigned Context, + SourceLocation UsingLoc) { + assert(Tok.is(tok::kw_namespace) && "Not 'namespace' token"); + + // Eat 'namespace'. + SourceLocation NamespcLoc = ConsumeToken(); + + CXXScopeSpec SS; + // Parse (optional) nested-name-specifier. + MaybeParseCXXScopeSpecifier(SS); + + AttributeList *AttrList = 0; + IdentifierInfo *NamespcName = 0; + SourceLocation IdentLoc = SourceLocation(); + + // Parse namespace-name. + if (!SS.isInvalid() && Tok.is(tok::identifier)) { + // Parse identifier. + NamespcName = Tok.getIdentifierInfo(); + IdentLoc = ConsumeToken(); + // Parse (optional) attributes (most likely GNU strong-using extension) + if (Tok.is(tok::kw___attribute)) { + AttrList = ParseAttributes(); + } + // Eat ';'. + if (ExpectAndConsume(tok::semi, diag::err_expected_semi_after, + AttrList? "attributes list" : "namespace name")) { + SkipUntil(tok::semi); + return 0; + } + } else { + Diag(Tok, diag::err_expected_namespace_name); + // If there was invalid namespace name, skip to end of decl, and eat ';'. + SkipUntil(tok::semi); + // FIXME: Are there cases, when we would like to call ActOnUsingDirective? + return 0; + } + + return Actions.ActOnUsingDirective(CurScope, UsingLoc, NamespcLoc, SS, + IdentLoc ,NamespcName, AttrList); +} + +/// ParseUsingDeclaration - Parse C++ using-declaration. Assumes that +/// 'using' was already seen. +/// +/// using-declaration: [C++ 7.3.p3: namespace.udecl] +/// 'using' 'typename'[opt] ::[opt] nested-name-specifier +/// unqualified-id [TODO] +/// 'using' :: unqualified-id [TODO] +/// +Parser::DeclTy *Parser::ParseUsingDeclaration(unsigned Context, + SourceLocation UsingLoc) { + assert(false && "Not implemented"); + // FIXME: Implement parsing. + return 0; +} + /// ParseClassName - Parse a C++ class-name, which names a class. Note /// that we only check that the result names a type; semantic analysis /// will need to verify that the type names a class. The result is diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index e5b40d9a44..3c79d6405c 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -43,6 +43,21 @@ ActionBase::~ActionBase() {} /// Out-of-line virtual destructor to provide home for Action class. Action::~Action() {} +// Defined out-of-line here because of dependecy on AttributeList +Action::DeclTy *Action::ActOnUsingDirective(Scope *CurScope, + SourceLocation UsingLoc, + SourceLocation NamespcLoc, + const CXXScopeSpec &SS, + SourceLocation IdentLoc, + IdentifierInfo *NamespcName, + AttributeList *AttrList) { + + // FIXME: Parser seems to assume that Action::ActOn* takes ownership over + // passed AttributeList, however other actions don't free it, is it + // temporary state or bug? + delete AttrList; + return 0; +} DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) { return Diags.Report(FullSourceLoc(Loc,PP.getSourceManager()), DiagID); @@ -358,6 +373,7 @@ Parser::DeclTy *Parser::ParseExternalDeclaration() { ConsumeToken(); } return 0; + case tok::kw_using: case tok::kw_namespace: case tok::kw_typedef: case tok::kw_template: diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 386639061d..ca151760c5 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -491,11 +491,25 @@ public: void CheckCXXDefaultArguments(FunctionDecl *FD); void CheckExtraCXXDefaultArguments(Declarator &D); + // FIXME: NamespaceNameOnly parameter is added temporarily + // we will need a better way to specify lookup criteria for things + // like template specializations, explicit template instatatiation etc. + /// More parsing and symbol table subroutines... Decl *LookupDecl(DeclarationName Name, unsigned NSI, Scope *S, const DeclContext *LookupCtx = 0, bool enableLazyBuiltinCreation = true, - bool LookInParent = true); + bool LookInParent = true, + bool NamespaceNameOnly = false); + + Decl *LookupNamespaceName(DeclarationName Name, Scope *S, + const DeclContext *LookupCtx) { + return LookupDecl(Name, Decl::IDNS_Tag | Decl::IDNS_Ordinary, S, + LookupCtx, + /* enableLazyBuiltinCreation */ false, + /* LookInParent */ true, + /* NamespaceNameOnly */ true); + } ObjCInterfaceDecl *getObjCInterfaceDecl(IdentifierInfo *Id); ScopedDecl *LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID, Scope *S); @@ -807,6 +821,14 @@ public: SourceLocation LBrace); virtual void ActOnFinishNamespaceDef(DeclTy *Dcl, SourceLocation RBrace); + virtual DeclTy *ActOnUsingDirective(Scope *CurScope, + SourceLocation UsingLoc, + SourceLocation NamespcLoc, + const CXXScopeSpec &SS, + SourceLocation IdentLoc, + IdentifierInfo *NamespcName, + AttributeList *AttrList); + /// AddCXXDirectInitializerToDecl - This action is called immediately after /// ActOnDeclarator, when a C++ direct initializer is present. /// e.g: "int x(1);" diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 875f0f19ad..21d3ad68a0 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -243,11 +243,15 @@ MaybeConstructOverloadSet(ASTContext &Context, } /// LookupDecl - Look up the inner-most declaration in the specified -/// namespace. +/// namespace. NamespaceNameOnly - during lookup only namespace names +/// are considered as required in C++ [basic.lookup.udir] 3.4.6.p1 +/// 'When looking up a namespace-name in a using-directive or +/// namespace-alias-definition, only namespace names are considered.' Decl *Sema::LookupDecl(DeclarationName Name, unsigned NSI, Scope *S, const DeclContext *LookupCtx, bool enableLazyBuiltinCreation, - bool LookInParent) { + bool LookInParent, + bool NamespaceNameOnly) { if (!Name) return 0; unsigned NS = NSI; if (getLangOptions().CPlusPlus && (NS & Decl::IDNS_Ordinary)) @@ -259,6 +263,7 @@ Decl *Sema::LookupDecl(DeclarationName Name, unsigned NSI, Scope *S, // labels in C++ is purely lexical, so search in the // declarations attached to the name. assert(!LookupCtx && "Can't perform qualified name lookup here"); + assert(!NamespaceNameOnly && "Can't perform namespace name lookup here"); IdentifierResolver::iterator I = IdResolver.begin(Name, CurContext, LookInParent); @@ -268,14 +273,19 @@ Decl *Sema::LookupDecl(DeclarationName Name, unsigned NSI, Scope *S, // deep shadowing is extremely uncommon. for (; I != IdResolver.end(); ++I) if ((*I)->getIdentifierNamespace() & NS) - return *I; + return *I; } else if (LookupCtx) { // Perform qualified name lookup into the LookupCtx. // FIXME: Will need to look into base classes and such. DeclContext::lookup_const_iterator I, E; for (llvm::tie(I, E) = LookupCtx->lookup(Context, Name); I != E; ++I) - if ((*I)->getIdentifierNamespace() & NS) - return MaybeConstructOverloadSet(Context, I, E); + if ((*I)->getIdentifierNamespace() & NS) { + if (NamespaceNameOnly && !isa(*I)) { + // Skip non-namespace name. + } else { + return MaybeConstructOverloadSet(Context, I, E); + } + } } else { // Name lookup for ordinary names and tag names in C++ requires // looking into scopes that aren't strictly lexical, and @@ -289,17 +299,21 @@ Decl *Sema::LookupDecl(DeclarationName Name, unsigned NSI, Scope *S, // FIXME: The isDeclScope check could be expensive. Can we do better? for (; I != IEnd && S->isDeclScope(*I); ++I) { if ((*I)->getIdentifierNamespace() & NS) { - // We found something. Look for anything else in our scope - // with this same name and in an acceptable identifier - // namespace, so that we can construct an overload set if we - // need to. - IdentifierResolver::iterator LastI = I; - for (++LastI; LastI != IEnd; ++LastI) { - if (((*LastI)->getIdentifierNamespace() & NS) == 0 || - !S->isDeclScope(*LastI)) - break; + if (NamespaceNameOnly && !isa(*I)) { + // Skip non-namespace name. + } else { + // We found something. Look for anything else in our scope + // with this same name and in an acceptable identifier + // namespace, so that we can construct an overload set if we + // need to. + IdentifierResolver::iterator LastI = I; + for (++LastI; LastI != IEnd; ++LastI) { + if (((*LastI)->getIdentifierNamespace() & NS) == 0 || + !S->isDeclScope(*LastI)) + break; + } + return MaybeConstructOverloadSet(Context, I, LastI); } - return MaybeConstructOverloadSet(Context, I, LastI); } } @@ -314,8 +328,10 @@ Decl *Sema::LookupDecl(DeclarationName Name, unsigned NSI, Scope *S, DeclContext::lookup_const_iterator I, E; for (llvm::tie(I, E) = Ctx->lookup(Context, Name); I != E; ++I) { // FIXME: Cache this result in the IdResolver - if ((*I)->getIdentifierNamespace() & NS) - return MaybeConstructOverloadSet(Context, I, E); + if ((*I)->getIdentifierNamespace() & NS) { + if (NamespaceNameOnly && !isa(*I)) {} + else return MaybeConstructOverloadSet(Context, I, E); + } } Ctx = Ctx->getParent(); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 5ba2850a3f..e3c8373723 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1349,6 +1349,34 @@ void Sema::ActOnFinishNamespaceDef(DeclTy *D, SourceLocation RBrace) { PopDeclContext(); } +Sema::DeclTy *Sema::ActOnUsingDirective(Scope *S, + SourceLocation UsingLoc, + SourceLocation NamespcLoc, + const CXXScopeSpec &SS, + SourceLocation IdentLoc, + IdentifierInfo *NamespcName, + AttributeList *AttrList) { + assert(!SS.isInvalid() && "Invalid CXXScopeSpec."); + assert(NamespcName && "Invalid NamespcName."); + assert(IdentLoc.isValid() && "Invalid NamespceName location."); + + // FIXME: This still requires lot more checks, and AST support. + // Lookup namespace name. + DeclContext *DC = static_cast(SS.getScopeRep()); + Decl *NS = 0; + + if ((NS = LookupNamespaceName(NamespcName, S, DC))) { + assert(isa(NS) && "expected namespace decl"); + } else { + DiagnosticBuilder Builder = Diag(IdentLoc, diag::err_expected_namespace_name); + if (SS.isSet()) + Builder << SS.getRange(); + } + + // FIXME: We ignore AttrList for now, and delete it to avoid leak. + delete AttrList; + return 0; +} /// AddCXXDirectInitializerToDecl - This action is called immediately after /// ActOnDeclarator, when a C++ direct initializer is present. diff --git a/test/Parser/cxx-using-directive.cpp b/test/Parser/cxx-using-directive.cpp new file mode 100644 index 0000000000..026ef9034b --- /dev/null +++ b/test/Parser/cxx-using-directive.cpp @@ -0,0 +1,32 @@ +// RUN: clang -fsyntax-only -verify %s + +class A {}; + +namespace B { + namespace A {} + using namespace A ; +} + +namespace C {} + +namespace D { + + class C { + + using namespace B ; // expected-error{{expected unqualified-id}} + //FIXME: this needs better error message + }; + + namespace B {} + + using namespace C ; + using namespace B::A ; // expected-error{{expected namespace name}} + //FIXME: would be nice to note, that A is not member of D::B + using namespace ::B::A ; + using namespace ::D::C ; // expected-error{{expected namespace name}} +} + +using namespace ! ; // expected-error{{expected namespace name}} +using namespace A ; // expected-error{{expected namespace name}} +using namespace ::A B ; // expected-error{{expected ';' after namespace name}} + diff --git a/www/cxx_status.html b/www/cxx_status.html index 0296c1b4b5..a4a6741b34 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -963,7 +963,14 @@ welcome!

      7.3.1.2 [namespace.memdef]     7.3.2 [namespace.alias]     7.3.3 [namespace.udecl] -    7.3.4 [namespace.udir] + +     7.3.4[namespace.udir] + ✓ + + + + +   7.4 [dcl.asm]   7.5 [dcl.link] 8 [dcl.decl]