diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index ef7b0ded16..1e694f2b99 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -130,6 +130,9 @@ def err_using_decl_conflict_reverse : Error< "declaration conflicts with target of using declaration already in scope">; def note_using_decl : Note<"%select{|previous }0using declaration">; +def warn_access_decl_deprecated : Warning< + "access declarations are deprecated; use using declarations instead">; + def err_invalid_thread : Error< "'__thread' is only allowed on variable declarations">; def err_thread_non_global : Error< diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index b5d5764a63..011d611782 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1264,6 +1264,10 @@ public: /// /// \param AS the currently-active access specifier. /// + /// \param HasUsingKeyword true if this was declared with an + /// explicit 'using' keyword (i.e. if this is technically a using + /// declaration, not an access declaration) + /// /// \param UsingLoc the location of the 'using' keyword. /// /// \param SS the nested-name-specifier that precedes the name. @@ -1281,6 +1285,7 @@ public: /// \returns a representation of the using declaration. virtual DeclPtrTy ActOnUsingDeclaration(Scope *CurScope, AccessSpecifier AS, + bool HasUsingKeyword, SourceLocation UsingLoc, const CXXScopeSpec &SS, UnqualifiedId &Name, diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp index aa0b89b1a3..8b207fab43 100644 --- a/lib/Parse/MinimalAction.cpp +++ b/lib/Parse/MinimalAction.cpp @@ -45,6 +45,7 @@ Action::DeclPtrTy Action::ActOnUsingDirective(Scope *CurScope, // Defined out-of-line here because of dependency on AttributeList Action::DeclPtrTy Action::ActOnUsingDeclaration(Scope *CurScope, AccessSpecifier AS, + bool HasUsingKeyword, SourceLocation UsingLoc, const CXXScopeSpec &SS, UnqualifiedId &Name, diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 34ea9c7d9b..78336bbfb1 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -357,7 +357,7 @@ Parser::DeclPtrTy Parser::ParseUsingDeclaration(unsigned Context, AttrList ? "attributes list" : "using declaration", tok::semi); - return Actions.ActOnUsingDeclaration(CurScope, AS, UsingLoc, SS, Name, + return Actions.ActOnUsingDeclaration(CurScope, AS, true, UsingLoc, SS, Name, AttrList, IsTypeName, TypenameLoc); } @@ -1065,6 +1065,46 @@ void Parser::HandleMemberFunctionDefaultArgs(Declarator& DeclaratorInfo, /// void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, const ParsedTemplateInfo &TemplateInfo) { + // Access declarations. + if (!TemplateInfo.Kind && + (Tok.is(tok::identifier) || Tok.is(tok::coloncolon)) && + TryAnnotateCXXScopeToken() && + Tok.is(tok::annot_cxxscope)) { + bool isAccessDecl = false; + if (NextToken().is(tok::identifier)) + isAccessDecl = GetLookAheadToken(2).is(tok::semi); + else + isAccessDecl = NextToken().is(tok::kw_operator); + + if (isAccessDecl) { + // Collect the scope specifier token we annotated earlier. + CXXScopeSpec SS; + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType*/ 0, false); + + // Try to parse an unqualified-id. + UnqualifiedId Name; + if (ParseUnqualifiedId(SS, false, true, true, /*ObjectType*/ 0, Name)) { + SkipUntil(tok::semi); + return; + } + + // TODO: recover from mistakenly-qualified operator declarations. + if (ExpectAndConsume(tok::semi, + diag::err_expected_semi_after, + "access declaration", + tok::semi)) + return; + + Actions.ActOnUsingDeclaration(CurScope, AS, + false, SourceLocation(), + SS, Name, + /* AttrList */ 0, + /* IsTypeName */ false, + SourceLocation()); + return; + } + } + // static_assert-declaration if (Tok.is(tok::kw_static_assert)) { // FIXME: Check for templates diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 88de8da2b3..aed7a9bc84 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1768,6 +1768,7 @@ public: virtual DeclPtrTy ActOnUsingDeclaration(Scope *CurScope, AccessSpecifier AS, + bool HasUsingKeyword, SourceLocation UsingLoc, const CXXScopeSpec &SS, UnqualifiedId &Name, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 70ee24c68e..75021c2850 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2876,6 +2876,7 @@ void Sema::PushUsingDirective(Scope *S, UsingDirectiveDecl *UDir) { Sema::DeclPtrTy Sema::ActOnUsingDeclaration(Scope *S, AccessSpecifier AS, + bool HasUsingKeyword, SourceLocation UsingLoc, const CXXScopeSpec &SS, UnqualifiedId &Name, @@ -2914,6 +2915,18 @@ Sema::DeclPtrTy Sema::ActOnUsingDeclaration(Scope *S, if (!TargetName) return DeclPtrTy(); + // Warn about using declarations. + // TODO: store that the declaration was written without 'using' and + // talk about access decls instead of using decls in the + // diagnostics. + if (!HasUsingKeyword) { + UsingLoc = Name.getSourceRange().getBegin(); + + Diag(UsingLoc, diag::warn_access_decl_deprecated) + << CodeModificationHint::CreateInsertion(SS.getRange().getBegin(), + "using "); + } + NamedDecl *UD = BuildUsingDeclaration(S, AS, UsingLoc, SS, Name.getSourceRange().getBegin(), TargetName, AttrList, diff --git a/test/CXX/class.access/class.access.dcl/p1.cpp b/test/CXX/class.access/class.access.dcl/p1.cpp new file mode 100644 index 0000000000..043a9bfb4e --- /dev/null +++ b/test/CXX/class.access/class.access.dcl/p1.cpp @@ -0,0 +1,199 @@ +// RUN: clang-cc -fsyntax-only -verify + +// This is just the test for [namespace.udecl]p4 with 'using' +// uniformly stripped out. + +// C++03 [namespace.udecl]p4: +// A using-declaration used as a member-declaration shall refer to a +// member of a base class of the class being defined, shall refer to +// a member of an anonymous union that is a member of a base class +// of the class being defined, or shall refer to an enumerator for +// an enumeration type that is a member of a base class of the class +// being defined. + +// There is no directly analogous paragraph in C++0x, and the feature +// works sufficiently differently there that it needs a separate test. + +namespace test0 { + namespace NonClass { + typedef int type; + struct hiding {}; + int hiding; + static union { double union_member; }; + enum tagname { enumerator }; + } + + class Test0 { + NonClass::type; // expected-error {{not a class}} expected-warning {{access declarations are deprecated}} + NonClass::hiding; // expected-error {{not a class}} expected-warning {{access declarations are deprecated}} + NonClass::union_member; // expected-error {{not a class}} expected-warning {{access declarations are deprecated}} + NonClass::enumerator; // expected-error {{not a class}} expected-warning {{access declarations are deprecated}} + }; +} + +struct Opaque0 {}; + +namespace test1 { + struct A { + typedef int type; + struct hiding {}; // expected-note {{previous use is here}} + Opaque0 hiding; + union { double union_member; }; + enum tagname { enumerator }; + }; + + struct B : A { + A::type; // expected-warning {{access declarations are deprecated}} + A::hiding; // expected-warning {{access declarations are deprecated}} + A::union_member; // expected-warning {{access declarations are deprecated}} + A::enumerator; // expected-warning {{access declarations are deprecated}} + A::tagname; // expected-warning {{access declarations are deprecated}} + + void test0() { + type t = 0; + } + + void test1() { + typedef struct A::hiding local; + struct hiding _ = local(); + } + + void test2() { + union hiding _; // expected-error {{tag type that does not match previous}} + } + + void test3() { + char array[sizeof(union_member) == sizeof(double) ? 1 : -1]; + } + + void test4() { + enum tagname _ = enumerator; + } + + void test5() { + Opaque0 _ = hiding; + } + }; +} + +namespace test2 { + struct A { + typedef int type; + struct hiding {}; // expected-note {{previous use is here}} + int hiding; + union { double union_member; }; + enum tagname { enumerator }; + }; + + template struct B : A { + A::type; // expected-warning {{access declarations are deprecated}} + A::hiding; // expected-warning {{access declarations are deprecated}} + A::union_member; // expected-warning {{access declarations are deprecated}} + A::enumerator; // expected-warning {{access declarations are deprecated}} + A::tagname; // expected-warning {{access declarations are deprecated}} + + void test0() { + type t = 0; + } + + void test1() { + typedef struct A::hiding local; + struct hiding _ = local(); + } + + void test2() { + union hiding _; // expected-error {{tag type that does not match previous}} + } + + void test3() { + char array[sizeof(union_member) == sizeof(double) ? 1 : -1]; + } + + void test4() { + enum tagname _ = enumerator; + } + + void test5() { + Opaque0 _ = hiding; + } + }; +} + +namespace test3 { + struct hiding {}; + + template struct A { + typedef int type; // expected-note {{target of using declaration}} + struct hiding {}; + Opaque0 hiding; + union { double union_member; }; + enum tagname { enumerator }; // expected-note {{target of using declaration}} + }; + + template struct B : A { + A::type; // expected-error {{dependent using declaration resolved to type without 'typename'}} // expected-warning {{access declarations are deprecated}} + A::hiding; // expected-warning {{access declarations are deprecated}} + A::union_member; // expected-warning {{access declarations are deprecated}} + A::enumerator; // expected-warning {{access declarations are deprecated}} + A::tagname; // expected-error {{dependent using declaration resolved to type without 'typename'}} // expected-warning {{access declarations are deprecated}} + + // FIXME: re-enable these when the various bugs involving tags are fixed +#if 0 + void test1() { + typedef struct A::hiding local; + struct hiding _ = local(); + } + + void test2() { + typedef struct A::hiding local; + union hiding _ = local(); + } +#endif + + void test3() { + char array[sizeof(union_member) == sizeof(double) ? 1 : -1]; + } + +#if 0 + void test4() { + enum tagname _ = enumerator; + } +#endif + + void test5() { + Opaque0 _ = hiding; + } + }; + + template struct B; // expected-note {{in instantiation}} +} + +namespace test4 { + struct Base { + int foo(); + }; + + struct Unrelated { + int foo(); + }; + + struct Subclass : Base { + }; + + namespace InnerNS { + int foo(); + } + + // We should be able to diagnose these without instantiation. + template struct C : Base { + InnerNS::foo; // expected-error {{not a class}} expected-warning {{access declarations are deprecated}} + Base::bar; // expected-error {{no member named 'bar'}} expected-warning {{access declarations are deprecated}} + Unrelated::foo; // expected-error {{not a base class}} expected-warning {{access declarations are deprecated}} + C::foo; // legal in C++03 // expected-warning {{access declarations are deprecated}} + Subclass::foo; // legal in C++03 // expected-warning {{access declarations are deprecated}} + + int bar(); //expected-note {{target of using declaration}} + C::bar; // expected-error {{refers to its own class}} expected-warning {{access declarations are deprecated}} + }; +} +