]> granicus.if.org Git - clang/commitdiff
Implement access declarations. Most of the work here is parsing them, which
authorJohn McCall <rjmccall@apple.com>
Fri, 11 Dec 2009 02:10:03 +0000 (02:10 +0000)
committerJohn McCall <rjmccall@apple.com>
Fri, 11 Dec 2009 02:10:03 +0000 (02:10 +0000)
is difficult because they're so terribly, terribly ambiguous.

We implement access declarations in terms of using declarations, which is
quite reasonable.  However, we should really persist the access/using
distinction in the AST and use the appropriate name in diagnostics.  This
isn't a priority, so I'll just file a PR and hope someone else does it. :)

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@91095 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Action.h
lib/Parse/MinimalAction.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
test/CXX/class.access/class.access.dcl/p1.cpp [new file with mode: 0644]

index ef7b0ded166f7f5917c789e5fcef99298a52a453..1e694f2b998c97bafe9b080c789979ffcfb08e9e 100644 (file)
@@ -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<
index b5d5764a63cb0aa800ee5de26e963ccbe8e03e77..011d6117825b8edd5b36b36a77eec9c0a06dd549 100644 (file)
@@ -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,
index aa0b89b1a3a23f42212b5a26840ea8298e93f791..8b207fab436c979d2780b7804e1ff5308692c9ce 100644 (file)
@@ -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,
index 34ea9c7d9b025d6c51a5cf0cfd60499a6f1059cf..78336bbfb1585e21bbe59b7f24ebfc78ddc29b1d 100644 (file)
@@ -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
index 88de8da2b381e6919ad1208cdf3d789274ed7613..aed7a9bc848e0d17709d8c4ff1c8fa7f18425492 100644 (file)
@@ -1768,6 +1768,7 @@ public:
 
   virtual DeclPtrTy ActOnUsingDeclaration(Scope *CurScope,
                                           AccessSpecifier AS,
+                                          bool HasUsingKeyword,
                                           SourceLocation UsingLoc,
                                           const CXXScopeSpec &SS,
                                           UnqualifiedId &Name,
index 70ee24c68e8fab1f2a9393b6bc3c8627b915c6d3..75021c28509fabc705f5fdd2dd180dc0189e6689 100644 (file)
@@ -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 (file)
index 0000000..043a9bf
--- /dev/null
@@ -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 <class T> 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 <class T> 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 <class T> struct B : A<T> {
+    A<T>::type; // expected-error {{dependent using declaration resolved to type without 'typename'}} // expected-warning {{access declarations are deprecated}}
+    A<T>::hiding; // expected-warning {{access declarations are deprecated}}
+    A<T>::union_member; // expected-warning {{access declarations are deprecated}}
+    A<T>::enumerator; // expected-warning {{access declarations are deprecated}}
+    A<T>::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<T>::hiding local;
+      struct hiding _ = local();
+    }
+
+    void test2() {
+      typedef struct A<T>::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<int>; // 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 <class T> 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}}
+  };
+}
+