blob: 51f9e300ef90a03b30fdd5d7b4df460b55935a20 [file] [log] [blame]
Anders Carlsson4742a9c2009-03-27 05:05:05 +00001//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===//
Anders Carlsson8ed6f362009-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 Carlsson17941122009-03-27 04:54:36 +000013
14#include "Sema.h"
John McCall553c0792010-01-23 00:46:32 +000015#include "Lookup.h"
Anders Carlsson733d77f2009-03-27 06:03:27 +000016#include "clang/AST/ASTContext.h"
Douglas Gregor36d1b142009-10-06 17:59:45 +000017#include "clang/AST/CXXInheritance.h"
18#include "clang/AST/DeclCXX.h"
Anders Carlsson17941122009-03-27 04:54:36 +000019using namespace clang;
20
Anders Carlsson4742a9c2009-03-27 05:05:05 +000021/// SetMemberAccessSpecifier - Set the access specifier of a member.
22/// Returns true on error (when the previous member decl access specifier
23/// is different from the new member decl access specifier).
Mike Stump11289f42009-09-09 15:08:12 +000024bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
Anders Carlsson17941122009-03-27 04:54:36 +000025 NamedDecl *PrevMemberDecl,
26 AccessSpecifier LexicalAS) {
27 if (!PrevMemberDecl) {
28 // Use the lexical access specifier.
29 MemberDecl->setAccess(LexicalAS);
30 return false;
31 }
Mike Stump11289f42009-09-09 15:08:12 +000032
Anders Carlsson17941122009-03-27 04:54:36 +000033 // C++ [class.access.spec]p3: When a member is redeclared its access
34 // specifier must be same as its initial declaration.
35 if (LexicalAS != AS_none && LexicalAS != PrevMemberDecl->getAccess()) {
Mike Stump11289f42009-09-09 15:08:12 +000036 Diag(MemberDecl->getLocation(),
37 diag::err_class_redeclared_with_different_access)
Anders Carlsson17941122009-03-27 04:54:36 +000038 << MemberDecl << LexicalAS;
39 Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration)
40 << PrevMemberDecl << PrevMemberDecl->getAccess();
John McCall0a4bb262009-12-23 00:37:40 +000041
42 MemberDecl->setAccess(LexicalAS);
Anders Carlsson17941122009-03-27 04:54:36 +000043 return true;
44 }
Mike Stump11289f42009-09-09 15:08:12 +000045
Anders Carlsson17941122009-03-27 04:54:36 +000046 MemberDecl->setAccess(PrevMemberDecl->getAccess());
47 return false;
48}
Anders Carlsson4742a9c2009-03-27 05:05:05 +000049
Sebastian Redle644e192009-07-18 14:32:15 +000050/// Find a class on the derivation path between Derived and Base that is
51/// inaccessible. If @p NoPrivileges is true, special access rights (members
52/// and friends) are not considered.
53const CXXBaseSpecifier *Sema::FindInaccessibleBase(
Douglas Gregor36d1b142009-10-06 17:59:45 +000054 QualType Derived, QualType Base, CXXBasePaths &Paths, bool NoPrivileges) {
Anders Carlsson733d77f2009-03-27 06:03:27 +000055 Base = Context.getCanonicalType(Base).getUnqualifiedType();
Mike Stump11289f42009-09-09 15:08:12 +000056 assert(!Paths.isAmbiguous(Base) &&
Anders Carlsson733d77f2009-03-27 06:03:27 +000057 "Can't check base class access if set of paths is ambiguous");
58 assert(Paths.isRecordingPaths() &&
59 "Can't check base class access without recorded paths");
Anders Carlsson733d77f2009-03-27 06:03:27 +000060
Sebastian Redle644e192009-07-18 14:32:15 +000061
62 const CXXBaseSpecifier *InaccessibleBase = 0;
63
64 const CXXRecordDecl *CurrentClassDecl = 0;
Anders Carlssonaf06b972009-03-27 19:01:12 +000065 if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(getCurFunctionDecl()))
66 CurrentClassDecl = MD->getParent();
67
Douglas Gregor36d1b142009-10-06 17:59:45 +000068 for (CXXBasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end();
Anders Carlsson733d77f2009-03-27 06:03:27 +000069 Path != PathsEnd; ++Path) {
Sebastian Redle644e192009-07-18 14:32:15 +000070
Anders Carlsson733d77f2009-03-27 06:03:27 +000071 bool FoundInaccessibleBase = false;
Sebastian Redle644e192009-07-18 14:32:15 +000072
Douglas Gregor36d1b142009-10-06 17:59:45 +000073 for (CXXBasePath::const_iterator Element = Path->begin(),
Anders Carlsson733d77f2009-03-27 06:03:27 +000074 ElementEnd = Path->end(); Element != ElementEnd; ++Element) {
75 const CXXBaseSpecifier *Base = Element->Base;
Sebastian Redle644e192009-07-18 14:32:15 +000076
Anders Carlsson733d77f2009-03-27 06:03:27 +000077 switch (Base->getAccessSpecifier()) {
78 default:
79 assert(0 && "invalid access specifier");
80 case AS_public:
81 // Nothing to do.
82 break;
83 case AS_private:
Anders Carlssonaf06b972009-03-27 19:01:12 +000084 // FIXME: Check if the current function/class is a friend.
Sebastian Redle644e192009-07-18 14:32:15 +000085 if (NoPrivileges || CurrentClassDecl != Element->Class)
Anders Carlssonaf06b972009-03-27 19:01:12 +000086 FoundInaccessibleBase = true;
Anders Carlsson733d77f2009-03-27 06:03:27 +000087 break;
Sebastian Redle644e192009-07-18 14:32:15 +000088 case AS_protected:
Anders Carlsson72f307a2009-03-28 04:17:27 +000089 // FIXME: Implement
90 break;
Anders Carlsson733d77f2009-03-27 06:03:27 +000091 }
Sebastian Redle644e192009-07-18 14:32:15 +000092
Anders Carlsson733d77f2009-03-27 06:03:27 +000093 if (FoundInaccessibleBase) {
Sebastian Redle644e192009-07-18 14:32:15 +000094 InaccessibleBase = Base;
Anders Carlsson733d77f2009-03-27 06:03:27 +000095 break;
96 }
97 }
Sebastian Redle644e192009-07-18 14:32:15 +000098
Anders Carlsson733d77f2009-03-27 06:03:27 +000099 if (!FoundInaccessibleBase) {
100 // We found a path to the base, our work here is done.
Sebastian Redle644e192009-07-18 14:32:15 +0000101 return 0;
Anders Carlsson733d77f2009-03-27 06:03:27 +0000102 }
103 }
104
Sebastian Redle644e192009-07-18 14:32:15 +0000105 assert(InaccessibleBase && "no path found, but no inaccessible base");
106 return InaccessibleBase;
107}
108
109/// CheckBaseClassAccess - Check that a derived class can access its base class
110/// and report an error if it can't. [class.access.base]
Mike Stump11289f42009-09-09 15:08:12 +0000111bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base,
Sebastian Redle644e192009-07-18 14:32:15 +0000112 unsigned InaccessibleBaseID,
Douglas Gregor36d1b142009-10-06 17:59:45 +0000113 CXXBasePaths &Paths, SourceLocation AccessLoc,
Sebastian Redle644e192009-07-18 14:32:15 +0000114 DeclarationName Name) {
115
116 if (!getLangOptions().AccessControl)
117 return false;
118 const CXXBaseSpecifier *InaccessibleBase = FindInaccessibleBase(
119 Derived, Base, Paths);
120
121 if (InaccessibleBase) {
Mike Stump11289f42009-09-09 15:08:12 +0000122 Diag(AccessLoc, InaccessibleBaseID)
Anders Carlsson49d216d2009-05-13 21:11:42 +0000123 << Derived << Base << Name;
Anders Carlsson733d77f2009-03-27 06:03:27 +0000124
Sebastian Redle644e192009-07-18 14:32:15 +0000125 AccessSpecifier AS = InaccessibleBase->getAccessSpecifierAsWritten();
126
Anders Carlsson733d77f2009-03-27 06:03:27 +0000127 // If there's no written access specifier, then the inheritance specifier
128 // is implicitly private.
129 if (AS == AS_none)
Sebastian Redle644e192009-07-18 14:32:15 +0000130 Diag(InaccessibleBase->getSourceRange().getBegin(),
Anders Carlsson733d77f2009-03-27 06:03:27 +0000131 diag::note_inheritance_implicitly_private_here);
132 else
Sebastian Redle644e192009-07-18 14:32:15 +0000133 Diag(InaccessibleBase->getSourceRange().getBegin(),
Anders Carlsson733d77f2009-03-27 06:03:27 +0000134 diag::note_inheritance_specifier_here) << AS;
135
136 return true;
137 }
Sebastian Redle644e192009-07-18 14:32:15 +0000138
Anders Carlsson4742a9c2009-03-27 05:05:05 +0000139 return false;
140}
John McCall553c0792010-01-23 00:46:32 +0000141
142/// Diagnose the path which caused the given declaration to become
143/// inaccessible.
144static void DiagnoseAccessPath(Sema &S, const LookupResult &R, NamedDecl *D,
145 AccessSpecifier Access) {
146 // Easy case: the decl's natural access determined its path access.
147 if (Access == D->getAccess() || D->getAccess() == AS_private) {
148 S.Diag(D->getLocation(), diag::note_access_natural)
149 << (unsigned) (Access == AS_protected);
150 return;
151 }
152
153 // TODO: flesh this out
154 S.Diag(D->getLocation(), diag::note_access_constrained_by_path)
155 << (unsigned) (Access == AS_protected);
156}
157
158/// Checks access to the given declaration in the current context.
159///
160/// \param R the means via which the access was made; must have a naming
161/// class set
162/// \param D the declaration accessed
163/// \param Access the best access along any inheritance path from the
164/// naming class to the declaration. AS_none means the path is impossible
165bool Sema::CheckAccess(const LookupResult &R, NamedDecl *D,
166 AccessSpecifier Access) {
167 assert(R.getNamingClass() && "performing access check without naming class");
168
169 // If the access path is public, it's accessible everywhere.
170 if (Access == AS_public)
171 return false;
172
173 // Otherwise, derive the current class context.
174 DeclContext *DC = CurContext;
175 while (isa<CXXRecordDecl>(DC) &&
176 cast<CXXRecordDecl>(DC)->isAnonymousStructOrUnion())
177 DC = DC->getParent();
178
179 CXXRecordDecl *CurRecord;
180 if (isa<CXXRecordDecl>(DC))
181 CurRecord = cast<CXXRecordDecl>(DC);
182 else if (isa<CXXMethodDecl>(DC))
183 CurRecord = cast<CXXMethodDecl>(DC)->getParent();
184 else {
185 Diag(R.getNameLoc(), diag::err_access_outside_class)
186 << (Access == AS_protected);
187 DiagnoseAccessPath(*this, R, D, Access);
188 return true;
189 }
190
191 CXXRecordDecl *NamingClass = R.getNamingClass();
192 while (NamingClass->isAnonymousStructOrUnion())
193 // This should be guaranteed by the fact that the decl has
194 // non-public access. If not, we should make it guaranteed!
195 NamingClass = cast<CXXRecordDecl>(NamingClass);
196
197 // White-list accesses from within the declaring class.
198 if (Access != AS_none &&
199 CurRecord->getCanonicalDecl() == NamingClass->getCanonicalDecl())
200 return false;
201
202 // Protected access.
203 if (Access == AS_protected) {
204 // FIXME: implement [class.protected]p1
205 if (CurRecord->isDerivedFrom(NamingClass))
206 return false;
207
208 // FIXME: dependent classes
209 }
210
211 // FIXME: friends
212
213 // Okay, it's a bad access, reject it.
214
215
216 CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext());
217
218 if (Access == AS_protected) {
219 Diag(R.getNameLoc(), diag::err_access_protected)
220 << Context.getTypeDeclType(DeclaringClass)
221 << Context.getTypeDeclType(CurRecord);
222 DiagnoseAccessPath(*this, R, D, Access);
223 return true;
224 }
225
226 assert(Access == AS_private || Access == AS_none);
227 Diag(R.getNameLoc(), diag::err_access_private)
228 << Context.getTypeDeclType(DeclaringClass)
229 << Context.getTypeDeclType(CurRecord);
230 DiagnoseAccessPath(*this, R, D, Access);
231 return true;
232}
233
234/// Checks access to all the declarations in the given result set.
235void Sema::CheckAccess(const LookupResult &R) {
236 for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
237 CheckAccess(R, *I, I.getAccess());
238}