2009-03-27 13:05:05 +08:00
|
|
|
//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===//
|
2009-03-27 12:43:36 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file provides Sema routines for C++ access control semantics.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
2009-03-27 12:54:36 +08:00
|
|
|
|
|
|
|
#include "Sema.h"
|
2010-01-23 08:46:32 +08:00
|
|
|
#include "Lookup.h"
|
2009-03-27 14:03:27 +08:00
|
|
|
#include "clang/AST/ASTContext.h"
|
2009-10-07 01:59:45 +08:00
|
|
|
#include "clang/AST/CXXInheritance.h"
|
|
|
|
#include "clang/AST/DeclCXX.h"
|
2010-01-27 09:50:18 +08:00
|
|
|
#include "clang/AST/ExprCXX.h"
|
|
|
|
|
2009-03-27 12:54:36 +08:00
|
|
|
using namespace clang;
|
|
|
|
|
2009-03-27 13:05:05 +08:00
|
|
|
/// SetMemberAccessSpecifier - Set the access specifier of a member.
|
|
|
|
/// Returns true on error (when the previous member decl access specifier
|
|
|
|
/// is different from the new member decl access specifier).
|
2009-09-09 23:08:12 +08:00
|
|
|
bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
|
2009-03-27 12:54:36 +08:00
|
|
|
NamedDecl *PrevMemberDecl,
|
|
|
|
AccessSpecifier LexicalAS) {
|
|
|
|
if (!PrevMemberDecl) {
|
|
|
|
// Use the lexical access specifier.
|
|
|
|
MemberDecl->setAccess(LexicalAS);
|
|
|
|
return false;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-27 12:54:36 +08:00
|
|
|
// C++ [class.access.spec]p3: When a member is redeclared its access
|
|
|
|
// specifier must be same as its initial declaration.
|
|
|
|
if (LexicalAS != AS_none && LexicalAS != PrevMemberDecl->getAccess()) {
|
2009-09-09 23:08:12 +08:00
|
|
|
Diag(MemberDecl->getLocation(),
|
|
|
|
diag::err_class_redeclared_with_different_access)
|
2009-03-27 12:54:36 +08:00
|
|
|
<< MemberDecl << LexicalAS;
|
|
|
|
Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration)
|
|
|
|
<< PrevMemberDecl << PrevMemberDecl->getAccess();
|
2009-12-23 08:37:40 +08:00
|
|
|
|
|
|
|
MemberDecl->setAccess(LexicalAS);
|
2009-03-27 12:54:36 +08:00
|
|
|
return true;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-27 12:54:36 +08:00
|
|
|
MemberDecl->setAccess(PrevMemberDecl->getAccess());
|
|
|
|
return false;
|
|
|
|
}
|
2009-03-27 13:05:05 +08:00
|
|
|
|
2009-07-18 22:32:15 +08:00
|
|
|
/// Find a class on the derivation path between Derived and Base that is
|
|
|
|
/// inaccessible. If @p NoPrivileges is true, special access rights (members
|
|
|
|
/// and friends) are not considered.
|
|
|
|
const CXXBaseSpecifier *Sema::FindInaccessibleBase(
|
2009-10-07 01:59:45 +08:00
|
|
|
QualType Derived, QualType Base, CXXBasePaths &Paths, bool NoPrivileges) {
|
2009-03-27 14:03:27 +08:00
|
|
|
Base = Context.getCanonicalType(Base).getUnqualifiedType();
|
2009-09-09 23:08:12 +08:00
|
|
|
assert(!Paths.isAmbiguous(Base) &&
|
2009-03-27 14:03:27 +08:00
|
|
|
"Can't check base class access if set of paths is ambiguous");
|
|
|
|
assert(Paths.isRecordingPaths() &&
|
|
|
|
"Can't check base class access without recorded paths");
|
|
|
|
|
2009-07-18 22:32:15 +08:00
|
|
|
|
|
|
|
const CXXBaseSpecifier *InaccessibleBase = 0;
|
|
|
|
|
|
|
|
const CXXRecordDecl *CurrentClassDecl = 0;
|
2009-03-28 03:01:12 +08:00
|
|
|
if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(getCurFunctionDecl()))
|
|
|
|
CurrentClassDecl = MD->getParent();
|
|
|
|
|
2009-10-07 01:59:45 +08:00
|
|
|
for (CXXBasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end();
|
2009-03-27 14:03:27 +08:00
|
|
|
Path != PathsEnd; ++Path) {
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2009-03-27 14:03:27 +08:00
|
|
|
bool FoundInaccessibleBase = false;
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2009-10-07 01:59:45 +08:00
|
|
|
for (CXXBasePath::const_iterator Element = Path->begin(),
|
2009-03-27 14:03:27 +08:00
|
|
|
ElementEnd = Path->end(); Element != ElementEnd; ++Element) {
|
|
|
|
const CXXBaseSpecifier *Base = Element->Base;
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2009-03-27 14:03:27 +08:00
|
|
|
switch (Base->getAccessSpecifier()) {
|
|
|
|
default:
|
|
|
|
assert(0 && "invalid access specifier");
|
|
|
|
case AS_public:
|
|
|
|
// Nothing to do.
|
|
|
|
break;
|
|
|
|
case AS_private:
|
2009-03-28 03:01:12 +08:00
|
|
|
// FIXME: Check if the current function/class is a friend.
|
2009-07-18 22:32:15 +08:00
|
|
|
if (NoPrivileges || CurrentClassDecl != Element->Class)
|
2009-03-28 03:01:12 +08:00
|
|
|
FoundInaccessibleBase = true;
|
2009-03-27 14:03:27 +08:00
|
|
|
break;
|
2009-07-18 22:32:15 +08:00
|
|
|
case AS_protected:
|
2009-03-28 12:17:27 +08:00
|
|
|
// FIXME: Implement
|
|
|
|
break;
|
2009-03-27 14:03:27 +08:00
|
|
|
}
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2009-03-27 14:03:27 +08:00
|
|
|
if (FoundInaccessibleBase) {
|
2009-07-18 22:32:15 +08:00
|
|
|
InaccessibleBase = Base;
|
2009-03-27 14:03:27 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2009-03-27 14:03:27 +08:00
|
|
|
if (!FoundInaccessibleBase) {
|
|
|
|
// We found a path to the base, our work here is done.
|
2009-07-18 22:32:15 +08:00
|
|
|
return 0;
|
2009-03-27 14:03:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-18 22:32:15 +08:00
|
|
|
assert(InaccessibleBase && "no path found, but no inaccessible base");
|
|
|
|
return InaccessibleBase;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CheckBaseClassAccess - Check that a derived class can access its base class
|
|
|
|
/// and report an error if it can't. [class.access.base]
|
2009-09-09 23:08:12 +08:00
|
|
|
bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base,
|
2009-07-18 22:32:15 +08:00
|
|
|
unsigned InaccessibleBaseID,
|
2009-10-07 01:59:45 +08:00
|
|
|
CXXBasePaths &Paths, SourceLocation AccessLoc,
|
2009-07-18 22:32:15 +08:00
|
|
|
DeclarationName Name) {
|
|
|
|
|
|
|
|
if (!getLangOptions().AccessControl)
|
|
|
|
return false;
|
|
|
|
const CXXBaseSpecifier *InaccessibleBase = FindInaccessibleBase(
|
|
|
|
Derived, Base, Paths);
|
|
|
|
|
|
|
|
if (InaccessibleBase) {
|
2009-09-09 23:08:12 +08:00
|
|
|
Diag(AccessLoc, InaccessibleBaseID)
|
2009-05-14 05:11:42 +08:00
|
|
|
<< Derived << Base << Name;
|
2009-03-27 14:03:27 +08:00
|
|
|
|
2009-07-18 22:32:15 +08:00
|
|
|
AccessSpecifier AS = InaccessibleBase->getAccessSpecifierAsWritten();
|
|
|
|
|
2009-03-27 14:03:27 +08:00
|
|
|
// If there's no written access specifier, then the inheritance specifier
|
|
|
|
// is implicitly private.
|
|
|
|
if (AS == AS_none)
|
2009-07-18 22:32:15 +08:00
|
|
|
Diag(InaccessibleBase->getSourceRange().getBegin(),
|
2009-03-27 14:03:27 +08:00
|
|
|
diag::note_inheritance_implicitly_private_here);
|
|
|
|
else
|
2009-07-18 22:32:15 +08:00
|
|
|
Diag(InaccessibleBase->getSourceRange().getBegin(),
|
2009-03-27 14:03:27 +08:00
|
|
|
diag::note_inheritance_specifier_here) << AS;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2009-03-27 13:05:05 +08:00
|
|
|
return false;
|
|
|
|
}
|
2010-01-23 08:46:32 +08:00
|
|
|
|
|
|
|
/// Diagnose the path which caused the given declaration to become
|
|
|
|
/// inaccessible.
|
|
|
|
static void DiagnoseAccessPath(Sema &S, const LookupResult &R, NamedDecl *D,
|
|
|
|
AccessSpecifier Access) {
|
|
|
|
// Easy case: the decl's natural access determined its path access.
|
|
|
|
if (Access == D->getAccess() || D->getAccess() == AS_private) {
|
|
|
|
S.Diag(D->getLocation(), diag::note_access_natural)
|
|
|
|
<< (unsigned) (Access == AS_protected);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: flesh this out
|
|
|
|
S.Diag(D->getLocation(), diag::note_access_constrained_by_path)
|
|
|
|
<< (unsigned) (Access == AS_protected);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Checks access to the given declaration in the current context.
|
|
|
|
///
|
|
|
|
/// \param R the means via which the access was made; must have a naming
|
|
|
|
/// class set
|
|
|
|
/// \param D the declaration accessed
|
|
|
|
/// \param Access the best access along any inheritance path from the
|
|
|
|
/// naming class to the declaration. AS_none means the path is impossible
|
|
|
|
bool Sema::CheckAccess(const LookupResult &R, NamedDecl *D,
|
|
|
|
AccessSpecifier Access) {
|
|
|
|
assert(R.getNamingClass() && "performing access check without naming class");
|
|
|
|
|
|
|
|
// If the access path is public, it's accessible everywhere.
|
|
|
|
if (Access == AS_public)
|
|
|
|
return false;
|
|
|
|
|
2010-01-27 11:50:35 +08:00
|
|
|
// If we're currently parsing a top-level declaration, delay
|
|
|
|
// diagnostics. This is the only case where parsing a declaration
|
|
|
|
// can actually change our effective context for the purposes of
|
|
|
|
// access control.
|
|
|
|
if (CurContext->isFileContext() && ParsingDeclDepth) {
|
|
|
|
DelayedDiagnostics.push_back(
|
|
|
|
DelayedDiagnostic::makeAccess(R.getNameLoc(), D, Access,
|
|
|
|
R.getNamingClass()));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CheckEffectiveAccess(CurContext, R, D, Access);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Checks access from the given effective context.
|
|
|
|
bool Sema::CheckEffectiveAccess(DeclContext *EffectiveContext,
|
|
|
|
const LookupResult &R,
|
|
|
|
NamedDecl *D, AccessSpecifier Access) {
|
|
|
|
DeclContext *DC = EffectiveContext;
|
2010-01-23 08:46:32 +08:00
|
|
|
while (isa<CXXRecordDecl>(DC) &&
|
|
|
|
cast<CXXRecordDecl>(DC)->isAnonymousStructOrUnion())
|
|
|
|
DC = DC->getParent();
|
|
|
|
|
|
|
|
CXXRecordDecl *CurRecord;
|
|
|
|
if (isa<CXXRecordDecl>(DC))
|
|
|
|
CurRecord = cast<CXXRecordDecl>(DC);
|
|
|
|
else if (isa<CXXMethodDecl>(DC))
|
|
|
|
CurRecord = cast<CXXMethodDecl>(DC)->getParent();
|
|
|
|
else {
|
|
|
|
Diag(R.getNameLoc(), diag::err_access_outside_class)
|
|
|
|
<< (Access == AS_protected);
|
|
|
|
DiagnoseAccessPath(*this, R, D, Access);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CXXRecordDecl *NamingClass = R.getNamingClass();
|
|
|
|
while (NamingClass->isAnonymousStructOrUnion())
|
|
|
|
// This should be guaranteed by the fact that the decl has
|
|
|
|
// non-public access. If not, we should make it guaranteed!
|
|
|
|
NamingClass = cast<CXXRecordDecl>(NamingClass);
|
|
|
|
|
|
|
|
// White-list accesses from within the declaring class.
|
|
|
|
if (Access != AS_none &&
|
|
|
|
CurRecord->getCanonicalDecl() == NamingClass->getCanonicalDecl())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Protected access.
|
|
|
|
if (Access == AS_protected) {
|
|
|
|
// FIXME: implement [class.protected]p1
|
|
|
|
if (CurRecord->isDerivedFrom(NamingClass))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// FIXME: dependent classes
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: friends
|
|
|
|
|
|
|
|
// Okay, it's a bad access, reject it.
|
|
|
|
|
|
|
|
|
|
|
|
CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext());
|
|
|
|
|
|
|
|
if (Access == AS_protected) {
|
|
|
|
Diag(R.getNameLoc(), diag::err_access_protected)
|
|
|
|
<< Context.getTypeDeclType(DeclaringClass)
|
|
|
|
<< Context.getTypeDeclType(CurRecord);
|
|
|
|
DiagnoseAccessPath(*this, R, D, Access);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(Access == AS_private || Access == AS_none);
|
|
|
|
Diag(R.getNameLoc(), diag::err_access_private)
|
|
|
|
<< Context.getTypeDeclType(DeclaringClass)
|
|
|
|
<< Context.getTypeDeclType(CurRecord);
|
|
|
|
DiagnoseAccessPath(*this, R, D, Access);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-01-27 11:50:35 +08:00
|
|
|
void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {
|
|
|
|
NamedDecl *D = DD.AccessData.Decl;
|
|
|
|
|
|
|
|
// Fake up a lookup result.
|
|
|
|
LookupResult R(*this, D->getDeclName(), DD.Loc, LookupOrdinaryName);
|
|
|
|
R.suppressDiagnostics();
|
|
|
|
R.setNamingClass(DD.AccessData.NamingClass);
|
|
|
|
|
|
|
|
// Pretend we did this from the context of the newly-parsed
|
|
|
|
// declaration.
|
|
|
|
DeclContext *EffectiveContext = Ctx->getDeclContext();
|
|
|
|
|
|
|
|
if (CheckEffectiveAccess(EffectiveContext, R, D, DD.AccessData.Access))
|
|
|
|
DD.Triggered = true;
|
|
|
|
}
|
|
|
|
|
2010-01-27 09:50:18 +08:00
|
|
|
bool Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
|
|
|
|
NamedDecl *D, AccessSpecifier Access) {
|
|
|
|
if (!getLangOptions().AccessControl || !E->getNamingClass())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Fake up a lookup result.
|
|
|
|
LookupResult R(*this, E->getName(), E->getNameLoc(), LookupOrdinaryName);
|
|
|
|
R.suppressDiagnostics();
|
|
|
|
|
|
|
|
R.setNamingClass(E->getNamingClass());
|
|
|
|
R.addDecl(D, Access);
|
|
|
|
|
|
|
|
// FIXME: protected check (triggers for member-address expressions)
|
|
|
|
|
|
|
|
return CheckAccess(R, D, Access);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform access-control checking on a previously-unresolved member
|
|
|
|
/// access which has now been resolved to a member.
|
|
|
|
bool Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
|
|
|
|
NamedDecl *D, AccessSpecifier Access) {
|
|
|
|
if (!getLangOptions().AccessControl)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Fake up a lookup result.
|
|
|
|
LookupResult R(*this, E->getMemberName(), E->getMemberLoc(),
|
|
|
|
LookupOrdinaryName);
|
|
|
|
R.suppressDiagnostics();
|
|
|
|
|
|
|
|
R.setNamingClass(E->getNamingClass());
|
|
|
|
R.addDecl(D, Access);
|
|
|
|
|
|
|
|
if (CheckAccess(R, D, Access))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// FIXME: protected check
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-01-28 09:42:12 +08:00
|
|
|
/// Checks access to an overloaded member operator.
|
|
|
|
bool Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
|
|
|
|
Expr *ObjectExpr,
|
|
|
|
NamedDecl *MemberOperator,
|
|
|
|
AccessSpecifier Access) {
|
|
|
|
if (!getLangOptions().AccessControl)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const RecordType *RT = ObjectExpr->getType()->getAs<RecordType>();
|
|
|
|
assert(RT && "found member operator but object expr not of record type");
|
|
|
|
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
|
|
|
|
|
|
|
|
LookupResult R(*this, DeclarationName(), OpLoc, LookupOrdinaryName);
|
|
|
|
R.suppressDiagnostics();
|
|
|
|
|
|
|
|
R.setNamingClass(NamingClass);
|
|
|
|
if (CheckAccess(R, MemberOperator, Access))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// FIXME: protected check
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-01-23 08:46:32 +08:00
|
|
|
/// Checks access to all the declarations in the given result set.
|
|
|
|
void Sema::CheckAccess(const LookupResult &R) {
|
|
|
|
for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
|
|
|
|
CheckAccess(R, *I, I.getAccess());
|
|
|
|
}
|