blob: 9e1ab8cefd31ea4e325d3e7a8216f0b4d7a71487 [file] [log] [blame]
Anders Carlsson29f006b2009-03-27 05:05:05 +00001//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===//
Anders Carlsson60d6b0d2009-03-27 04:43:36 +00002//
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//===----------------------------------------------------------------------===//
Anders Carlssonc60e8882009-03-27 04:54:36 +000013
14#include "Sema.h"
John McCall92f88312010-01-23 00:46:32 +000015#include "Lookup.h"
Anders Carlssonc4f1e872009-03-27 06:03:27 +000016#include "clang/AST/ASTContext.h"
Douglas Gregora8f32e02009-10-06 17:59:45 +000017#include "clang/AST/CXXInheritance.h"
18#include "clang/AST/DeclCXX.h"
John McCallc373d482010-01-27 01:50:18 +000019#include "clang/AST/ExprCXX.h"
20
Anders Carlssonc60e8882009-03-27 04:54:36 +000021using namespace clang;
22
Anders Carlsson29f006b2009-03-27 05:05:05 +000023/// 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).
Mike Stump1eb44332009-09-09 15:08:12 +000026bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
Anders Carlssonc60e8882009-03-27 04:54:36 +000027 NamedDecl *PrevMemberDecl,
28 AccessSpecifier LexicalAS) {
29 if (!PrevMemberDecl) {
30 // Use the lexical access specifier.
31 MemberDecl->setAccess(LexicalAS);
32 return false;
33 }
Mike Stump1eb44332009-09-09 15:08:12 +000034
Anders Carlssonc60e8882009-03-27 04:54:36 +000035 // 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()) {
Mike Stump1eb44332009-09-09 15:08:12 +000038 Diag(MemberDecl->getLocation(),
39 diag::err_class_redeclared_with_different_access)
Anders Carlssonc60e8882009-03-27 04:54:36 +000040 << MemberDecl << LexicalAS;
41 Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration)
42 << PrevMemberDecl << PrevMemberDecl->getAccess();
John McCall44e067b2009-12-23 00:37:40 +000043
44 MemberDecl->setAccess(LexicalAS);
Anders Carlssonc60e8882009-03-27 04:54:36 +000045 return true;
46 }
Mike Stump1eb44332009-09-09 15:08:12 +000047
Anders Carlssonc60e8882009-03-27 04:54:36 +000048 MemberDecl->setAccess(PrevMemberDecl->getAccess());
49 return false;
50}
Anders Carlsson29f006b2009-03-27 05:05:05 +000051
Sebastian Redl726212f2009-07-18 14:32:15 +000052/// 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(
Douglas Gregora8f32e02009-10-06 17:59:45 +000056 QualType Derived, QualType Base, CXXBasePaths &Paths, bool NoPrivileges) {
Anders Carlssonc4f1e872009-03-27 06:03:27 +000057 Base = Context.getCanonicalType(Base).getUnqualifiedType();
Mike Stump1eb44332009-09-09 15:08:12 +000058 assert(!Paths.isAmbiguous(Base) &&
Anders Carlssonc4f1e872009-03-27 06:03:27 +000059 "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");
Anders Carlssonc4f1e872009-03-27 06:03:27 +000062
Sebastian Redl726212f2009-07-18 14:32:15 +000063
64 const CXXBaseSpecifier *InaccessibleBase = 0;
65
66 const CXXRecordDecl *CurrentClassDecl = 0;
Anders Carlssonf8080a32009-03-27 19:01:12 +000067 if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(getCurFunctionDecl()))
68 CurrentClassDecl = MD->getParent();
69
Douglas Gregora8f32e02009-10-06 17:59:45 +000070 for (CXXBasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end();
Anders Carlssonc4f1e872009-03-27 06:03:27 +000071 Path != PathsEnd; ++Path) {
Sebastian Redl726212f2009-07-18 14:32:15 +000072
Anders Carlssonc4f1e872009-03-27 06:03:27 +000073 bool FoundInaccessibleBase = false;
Sebastian Redl726212f2009-07-18 14:32:15 +000074
Douglas Gregora8f32e02009-10-06 17:59:45 +000075 for (CXXBasePath::const_iterator Element = Path->begin(),
Anders Carlssonc4f1e872009-03-27 06:03:27 +000076 ElementEnd = Path->end(); Element != ElementEnd; ++Element) {
77 const CXXBaseSpecifier *Base = Element->Base;
Sebastian Redl726212f2009-07-18 14:32:15 +000078
Anders Carlssonc4f1e872009-03-27 06:03:27 +000079 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:
Anders Carlssonf8080a32009-03-27 19:01:12 +000086 // FIXME: Check if the current function/class is a friend.
Sebastian Redl726212f2009-07-18 14:32:15 +000087 if (NoPrivileges || CurrentClassDecl != Element->Class)
Anders Carlssonf8080a32009-03-27 19:01:12 +000088 FoundInaccessibleBase = true;
Anders Carlssonc4f1e872009-03-27 06:03:27 +000089 break;
Sebastian Redl726212f2009-07-18 14:32:15 +000090 case AS_protected:
Anders Carlsson14734f72009-03-28 04:17:27 +000091 // FIXME: Implement
92 break;
Anders Carlssonc4f1e872009-03-27 06:03:27 +000093 }
Sebastian Redl726212f2009-07-18 14:32:15 +000094
Anders Carlssonc4f1e872009-03-27 06:03:27 +000095 if (FoundInaccessibleBase) {
Sebastian Redl726212f2009-07-18 14:32:15 +000096 InaccessibleBase = Base;
Anders Carlssonc4f1e872009-03-27 06:03:27 +000097 break;
98 }
99 }
Sebastian Redl726212f2009-07-18 14:32:15 +0000100
Anders Carlssonc4f1e872009-03-27 06:03:27 +0000101 if (!FoundInaccessibleBase) {
102 // We found a path to the base, our work here is done.
Sebastian Redl726212f2009-07-18 14:32:15 +0000103 return 0;
Anders Carlssonc4f1e872009-03-27 06:03:27 +0000104 }
105 }
106
Sebastian Redl726212f2009-07-18 14:32:15 +0000107 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]
Mike Stump1eb44332009-09-09 15:08:12 +0000113bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base,
Sebastian Redl726212f2009-07-18 14:32:15 +0000114 unsigned InaccessibleBaseID,
Douglas Gregora8f32e02009-10-06 17:59:45 +0000115 CXXBasePaths &Paths, SourceLocation AccessLoc,
Sebastian Redl726212f2009-07-18 14:32:15 +0000116 DeclarationName Name) {
117
118 if (!getLangOptions().AccessControl)
119 return false;
120 const CXXBaseSpecifier *InaccessibleBase = FindInaccessibleBase(
121 Derived, Base, Paths);
122
123 if (InaccessibleBase) {
Mike Stump1eb44332009-09-09 15:08:12 +0000124 Diag(AccessLoc, InaccessibleBaseID)
Anders Carlssond8f9cb02009-05-13 21:11:42 +0000125 << Derived << Base << Name;
Anders Carlssonc4f1e872009-03-27 06:03:27 +0000126
Sebastian Redl726212f2009-07-18 14:32:15 +0000127 AccessSpecifier AS = InaccessibleBase->getAccessSpecifierAsWritten();
128
Anders Carlssonc4f1e872009-03-27 06:03:27 +0000129 // If there's no written access specifier, then the inheritance specifier
130 // is implicitly private.
131 if (AS == AS_none)
Sebastian Redl726212f2009-07-18 14:32:15 +0000132 Diag(InaccessibleBase->getSourceRange().getBegin(),
Anders Carlssonc4f1e872009-03-27 06:03:27 +0000133 diag::note_inheritance_implicitly_private_here);
134 else
Sebastian Redl726212f2009-07-18 14:32:15 +0000135 Diag(InaccessibleBase->getSourceRange().getBegin(),
Anders Carlssonc4f1e872009-03-27 06:03:27 +0000136 diag::note_inheritance_specifier_here) << AS;
137
138 return true;
139 }
Sebastian Redl726212f2009-07-18 14:32:15 +0000140
Anders Carlsson29f006b2009-03-27 05:05:05 +0000141 return false;
142}
John McCall92f88312010-01-23 00:46:32 +0000143
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
John McCall2f514482010-01-27 03:50:35 +0000175 // 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;
John McCall92f88312010-01-23 00:46:32 +0000194 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
John McCall2f514482010-01-27 03:50:35 +0000253void 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
John McCallc373d482010-01-27 01:50:18 +0000269bool 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
John McCall68c6c9a2010-02-02 09:10:11 +0000309bool Sema::CheckDestructorAccess(SourceLocation Loc, const RecordType *RT) {
John McCall4f9506a2010-02-02 08:45:54 +0000310 if (!getLangOptions().AccessControl)
311 return false;
312
John McCall68c6c9a2010-02-02 09:10:11 +0000313 CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
John McCall4f9506a2010-02-02 08:45:54 +0000314 CXXDestructorDecl *Dtor = NamingClass->getDestructor(Context);
315
316 AccessSpecifier Access = Dtor->getAccess();
317 if (Access == AS_public)
318 return false;
319
320 LookupResult R(*this, Dtor->getDeclName(), Loc, LookupOrdinaryName);
321 R.suppressDiagnostics();
322
323 R.setNamingClass(NamingClass);
324 return CheckAccess(R, Dtor, Access);
325
326 // FIXME: protected check
327}
328
John McCallb13b7372010-02-01 03:16:54 +0000329/// Checks access to a constructor.
330bool Sema::CheckConstructorAccess(SourceLocation UseLoc,
331 CXXConstructorDecl *Constructor,
332 AccessSpecifier Access) {
333 if (!getLangOptions().AccessControl)
334 return false;
335
336 CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Constructor->getParent());
337
338 LookupResult R(*this, Constructor->getDeclName(), UseLoc, LookupOrdinaryName);
339 R.suppressDiagnostics();
340
341 R.setNamingClass(NamingClass);
342 return CheckAccess(R, Constructor, Access);
343}
344
345/// Checks access to an overloaded member operator, including
346/// conversion operators.
John McCall5357b612010-01-28 01:42:12 +0000347bool Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
348 Expr *ObjectExpr,
349 NamedDecl *MemberOperator,
350 AccessSpecifier Access) {
351 if (!getLangOptions().AccessControl)
352 return false;
353
354 const RecordType *RT = ObjectExpr->getType()->getAs<RecordType>();
355 assert(RT && "found member operator but object expr not of record type");
356 CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
357
358 LookupResult R(*this, DeclarationName(), OpLoc, LookupOrdinaryName);
359 R.suppressDiagnostics();
360
361 R.setNamingClass(NamingClass);
362 if (CheckAccess(R, MemberOperator, Access))
363 return true;
364
365 // FIXME: protected check
366
367 return false;
368}
369
John McCall92f88312010-01-23 00:46:32 +0000370/// Checks access to all the declarations in the given result set.
371void Sema::CheckAccess(const LookupResult &R) {
372 for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
373 CheckAccess(R, *I, I.getAccess());
374}