 |
|
 |
|
| Files: |
1 |
|
Branches Taken: |
88.0% |
95 / 108 |
| Generated: |
2010-02-10 01:31 |
|
Branches Executed: |
100.0% |
108 / 108 |
| |
|
Line Coverage: |
96.1% |
146 / 152 |
| |
 |
|
 |
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 :
21 : using 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).
26 : bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
27 : NamedDecl *PrevMemberDecl,
28 5737: AccessSpecifier LexicalAS) {
5574: branch 0 taken
163: branch 1 taken
29 5737: if (!PrevMemberDecl) {
30 : // Use the lexical access specifier.
31 5574: MemberDecl->setAccess(LexicalAS);
32 5574: 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.
8: branch 0 taken
155: branch 1 taken
4: branch 3 taken
4: branch 4 taken
4: branch 5 taken
159: branch 6 taken
37 163: if (LexicalAS != AS_none && LexicalAS != PrevMemberDecl->getAccess()) {
38 : Diag(MemberDecl->getLocation(),
39 : diag::err_class_redeclared_with_different_access)
40 4: << MemberDecl << LexicalAS;
41 : Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration)
42 4: << PrevMemberDecl << PrevMemberDecl->getAccess();
43 :
44 4: MemberDecl->setAccess(LexicalAS);
45 4: return true;
46 : }
47 :
48 159: MemberDecl->setAccess(PrevMemberDecl->getAccess());
49 159: 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.
55 : const CXXBaseSpecifier *Sema::FindInaccessibleBase(
56 87: QualType Derived, QualType Base, CXXBasePaths &Paths, bool NoPrivileges) {
57 87: Base = Context.getCanonicalType(Base).getUnqualifiedType();
58 : assert(!Paths.isAmbiguous(Base) &&
87: branch 1 taken
0: branch 2 not taken
59 87: "Can't check base class access if set of paths is ambiguous");
60 : assert(Paths.isRecordingPaths() &&
87: branch 1 taken
0: branch 2 not taken
61 87: "Can't check base class access without recorded paths");
62 :
63 :
64 87: const CXXBaseSpecifier *InaccessibleBase = 0;
65 :
66 87: const CXXRecordDecl *CurrentClassDecl = 0;
25: branch 2 taken
62: branch 3 taken
67 87: if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(getCurFunctionDecl()))
68 25: CurrentClassDecl = MD->getParent();
69 :
88: branch 4 taken
19: branch 5 taken
70 107: for (CXXBasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end();
71 : Path != PathsEnd; ++Path) {
72 :
73 88: bool FoundInaccessibleBase = false;
74 :
98: branch 2 taken
68: branch 3 taken
75 254: for (CXXBasePath::const_iterator Element = Path->begin(),
76 88: ElementEnd = Path->end(); Element != ElementEnd; ++Element) {
77 98: const CXXBaseSpecifier *Base = Element->Base;
78 :
0: branch 1 not taken
56: branch 2 taken
30: branch 3 taken
12: branch 4 taken
79 98: switch (Base->getAccessSpecifier()) {
80 : default:
81 0: assert(0 && "invalid access specifier");
82 : case AS_public:
83 : // Nothing to do.
84 56: break;
85 : case AS_private:
86 : // FIXME: Check if the current function/class is a friend.
29: branch 0 taken
1: branch 1 taken
19: branch 2 taken
10: branch 3 taken
87 30: if (NoPrivileges || CurrentClassDecl != Element->Class)
88 20: FoundInaccessibleBase = true;
89 : break;
90 : case AS_protected:
91 : // FIXME: Implement
92 : break;
93 : }
94 :
20: branch 0 taken
78: branch 1 taken
95 98: if (FoundInaccessibleBase) {
96 20: InaccessibleBase = Base;
97 20: break;
98 : }
99 : }
100 :
68: branch 0 taken
20: branch 1 taken
101 88: if (!FoundInaccessibleBase) {
102 : // We found a path to the base, our work here is done.
103 68: return 0;
104 : }
105 : }
106 :
0: branch 0 not taken
19: branch 1 taken
107 19: assert(InaccessibleBase && "no path found, but no inaccessible base");
108 19: 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]
113 : bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base,
114 : unsigned InaccessibleBaseID,
115 : CXXBasePaths &Paths, SourceLocation AccessLoc,
116 318: DeclarationName Name) {
117 :
237: branch 1 taken
81: branch 2 taken
118 318: if (!getLangOptions().AccessControl)
119 237: return false;
120 : const CXXBaseSpecifier *InaccessibleBase = FindInaccessibleBase(
121 81: Derived, Base, Paths);
122 :
18: branch 0 taken
63: branch 1 taken
123 81: if (InaccessibleBase) {
124 : Diag(AccessLoc, InaccessibleBaseID)
125 18: << Derived << Base << Name;
126 :
127 18: AccessSpecifier AS = InaccessibleBase->getAccessSpecifierAsWritten();
128 :
129 : // If there's no written access specifier, then the inheritance specifier
130 : // is implicitly private.
1: branch 0 taken
17: branch 1 taken
131 18: if (AS == AS_none)
132 : Diag(InaccessibleBase->getSourceRange().getBegin(),
133 1: diag::note_inheritance_implicitly_private_here);
134 : else
135 : Diag(InaccessibleBase->getSourceRange().getBegin(),
136 17: diag::note_inheritance_specifier_here) << AS;
137 :
138 18: return true;
139 : }
140 :
141 63: return false;
142 : }
143 :
144 : /// Diagnose the path which caused the given declaration to become
145 : /// inaccessible.
146 : static void DiagnoseAccessPath(Sema &S, const LookupResult &R, NamedDecl *D,
147 76: AccessSpecifier Access) {
148 : // Easy case: the decl's natural access determined its path access.
32: branch 1 taken
44: branch 2 taken
14: branch 4 taken
18: branch 5 taken
58: branch 6 taken
18: branch 7 taken
149 76: if (Access == D->getAccess() || D->getAccess() == AS_private) {
150 : S.Diag(D->getLocation(), diag::note_access_natural)
151 58: << (unsigned) (Access == AS_protected);
152 58: return;
153 : }
154 :
155 : // TODO: flesh this out
156 : S.Diag(D->getLocation(), diag::note_access_constrained_by_path)
157 18: << (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
167 : bool Sema::CheckAccess(const LookupResult &R, NamedDecl *D,
168 239: AccessSpecifier Access) {
239: branch 1 taken
0: branch 2 not taken
169 239: assert(R.getNamingClass() && "performing access check without naming class");
170 :
171 : // If the access path is public, it's accessible everywhere.
129: branch 0 taken
110: branch 1 taken
172 239: if (Access == AS_public)
173 129: 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.
6: branch 1 taken
104: branch 2 taken
6: branch 3 taken
0: branch 4 not taken
6: branch 5 taken
104: branch 6 taken
179 110: if (CurContext->isFileContext() && ParsingDeclDepth) {
180 : DelayedDiagnostics.push_back(
181 : DelayedDiagnostic::makeAccess(R.getNameLoc(), D, Access,
182 6: R.getNamingClass()));
183 6: return false;
184 : }
185 :
186 104: return CheckEffectiveAccess(CurContext, R, D, Access);
187 : }
188 :
189 : /// Checks access from the given effective context.
190 : bool Sema::CheckEffectiveAccess(DeclContext *EffectiveContext,
191 : const LookupResult &R,
192 110: NamedDecl *D, AccessSpecifier Access) {
193 110: DeclContext *DC = EffectiveContext;
3: branch 1 taken
107: branch 2 taken
0: branch 5 not taken
3: branch 6 taken
0: branch 7 not taken
110: branch 8 taken
194 220: while (isa<CXXRecordDecl>(DC) &&
195 : cast<CXXRecordDecl>(DC)->isAnonymousStructOrUnion())
196 0: DC = DC->getParent();
197 :
198 : CXXRecordDecl *CurRecord;
3: branch 1 taken
107: branch 2 taken
199 110: if (isa<CXXRecordDecl>(DC))
200 3: CurRecord = cast<CXXRecordDecl>(DC);
45: branch 1 taken
62: branch 2 taken
201 107: else if (isa<CXXMethodDecl>(DC))
202 45: CurRecord = cast<CXXMethodDecl>(DC)->getParent();
203 : else {
204 : Diag(R.getNameLoc(), diag::err_access_outside_class)
205 62: << (Access == AS_protected);
206 62: DiagnoseAccessPath(*this, R, D, Access);
207 62: return true;
208 : }
209 :
210 48: CXXRecordDecl *NamingClass = R.getNamingClass();
0: branch 1 not taken
48: branch 2 taken
211 96: 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 0: NamingClass = cast<CXXRecordDecl>(NamingClass);
215 :
216 : // White-list accesses from within the declaring class.
40: branch 0 taken
8: branch 1 taken
28: branch 4 taken
12: branch 5 taken
28: branch 6 taken
20: branch 7 taken
217 48: if (Access != AS_none &&
218 : CurRecord->getCanonicalDecl() == NamingClass->getCanonicalDecl())
219 28: return false;
220 :
221 : // Protected access.
6: branch 0 taken
14: branch 1 taken
222 20: if (Access == AS_protected) {
223 : // FIXME: implement [class.protected]p1
6: branch 1 taken
0: branch 2 not taken
224 6: if (CurRecord->isDerivedFrom(NamingClass))
225 6: 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 14: CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext());
236 :
0: branch 0 not taken
14: branch 1 taken
237 14: if (Access == AS_protected) {
238 : Diag(R.getNameLoc(), diag::err_access_protected)
239 : << Context.getTypeDeclType(DeclaringClass)
240 0: << Context.getTypeDeclType(CurRecord);
241 0: DiagnoseAccessPath(*this, R, D, Access);
242 0: return true;
243 : }
244 :
8: branch 0 taken
6: branch 1 taken
0: branch 2 not taken
8: branch 3 taken
245 14: assert(Access == AS_private || Access == AS_none);
246 : Diag(R.getNameLoc(), diag::err_access_private)
247 : << Context.getTypeDeclType(DeclaringClass)
248 14: << Context.getTypeDeclType(CurRecord);
249 14: DiagnoseAccessPath(*this, R, D, Access);
250 14: return true;
251 : }
252 :
253 6: void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {
254 6: NamedDecl *D = DD.AccessData.Decl;
255 :
256 : // Fake up a lookup result.
257 6: LookupResult R(*this, D->getDeclName(), DD.Loc, LookupOrdinaryName);
258 6: R.suppressDiagnostics();
259 6: R.setNamingClass(DD.AccessData.NamingClass);
260 :
261 : // Pretend we did this from the context of the newly-parsed
262 : // declaration.
263 6: DeclContext *EffectiveContext = Ctx->getDeclContext();
264 :
3: branch 1 taken
3: branch 2 taken
265 6: if (CheckEffectiveAccess(EffectiveContext, R, D, DD.AccessData.Access))
266 3: DD.Triggered = true;
267 6: }
268 :
269 : bool Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
270 1315: NamedDecl *D, AccessSpecifier Access) {
9: branch 1 taken
1306: branch 2 taken
6: branch 4 taken
3: branch 5 taken
1312: branch 6 taken
3: branch 7 taken
271 1315: if (!getLangOptions().AccessControl || !E->getNamingClass())
272 1312: return false;
273 :
274 : // Fake up a lookup result.
275 3: LookupResult R(*this, E->getName(), E->getNameLoc(), LookupOrdinaryName);
276 3: R.suppressDiagnostics();
277 :
278 3: R.setNamingClass(E->getNamingClass());
279 3: R.addDecl(D, Access);
280 :
281 : // FIXME: protected check (triggers for member-address expressions)
282 :
283 3: 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.
288 : bool Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
289 160: NamedDecl *D, AccessSpecifier Access) {
147: branch 1 taken
13: branch 2 taken
290 160: if (!getLangOptions().AccessControl)
291 147: return false;
292 :
293 : // Fake up a lookup result.
294 : LookupResult R(*this, E->getMemberName(), E->getMemberLoc(),
295 13: LookupOrdinaryName);
296 13: R.suppressDiagnostics();
297 :
298 13: R.setNamingClass(E->getNamingClass());
299 13: R.addDecl(D, Access);
300 :
2: branch 1 taken
11: branch 2 taken
301 13: if (CheckAccess(R, D, Access))
302 2: return true;
303 :
304 : // FIXME: protected check
305 :
306 11: return false;
307 : }
308 :
309 106: bool Sema::CheckDestructorAccess(SourceLocation Loc, const RecordType *RT) {
101: branch 1 taken
5: branch 2 taken
310 106: if (!getLangOptions().AccessControl)
311 101: return false;
312 :
313 5: CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
314 5: CXXDestructorDecl *Dtor = NamingClass->getDestructor(Context);
315 :
316 5: AccessSpecifier Access = Dtor->getAccess();
1: branch 0 taken
4: branch 1 taken
317 5: if (Access == AS_public)
318 1: return false;
319 :
320 4: LookupResult R(*this, Dtor->getDeclName(), Loc, LookupOrdinaryName);
321 4: R.suppressDiagnostics();
322 :
323 4: R.setNamingClass(NamingClass);
324 4: return CheckAccess(R, Dtor, Access);
325 :
326 : // FIXME: protected check
327 : }
328 :
329 : /// Checks access to a constructor.
330 : bool Sema::CheckConstructorAccess(SourceLocation UseLoc,
331 : CXXConstructorDecl *Constructor,
332 1820: AccessSpecifier Access) {
1764: branch 1 taken
56: branch 2 taken
333 1820: if (!getLangOptions().AccessControl)
334 1764: return false;
335 :
336 56: CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Constructor->getParent());
337 :
338 56: LookupResult R(*this, Constructor->getDeclName(), UseLoc, LookupOrdinaryName);
339 56: R.suppressDiagnostics();
340 :
341 56: R.setNamingClass(NamingClass);
342 56: return CheckAccess(R, Constructor, Access);
343 : }
344 :
345 : /// Checks access to an overloaded member operator, including
346 : /// conversion operators.
347 : bool Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
348 : Expr *ObjectExpr,
349 : NamedDecl *MemberOperator,
350 242: AccessSpecifier Access) {
222: branch 1 taken
20: branch 2 taken
351 242: if (!getLangOptions().AccessControl)
352 222: return false;
353 :
354 20: const RecordType *RT = ObjectExpr->getType()->getAs<RecordType>();
0: branch 0 not taken
20: branch 1 taken
355 20: assert(RT && "found member operator but object expr not of record type");
356 20: CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
357 :
358 20: LookupResult R(*this, DeclarationName(), OpLoc, LookupOrdinaryName);
359 20: R.suppressDiagnostics();
360 :
361 20: R.setNamingClass(NamingClass);
11: branch 1 taken
9: branch 2 taken
362 20: if (CheckAccess(R, MemberOperator, Access))
363 11: return true;
364 :
365 : // FIXME: protected check
366 :
367 9: return false;
368 : }
369 :
370 : /// Checks access to all the declarations in the given result set.
371 143: void Sema::CheckAccess(const LookupResult &R) {
143: branch 4 taken
143: branch 5 taken
372 286: for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
373 143: CheckAccess(R, *I, I.getAccess());
374 143: }
Generated: 2010-02-10 01:31 by zcov