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