SemaAccess.cpp revision c373d48502ca7683ab55385f5bd624d778eb288d
1//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file provides Sema routines for C++ access control semantics. 11// 12//===----------------------------------------------------------------------===// 13 14#include "Sema.h" 15#include "Lookup.h" 16#include "clang/AST/ASTContext.h" 17#include "clang/AST/CXXInheritance.h" 18#include "clang/AST/DeclCXX.h" 19#include "clang/AST/ExprCXX.h" 20 21using namespace clang; 22 23/// SetMemberAccessSpecifier - Set the access specifier of a member. 24/// Returns true on error (when the previous member decl access specifier 25/// is different from the new member decl access specifier). 26bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl, 27 NamedDecl *PrevMemberDecl, 28 AccessSpecifier LexicalAS) { 29 if (!PrevMemberDecl) { 30 // Use the lexical access specifier. 31 MemberDecl->setAccess(LexicalAS); 32 return false; 33 } 34 35 // C++ [class.access.spec]p3: When a member is redeclared its access 36 // specifier must be same as its initial declaration. 37 if (LexicalAS != AS_none && LexicalAS != PrevMemberDecl->getAccess()) { 38 Diag(MemberDecl->getLocation(), 39 diag::err_class_redeclared_with_different_access) 40 << MemberDecl << LexicalAS; 41 Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration) 42 << PrevMemberDecl << PrevMemberDecl->getAccess(); 43 44 MemberDecl->setAccess(LexicalAS); 45 return true; 46 } 47 48 MemberDecl->setAccess(PrevMemberDecl->getAccess()); 49 return false; 50} 51 52/// Find a class on the derivation path between Derived and Base that is 53/// inaccessible. If @p NoPrivileges is true, special access rights (members 54/// and friends) are not considered. 55const CXXBaseSpecifier *Sema::FindInaccessibleBase( 56 QualType Derived, QualType Base, CXXBasePaths &Paths, bool NoPrivileges) { 57 Base = Context.getCanonicalType(Base).getUnqualifiedType(); 58 assert(!Paths.isAmbiguous(Base) && 59 "Can't check base class access if set of paths is ambiguous"); 60 assert(Paths.isRecordingPaths() && 61 "Can't check base class access without recorded paths"); 62 63 64 const CXXBaseSpecifier *InaccessibleBase = 0; 65 66 const CXXRecordDecl *CurrentClassDecl = 0; 67 if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(getCurFunctionDecl())) 68 CurrentClassDecl = MD->getParent(); 69 70 for (CXXBasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end(); 71 Path != PathsEnd; ++Path) { 72 73 bool FoundInaccessibleBase = false; 74 75 for (CXXBasePath::const_iterator Element = Path->begin(), 76 ElementEnd = Path->end(); Element != ElementEnd; ++Element) { 77 const CXXBaseSpecifier *Base = Element->Base; 78 79 switch (Base->getAccessSpecifier()) { 80 default: 81 assert(0 && "invalid access specifier"); 82 case AS_public: 83 // Nothing to do. 84 break; 85 case AS_private: 86 // FIXME: Check if the current function/class is a friend. 87 if (NoPrivileges || CurrentClassDecl != Element->Class) 88 FoundInaccessibleBase = true; 89 break; 90 case AS_protected: 91 // FIXME: Implement 92 break; 93 } 94 95 if (FoundInaccessibleBase) { 96 InaccessibleBase = Base; 97 break; 98 } 99 } 100 101 if (!FoundInaccessibleBase) { 102 // We found a path to the base, our work here is done. 103 return 0; 104 } 105 } 106 107 assert(InaccessibleBase && "no path found, but no inaccessible base"); 108 return InaccessibleBase; 109} 110 111/// CheckBaseClassAccess - Check that a derived class can access its base class 112/// and report an error if it can't. [class.access.base] 113bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base, 114 unsigned InaccessibleBaseID, 115 CXXBasePaths &Paths, SourceLocation AccessLoc, 116 DeclarationName Name) { 117 118 if (!getLangOptions().AccessControl) 119 return false; 120 const CXXBaseSpecifier *InaccessibleBase = FindInaccessibleBase( 121 Derived, Base, Paths); 122 123 if (InaccessibleBase) { 124 Diag(AccessLoc, InaccessibleBaseID) 125 << Derived << Base << Name; 126 127 AccessSpecifier AS = InaccessibleBase->getAccessSpecifierAsWritten(); 128 129 // If there's no written access specifier, then the inheritance specifier 130 // is implicitly private. 131 if (AS == AS_none) 132 Diag(InaccessibleBase->getSourceRange().getBegin(), 133 diag::note_inheritance_implicitly_private_here); 134 else 135 Diag(InaccessibleBase->getSourceRange().getBegin(), 136 diag::note_inheritance_specifier_here) << AS; 137 138 return true; 139 } 140 141 return false; 142} 143 144/// Diagnose the path which caused the given declaration to become 145/// inaccessible. 146static void DiagnoseAccessPath(Sema &S, const LookupResult &R, NamedDecl *D, 147 AccessSpecifier Access) { 148 // Easy case: the decl's natural access determined its path access. 149 if (Access == D->getAccess() || D->getAccess() == AS_private) { 150 S.Diag(D->getLocation(), diag::note_access_natural) 151 << (unsigned) (Access == AS_protected); 152 return; 153 } 154 155 // TODO: flesh this out 156 S.Diag(D->getLocation(), diag::note_access_constrained_by_path) 157 << (unsigned) (Access == AS_protected); 158} 159 160/// Checks access to the given declaration in the current context. 161/// 162/// \param R the means via which the access was made; must have a naming 163/// class set 164/// \param D the declaration accessed 165/// \param Access the best access along any inheritance path from the 166/// naming class to the declaration. AS_none means the path is impossible 167bool Sema::CheckAccess(const LookupResult &R, NamedDecl *D, 168 AccessSpecifier Access) { 169 assert(R.getNamingClass() && "performing access check without naming class"); 170 171 // If the access path is public, it's accessible everywhere. 172 if (Access == AS_public) 173 return false; 174 175 // Otherwise, derive the current class context. 176 DeclContext *DC = CurContext; 177 while (isa<CXXRecordDecl>(DC) && 178 cast<CXXRecordDecl>(DC)->isAnonymousStructOrUnion()) 179 DC = DC->getParent(); 180 181 CXXRecordDecl *CurRecord; 182 if (isa<CXXRecordDecl>(DC)) 183 CurRecord = cast<CXXRecordDecl>(DC); 184 else if (isa<CXXMethodDecl>(DC)) 185 CurRecord = cast<CXXMethodDecl>(DC)->getParent(); 186 else { 187 Diag(R.getNameLoc(), diag::err_access_outside_class) 188 << (Access == AS_protected); 189 DiagnoseAccessPath(*this, R, D, Access); 190 return true; 191 } 192 193 CXXRecordDecl *NamingClass = R.getNamingClass(); 194 while (NamingClass->isAnonymousStructOrUnion()) 195 // This should be guaranteed by the fact that the decl has 196 // non-public access. If not, we should make it guaranteed! 197 NamingClass = cast<CXXRecordDecl>(NamingClass); 198 199 // White-list accesses from within the declaring class. 200 if (Access != AS_none && 201 CurRecord->getCanonicalDecl() == NamingClass->getCanonicalDecl()) 202 return false; 203 204 // Protected access. 205 if (Access == AS_protected) { 206 // FIXME: implement [class.protected]p1 207 if (CurRecord->isDerivedFrom(NamingClass)) 208 return false; 209 210 // FIXME: dependent classes 211 } 212 213 // FIXME: friends 214 215 // Okay, it's a bad access, reject it. 216 217 218 CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext()); 219 220 if (Access == AS_protected) { 221 Diag(R.getNameLoc(), diag::err_access_protected) 222 << Context.getTypeDeclType(DeclaringClass) 223 << Context.getTypeDeclType(CurRecord); 224 DiagnoseAccessPath(*this, R, D, Access); 225 return true; 226 } 227 228 assert(Access == AS_private || Access == AS_none); 229 Diag(R.getNameLoc(), diag::err_access_private) 230 << Context.getTypeDeclType(DeclaringClass) 231 << Context.getTypeDeclType(CurRecord); 232 DiagnoseAccessPath(*this, R, D, Access); 233 return true; 234} 235 236bool Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, 237 NamedDecl *D, AccessSpecifier Access) { 238 if (!getLangOptions().AccessControl || !E->getNamingClass()) 239 return false; 240 241 // Fake up a lookup result. 242 LookupResult R(*this, E->getName(), E->getNameLoc(), LookupOrdinaryName); 243 R.suppressDiagnostics(); 244 245 R.setNamingClass(E->getNamingClass()); 246 R.addDecl(D, Access); 247 248 // FIXME: protected check (triggers for member-address expressions) 249 250 return CheckAccess(R, D, Access); 251} 252 253/// Perform access-control checking on a previously-unresolved member 254/// access which has now been resolved to a member. 255bool Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E, 256 NamedDecl *D, AccessSpecifier Access) { 257 if (!getLangOptions().AccessControl) 258 return false; 259 260 // Fake up a lookup result. 261 LookupResult R(*this, E->getMemberName(), E->getMemberLoc(), 262 LookupOrdinaryName); 263 R.suppressDiagnostics(); 264 265 R.setNamingClass(E->getNamingClass()); 266 R.addDecl(D, Access); 267 268 if (CheckAccess(R, D, Access)) 269 return true; 270 271 // FIXME: protected check 272 273 return false; 274} 275 276/// Checks access to all the declarations in the given result set. 277void Sema::CheckAccess(const LookupResult &R) { 278 for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) 279 CheckAccess(R, *I, I.getAccess()); 280} 281