]> granicus.if.org Git - clang/commitdiff
Implement checking for base class access. Right now it's overly conservative but...
authorAnders Carlsson <andersca@mac.com>
Fri, 27 Mar 2009 06:03:27 +0000 (06:03 +0000)
committerAnders Carlsson <andersca@mac.com>
Fri, 27 Mar 2009 06:03:27 +0000 (06:03 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67827 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaAccess.cpp
test/SemaCXX/access-base-class.cpp [new file with mode: 0644]

index 05b8f512e182ab7a677ca6da155b320937a278ef..c684f4ef52db8ee83669cb9c57ff3f3f9e6fa507 100644 (file)
@@ -1297,6 +1297,13 @@ def err_memptr_conv_via_virtual : Error<
   "conversion from pointer to member of class %0 to pointer to member "
   "of class %1 via virtual base %2 is not allowed">;
 
+// C++ access control
+def err_conv_to_inaccessible_base : Error<
+  "conversion from %0 to inaccessible base class %1">;
+def note_inheritance_specifier_here : Note<
+  "'%0' inheritance specifier here">;
+def note_inheritance_implicitly_private_here : Note<
+  "inheritance is implicitly 'private'">;
 
 // C++ member name lookup
 def err_ambiguous_member_multiple_subobjects : Error<
index e44ef5f9ea48a88c7ebea4c5521a79709f2b65e1..85e1c2ed883b497464ca47fcf7125dc53c173378 100644 (file)
@@ -11,7 +11,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "SemaInherit.h"
 #include "Sema.h"
+#include "clang/AST/ASTContext.h"
 using namespace clang;
 
 /// SetMemberAccessSpecifier - Set the access specifier of a member.
@@ -45,5 +47,68 @@ bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
 /// and report an error if it can't. [class.access.base]
 bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base, 
                                 BasePaths& Paths, SourceLocation AccessLoc) {
+  Base = Context.getCanonicalType(Base).getUnqualifiedType();
+  assert(!Paths.isAmbiguous(Base) && 
+         "Can't check base class access if set of paths is ambiguous");
+  assert(Paths.isRecordingPaths() &&
+         "Can't check base class access without recorded paths");
+  
+  const CXXBaseSpecifier *InacessibleBase = 0;
+
+  for (BasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end(); 
+      Path != PathsEnd; ++Path) {
+    
+    bool FoundInaccessibleBase = false;
+    
+    for (BasePath::const_iterator Element = Path->begin(), 
+         ElementEnd = Path->end(); Element != ElementEnd; ++Element) {
+      const CXXBaseSpecifier *Base = Element->Base;
+      
+      switch (Base->getAccessSpecifier()) {
+      default:
+        assert(0 && "invalid access specifier");
+      case AS_public:
+        // Nothing to do.
+        break;
+      case AS_private:
+        // FIXME: Check if the current function is a member or friend.
+        FoundInaccessibleBase = true;
+        break;
+      case AS_protected:  
+        // FIXME: Implement
+        break;
+      }
+      
+      if (FoundInaccessibleBase) {
+        InacessibleBase = Base;
+        break;
+      }
+    }
+    
+    if (!FoundInaccessibleBase) {
+      // We found a path to the base, our work here is done.
+      InacessibleBase = 0;
+      break;
+    }
+  }
+
+  if (InacessibleBase) {
+    Diag(AccessLoc, diag::err_conv_to_inaccessible_base) 
+      << Derived << Base;
+
+    AccessSpecifier AS = InacessibleBase->getAccessSpecifierAsWritten();
+    
+    // If there's no written access specifier, then the inheritance specifier
+    // is implicitly private.
+    if (AS == AS_none)
+      Diag(InacessibleBase->getSourceRange().getBegin(),
+           diag::note_inheritance_implicitly_private_here);
+    else
+      Diag(InacessibleBase->getSourceRange().getBegin(),
+           diag::note_inheritance_specifier_here) << AS;
+
+    return true;
+  }
+  
   return false;
 }
diff --git a/test/SemaCXX/access-base-class.cpp b/test/SemaCXX/access-base-class.cpp
new file mode 100644 (file)
index 0000000..2958675
--- /dev/null
@@ -0,0 +1,51 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+namespace T1 {
+  
+class A { };
+class B : private A { }; // expected-note {{'private' inheritance specifier here}}
+
+void f(B* b) {
+  A *a = b; // expected-error{{conversion from 'class T1::B' to inaccessible base class 'class T1::A'}} \
+               expected-error{{incompatible type initializing 'class T1::B *', expected 'class T1::A *'}}
+}
+
+}
+
+namespace T2 { 
+
+class A { };
+class B : A { }; // expected-note {{inheritance is implicitly 'private'}}
+
+void f(B* b) {
+  A *a = b; // expected-error {{conversion from 'class T2::B' to inaccessible base class 'class T2::A'}} \
+               expected-error {{incompatible type initializing 'class T2::B *', expected 'class T2::A *'}}
+}
+
+}
+
+namespace T3 {
+
+class A { };
+class B : public A { }; 
+
+void f(B* b) {
+  A *a = b;
+}
+
+}
+
+namespace T4 {
+
+class A {};
+
+class B : private virtual A {};
+class C : public virtual A {};
+
+class D : public B, public C {};
+
+void f(D *d) {
+  // This takes the D->C->B->A path.
+  A *a = d;
+}
+
+}