aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaAccess.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Sema/SemaAccess.cpp')
-rw-r--r--lib/Sema/SemaAccess.cpp207
1 files changed, 124 insertions, 83 deletions
diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp
index eca8bb4c2a9b..f0a38d5970cc 100644
--- a/lib/Sema/SemaAccess.cpp
+++ b/lib/Sema/SemaAccess.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
#include "clang/AST/ExprCXX.h"
using namespace clang;
@@ -55,7 +56,7 @@ struct EffectiveContext {
explicit EffectiveContext(DeclContext *DC) {
if (isa<FunctionDecl>(DC)) {
- Function = cast<FunctionDecl>(DC);
+ Function = cast<FunctionDecl>(DC)->getCanonicalDecl();
DC = Function->getDeclContext();
} else
Function = 0;
@@ -85,10 +86,34 @@ static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
static Sema::AccessResult GetFriendKind(Sema &S,
const EffectiveContext &EC,
const CXXRecordDecl *Class) {
+ // A class always has access to its own members.
if (EC.isClass(Class))
return Sema::AR_accessible;
- // FIXME: implement
+ // Okay, check friends.
+ for (CXXRecordDecl::friend_iterator I = Class->friend_begin(),
+ E = Class->friend_end(); I != E; ++I) {
+ FriendDecl *Friend = *I;
+
+ if (Type *T = Friend->getFriendType()) {
+ if (EC.Record &&
+ S.Context.hasSameType(QualType(T, 0),
+ S.Context.getTypeDeclType(EC.Record)))
+ return Sema::AR_accessible;
+ } else {
+ NamedDecl *D
+ = cast<NamedDecl>(Friend->getFriendDecl()->getCanonicalDecl());
+
+ // The decl pointers in EC have been canonicalized, so pointer
+ // equality is sufficient.
+ if (D == EC.Function || D == EC.Record)
+ return Sema::AR_accessible;
+ }
+
+ // FIXME: templates! templated contexts! dependent delay!
+ }
+
+ // That's it, give up.
return Sema::AR_inaccessible;
}
@@ -230,18 +255,11 @@ static void DiagnoseInaccessibleMember(Sema &S, SourceLocation Loc,
NamedDecl *D = Entity.getTargetDecl();
CXXRecordDecl *DeclaringClass = FindDeclaringClass(D);
- if (isa<CXXConstructorDecl>(D)) {
- unsigned DiagID = (Access == AS_protected ? diag::err_access_ctor_protected
- : diag::err_access_ctor_private);
- S.Diag(Loc, DiagID)
- << S.Context.getTypeDeclType(DeclaringClass);
- } else {
- unsigned DiagID = (Access == AS_protected ? diag::err_access_protected
- : diag::err_access_private);
- S.Diag(Loc, DiagID)
- << D->getDeclName()
- << S.Context.getTypeDeclType(DeclaringClass);
- }
+ S.Diag(Loc, Entity.getDiag())
+ << (Access == AS_protected)
+ << D->getDeclName()
+ << S.Context.getTypeDeclType(NamingClass)
+ << S.Context.getTypeDeclType(DeclaringClass);
DiagnoseAccessPath(S, EC, NamingClass, DeclaringClass, D, Access);
}
@@ -249,39 +267,25 @@ static void DiagnoseInaccessibleMember(Sema &S, SourceLocation Loc,
static void DiagnoseInaccessibleBase(Sema &S, SourceLocation Loc,
const EffectiveContext &EC,
AccessSpecifier Access,
- const Sema::AccessedEntity &Entity,
- Sema::AccessDiagnosticsKind ADK) {
- if (ADK == Sema::ADK_covariance) {
- S.Diag(Loc, diag::err_covariant_return_inaccessible_base)
- << S.Context.getTypeDeclType(Entity.getDerivedClass())
- << S.Context.getTypeDeclType(Entity.getBaseClass())
- << (Access == AS_protected);
- } else if (Entity.getKind() == Sema::AccessedEntity::BaseToDerivedConversion) {
- S.Diag(Loc, diag::err_downcast_from_inaccessible_base)
- << S.Context.getTypeDeclType(Entity.getDerivedClass())
- << S.Context.getTypeDeclType(Entity.getBaseClass())
- << (Access == AS_protected);
- } else {
- S.Diag(Loc, diag::err_upcast_to_inaccessible_base)
- << S.Context.getTypeDeclType(Entity.getDerivedClass())
- << S.Context.getTypeDeclType(Entity.getBaseClass())
- << (Access == AS_protected);
- }
+ const Sema::AccessedEntity &Entity) {
+ S.Diag(Loc, Entity.getDiag())
+ << (Access == AS_protected)
+ << DeclarationName()
+ << S.Context.getTypeDeclType(Entity.getDerivedClass())
+ << S.Context.getTypeDeclType(Entity.getBaseClass());
DiagnoseAccessPath(S, EC, Entity.getDerivedClass(),
Entity.getBaseClass(), 0, Access);
}
-static void DiagnoseBadAccess(Sema &S,
- SourceLocation Loc,
+static void DiagnoseBadAccess(Sema &S, SourceLocation Loc,
const EffectiveContext &EC,
CXXRecordDecl *NamingClass,
AccessSpecifier Access,
- const Sema::AccessedEntity &Entity,
- Sema::AccessDiagnosticsKind ADK) {
+ const Sema::AccessedEntity &Entity) {
if (Entity.isMemberAccess())
DiagnoseInaccessibleMember(S, Loc, EC, NamingClass, Access, Entity);
else
- DiagnoseInaccessibleBase(S, Loc, EC, Access, Entity, ADK);
+ DiagnoseInaccessibleBase(S, Loc, EC, Access, Entity);
}
@@ -344,8 +348,7 @@ static void TryElevateAccess(Sema &S,
static Sema::AccessResult CheckEffectiveAccess(Sema &S,
const EffectiveContext &EC,
SourceLocation Loc,
- Sema::AccessedEntity const &Entity,
- Sema::AccessDiagnosticsKind ADK) {
+ Sema::AccessedEntity const &Entity) {
AccessSpecifier Access = Entity.getAccess();
assert(Access != AS_public);
@@ -353,14 +356,14 @@ static Sema::AccessResult CheckEffectiveAccess(Sema &S,
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);
+ NamingClass = cast<CXXRecordDecl>(NamingClass->getParent());
if (!EC.Record) {
TryElevateAccess(S, EC, Entity, Access);
if (Access == AS_public) return Sema::AR_accessible;
- if (ADK != Sema::ADK_quiet)
- DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity, ADK);
+ if (!Entity.isQuiet())
+ DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity);
return Sema::AR_inaccessible;
}
@@ -393,15 +396,13 @@ static Sema::AccessResult CheckEffectiveAccess(Sema &S,
}
// Okay, that's it, reject it.
- if (ADK != Sema::ADK_quiet)
- DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity, ADK);
+ if (!Entity.isQuiet())
+ DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity);
return Sema::AR_inaccessible;
}
static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
- const Sema::AccessedEntity &Entity,
- Sema::AccessDiagnosticsKind ADK
- = Sema::ADK_normal) {
+ const Sema::AccessedEntity &Entity) {
// If the access path is public, it's accessible everywhere.
if (Entity.getAccess() == AS_public)
return Sema::AR_accessible;
@@ -411,14 +412,13 @@ static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
// can actually change our effective context for the purposes of
// access control.
if (S.CurContext->isFileContext() && S.ParsingDeclDepth) {
- assert(ADK == Sema::ADK_normal && "delaying abnormal access check");
S.DelayedDiagnostics.push_back(
Sema::DelayedDiagnostic::makeAccess(Loc, Entity));
return Sema::AR_delayed;
}
return CheckEffectiveAccess(S, EffectiveContext(S.CurContext),
- Loc, Entity, ADK);
+ Loc, Entity);
}
void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {
@@ -426,18 +426,23 @@ void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {
// declaration.
EffectiveContext EC(Ctx->getDeclContext());
- if (CheckEffectiveAccess(*this, EC, DD.Loc, DD.AccessData, ADK_normal))
+ if (CheckEffectiveAccess(*this, EC, DD.Loc, DD.getAccessData()))
DD.Triggered = true;
}
Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
NamedDecl *D,
AccessSpecifier Access) {
- if (!getLangOptions().AccessControl || !E->getNamingClass())
+ if (!getLangOptions().AccessControl ||
+ !E->getNamingClass() ||
+ Access == AS_public)
return AR_accessible;
- return CheckAccess(*this, E->getNameLoc(),
- AccessedEntity::makeMember(E->getNamingClass(), Access, D));
+ AccessedEntity Entity(AccessedEntity::Member,
+ E->getNamingClass(), Access, D);
+ Entity.setDiag(diag::err_access) << E->getSourceRange();
+
+ return CheckAccess(*this, E->getNameLoc(), Entity);
}
/// Perform access-control checking on a previously-unresolved member
@@ -445,56 +450,90 @@ Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
NamedDecl *D,
AccessSpecifier Access) {
- if (!getLangOptions().AccessControl)
+ if (!getLangOptions().AccessControl ||
+ Access == AS_public)
return AR_accessible;
- return CheckAccess(*this, E->getMemberLoc(),
- AccessedEntity::makeMember(E->getNamingClass(), Access, D));
+ AccessedEntity Entity(AccessedEntity::Member,
+ E->getNamingClass(), Access, D);
+ Entity.setDiag(diag::err_access) << E->getSourceRange();
+
+ return CheckAccess(*this, E->getMemberLoc(), Entity);
}
Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
- const RecordType *RT) {
+ CXXDestructorDecl *Dtor,
+ const PartialDiagnostic &PDiag) {
if (!getLangOptions().AccessControl)
return AR_accessible;
- CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
- CXXDestructorDecl *Dtor = NamingClass->getDestructor(Context);
-
+ // There's never a path involved when checking implicit destructor access.
AccessSpecifier Access = Dtor->getAccess();
if (Access == AS_public)
return AR_accessible;
- return CheckAccess(*this, Loc,
- AccessedEntity::makeMember(NamingClass, Access, Dtor));
+ CXXRecordDecl *NamingClass = Dtor->getParent();
+ AccessedEntity Entity(AccessedEntity::Member, NamingClass, Access, Dtor);
+ Entity.setDiag(PDiag); // TODO: avoid copy
+
+ return CheckAccess(*this, Loc, Entity);
}
/// Checks access to a constructor.
Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
CXXConstructorDecl *Constructor,
AccessSpecifier Access) {
- if (!getLangOptions().AccessControl)
+ if (!getLangOptions().AccessControl ||
+ Access == AS_public)
return AR_accessible;
CXXRecordDecl *NamingClass = Constructor->getParent();
- return CheckAccess(*this, UseLoc,
- AccessedEntity::makeMember(NamingClass, Access, Constructor));
+ AccessedEntity Entity(AccessedEntity::Member,
+ NamingClass, Access, Constructor);
+ Entity.setDiag(diag::err_access_ctor);
+
+ return CheckAccess(*this, UseLoc, Entity);
+}
+
+/// Checks direct (i.e. non-inherited) access to an arbitrary class
+/// member.
+Sema::AccessResult Sema::CheckDirectMemberAccess(SourceLocation UseLoc,
+ NamedDecl *Target,
+ const PartialDiagnostic &Diag) {
+ AccessSpecifier Access = Target->getAccess();
+ if (!getLangOptions().AccessControl ||
+ Access == AS_public)
+ return AR_accessible;
+
+ CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Target->getDeclContext());
+ AccessedEntity Entity(AccessedEntity::Member, NamingClass, Access, Target);
+ Entity.setDiag(Diag);
+ return CheckAccess(*this, UseLoc, Entity);
}
+
/// Checks access to an overloaded member operator, including
/// conversion operators.
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
+ Expr *ArgExpr,
NamedDecl *MemberOperator,
AccessSpecifier Access) {
- if (!getLangOptions().AccessControl)
+ if (!getLangOptions().AccessControl ||
+ Access == AS_public)
return AR_accessible;
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());
- return CheckAccess(*this, OpLoc,
- AccessedEntity::makeMember(NamingClass, Access, MemberOperator));
+ AccessedEntity Entity(AccessedEntity::Member,
+ NamingClass, Access, MemberOperator);
+ Entity.setDiag(diag::err_access)
+ << ObjectExpr->getSourceRange()
+ << (ArgExpr ? ArgExpr->getSourceRange() : SourceRange());
+
+ return CheckAccess(*this, OpLoc, Entity);
}
/// Checks access for a hierarchy conversion.
@@ -507,31 +546,29 @@ Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
/// context had no special privileges
/// \param ADK controls the kind of diagnostics that are used
Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc,
- bool IsBaseToDerived,
QualType Base,
QualType Derived,
const CXXBasePath &Path,
+ unsigned DiagID,
bool ForceCheck,
- bool ForceUnprivileged,
- AccessDiagnosticsKind ADK) {
+ bool ForceUnprivileged) {
if (!ForceCheck && !getLangOptions().AccessControl)
return AR_accessible;
if (Path.Access == AS_public)
return AR_accessible;
- // TODO: preserve the information about which types exactly were used.
CXXRecordDecl *BaseD, *DerivedD;
BaseD = cast<CXXRecordDecl>(Base->getAs<RecordType>()->getDecl());
DerivedD = cast<CXXRecordDecl>(Derived->getAs<RecordType>()->getDecl());
- AccessedEntity Entity = AccessedEntity::makeBaseClass(IsBaseToDerived,
- BaseD, DerivedD,
- Path.Access);
+
+ AccessedEntity Entity(AccessedEntity::Base, BaseD, DerivedD, Path.Access);
+ if (DiagID)
+ Entity.setDiag(DiagID) << Derived << Base;
if (ForceUnprivileged)
- return CheckEffectiveAccess(*this, EffectiveContext(),
- AccessLoc, Entity, ADK);
- return CheckAccess(*this, AccessLoc, Entity, ADK);
+ return CheckEffectiveAccess(*this, EffectiveContext(), AccessLoc, Entity);
+ return CheckAccess(*this, AccessLoc, Entity);
}
/// Checks access to all the declarations in the given result set.
@@ -540,9 +577,13 @@ void Sema::CheckLookupAccess(const LookupResult &R) {
&& "performing access check without access control");
assert(R.getNamingClass() && "performing access check without naming class");
- for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
- if (I.getAccess() != AS_public)
- CheckAccess(*this, R.getNameLoc(),
- AccessedEntity::makeMember(R.getNamingClass(),
- I.getAccess(), *I));
+ for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
+ if (I.getAccess() != AS_public) {
+ AccessedEntity Entity(AccessedEntity::Member,
+ R.getNamingClass(), I.getAccess(), *I);
+ Entity.setDiag(diag::err_access);
+
+ CheckAccess(*this, R.getNameLoc(), Entity);
+ }
+ }
}