SemaAccess.cpp revision 45572f4548ea1a56879f2fbf1ec542bfe774156f
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 // If we're currently parsing a top-level declaration, delay 176 // diagnostics. This is the only case where parsing a declaration 177 // can actually change our effective context for the purposes of 178 // access control. 179 if (CurContext->isFileContext() && ParsingDeclDepth) { 180 DelayedDiagnostics.push_back( 181 DelayedDiagnostic::makeAccess(R.getNameLoc(), D, Access, 182 R.getNamingClass())); 183 return false; 184 } 185 186 return CheckEffectiveAccess(CurContext, R, D, Access); 187} 188 189/// Checks access from the given effective context. 190bool Sema::CheckEffectiveAccess(DeclContext *EffectiveContext, 191 const LookupResult &R, 192 NamedDecl *D, AccessSpecifier Access) { 193 DeclContext *DC = EffectiveContext; 194 while (isa<CXXRecordDecl>(DC) && 195 cast<CXXRecordDecl>(DC)->isAnonymousStructOrUnion()) 196 DC = DC->getParent(); 197 198 CXXRecordDecl *CurRecord; 199 if (isa<CXXRecordDecl>(DC)) 200 CurRecord = cast<CXXRecordDecl>(DC); 201 else if (isa<CXXMethodDecl>(DC)) 202 CurRecord = cast<CXXMethodDecl>(DC)->getParent(); 203 else { 204 Diag(R.getNameLoc(), diag::err_access_outside_class) 205 << (Access == AS_protected); 206 DiagnoseAccessPath(*this, R, D, Access); 207 return true; 208 } 209 210 CXXRecordDecl *NamingClass = R.getNamingClass(); 211 while (NamingClass->isAnonymousStructOrUnion()) 212 // This should be guaranteed by the fact that the decl has 213 // non-public access. If not, we should make it guaranteed! 214 NamingClass = cast<CXXRecordDecl>(NamingClass); 215 216 // White-list accesses from within the declaring class. 217 if (Access != AS_none && 218 CurRecord->getCanonicalDecl() == NamingClass->getCanonicalDecl()) 219 return false; 220 221 // Protected access. 222 if (Access == AS_protected) { 223 // FIXME: implement [class.protected]p1 224 if (CurRecord->isDerivedFrom(NamingClass)) 225 return false; 226 227 // FIXME: dependent classes 228 } 229 230 // FIXME: friends 231 232 // Okay, it's a bad access, reject it. 233 234 235 CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext()); 236 237 if (Access == AS_protected) { 238 Diag(R.getNameLoc(), diag::err_access_protected) 239 << Context.getTypeDeclType(DeclaringClass) 240 << Context.getTypeDeclType(CurRecord); 241 DiagnoseAccessPath(*this, R, D, Access); 242 return true; 243 } 244 245 assert(Access == AS_private || Access == AS_none); 246 Diag(R.getNameLoc(), diag::err_access_private) 247 << Context.getTypeDeclType(DeclaringClass) 248 << Context.getTypeDeclType(CurRecord); 249 DiagnoseAccessPath(*this, R, D, Access); 250 return true; 251} 252 253void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) { 254 NamedDecl *D = DD.AccessData.Decl; 255 256 // Fake up a lookup result. 257 LookupResult R(*this, D->getDeclName(), DD.Loc, LookupOrdinaryName); 258 R.suppressDiagnostics(); 259 R.setNamingClass(DD.AccessData.NamingClass); 260 261 // Pretend we did this from the context of the newly-parsed 262 // declaration. 263 DeclContext *EffectiveContext = Ctx->getDeclContext(); 264 265 if (CheckEffectiveAccess(EffectiveContext, R, D, DD.AccessData.Access)) 266 DD.Triggered = true; 267} 268 269bool Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, 270 NamedDecl *D, AccessSpecifier Access) { 271 if (!getLangOptions().AccessControl || !E->getNamingClass()) 272 return false; 273 274 // Fake up a lookup result. 275 LookupResult R(*this, E->getName(), E->getNameLoc(), LookupOrdinaryName); 276 R.suppressDiagnostics(); 277 278 R.setNamingClass(E->getNamingClass()); 279 R.addDecl(D, Access); 280 281 // FIXME: protected check (triggers for member-address expressions) 282 283 return CheckAccess(R, D, Access); 284} 285 286/// Perform access-control checking on a previously-unresolved member 287/// access which has now been resolved to a member. 288bool Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E, 289 NamedDecl *D, AccessSpecifier Access) { 290 if (!getLangOptions().AccessControl) 291 return false; 292 293 // Fake up a lookup result. 294 LookupResult R(*this, E->getMemberName(), E->getMemberLoc(), 295 LookupOrdinaryName); 296 R.suppressDiagnostics(); 297 298 R.setNamingClass(E->getNamingClass()); 299 R.addDecl(D, Access); 300 301 if (CheckAccess(R, D, Access)) 302 return true; 303 304 // FIXME: protected check 305 306 return false; 307} 308 309/// Checks access to an overloaded member operator. 310bool Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, 311 Expr *ObjectExpr, 312 NamedDecl *MemberOperator, 313 AccessSpecifier Access) { 314 if (!getLangOptions().AccessControl) 315 return false; 316 317 const RecordType *RT = ObjectExpr->getType()->getAs<RecordType>(); 318 assert(RT && "found member operator but object expr not of record type"); 319 CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl()); 320 321 LookupResult R(*this, DeclarationName(), OpLoc, LookupOrdinaryName); 322 R.suppressDiagnostics(); 323 324 R.setNamingClass(NamingClass); 325 if (CheckAccess(R, MemberOperator, Access)) 326 return true; 327 328 // FIXME: protected check 329 330 return false; 331} 332 333/// Checks access to all the declarations in the given result set. 334void Sema::CheckAccess(const LookupResult &R) { 335 for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) 336 CheckAccess(R, *I, I.getAccess()); 337} 338