#include "clang/Basic/Specifiers.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DependentDiagnostic.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
using namespace clang;
using namespace sema;
enum AccessResult {
AR_accessible,
AR_inaccessible,
AR_dependent
};
bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
NamedDecl *PrevMemberDecl,
AccessSpecifier LexicalAS) {
if (!PrevMemberDecl) {
MemberDecl->setAccess(LexicalAS);
return false;
}
if (LexicalAS != AS_none && LexicalAS != PrevMemberDecl->getAccess()) {
Diag(MemberDecl->getLocation(),
diag::err_class_redeclared_with_different_access)
<< MemberDecl << LexicalAS;
Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration)
<< PrevMemberDecl << PrevMemberDecl->getAccess();
MemberDecl->setAccess(LexicalAS);
return true;
}
MemberDecl->setAccess(PrevMemberDecl->getAccess());
return false;
}
static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
DeclContext *DC = D->getDeclContext();
if (isa<EnumDecl>(DC))
DC = cast<EnumDecl>(DC)->getDeclContext();
CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(DC);
while (DeclaringClass->isAnonymousStructOrUnion())
DeclaringClass = cast<CXXRecordDecl>(DeclaringClass->getDeclContext());
return DeclaringClass;
}
namespace {
struct EffectiveContext {
EffectiveContext() : Inner(nullptr), Dependent(false) {}
explicit EffectiveContext(DeclContext *DC)
: Inner(DC),
Dependent(DC->isDependentContext()) {
if (auto *DGD = dyn_cast<CXXDeductionGuideDecl>(DC)) {
if (DGD->isImplicit()) {
DC = DGD->getCorrespondingConstructor();
if (!DC) {
DC = cast<DeclContext>(DGD->getDeducedTemplate()->getTemplatedDecl());
}
}
}
while (true) {
if (isa<CXXRecordDecl>(DC)) {
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
Records.push_back(Record->getCanonicalDecl());
DC = Record->getDeclContext();
} else if (isa<FunctionDecl>(DC)) {
FunctionDecl *Function = cast<FunctionDecl>(DC);
Functions.push_back(Function->getCanonicalDecl());
if (Function->getFriendObjectKind())
DC = Function->getLexicalDeclContext();
else
DC = Function->getDeclContext();
} else if (DC->isFileContext()) {
break;
} else {
DC = DC->getParent();
}
}
}
bool isDependent() const { return Dependent; }
bool includesClass(const CXXRecordDecl *R) const {
R = R->getCanonicalDecl();
return llvm::is_contained(Records, R);
}
DeclContext *getInnerContext() const {
return Inner;
}
typedef SmallVectorImpl<CXXRecordDecl*>::const_iterator record_iterator;
DeclContext *Inner;
SmallVector<FunctionDecl*, 4> Functions;
SmallVector<CXXRecordDecl*, 4> Records;
bool Dependent;
};
struct AccessTarget : public AccessedEntity {
AccessTarget(const AccessedEntity &Entity)
: AccessedEntity(Entity) {
initialize();
}
AccessTarget(ASTContext &Context,
MemberNonce _,
CXXRecordDecl *NamingClass,
DeclAccessPair FoundDecl,
QualType BaseObjectType)
: AccessedEntity(Context.getDiagAllocator(), Member, NamingClass,
FoundDecl, BaseObjectType) {
initialize();
}
AccessTarget(ASTContext &Context,
BaseNonce _,
CXXRecordDecl *BaseClass,
CXXRecordDecl *DerivedClass,
AccessSpecifier Access)
: AccessedEntity(Context.getDiagAllocator(), Base, BaseClass, DerivedClass,
Access) {
initialize();
}
bool isInstanceMember() const {
return (isMemberAccess() && getTargetDecl()->isCXXInstanceMember());
}
bool hasInstanceContext() const {
return HasInstanceContext;
}
class SavedInstanceContext {
public:
SavedInstanceContext(SavedInstanceContext &&S)
: Target(S.Target), Has(S.Has) {
S.Target = nullptr;
}
~SavedInstanceContext() {
if (Target)
Target->HasInstanceContext = Has;
}
private:
friend struct AccessTarget;
explicit SavedInstanceContext(AccessTarget &Target)
: Target(&Target), Has(Target.HasInstanceContext) {}
AccessTarget *Target;
bool Has;
};
SavedInstanceContext saveInstanceContext() {
return SavedInstanceContext(*this);
}
void suppressInstanceContext() {
HasInstanceContext = false;
}
const CXXRecordDecl *resolveInstanceContext(Sema &S) const {
assert(HasInstanceContext);
if (CalculatedInstanceContext)
return InstanceContext;
CalculatedInstanceContext = true;
DeclContext *IC = S.computeDeclContext(getBaseObjectType());
InstanceContext = (IC ? cast<CXXRecordDecl>(IC)->getCanonicalDecl()
: nullptr);
return InstanceContext;
}
const CXXRecordDecl *getDeclaringClass() const {
return DeclaringClass;
}
const CXXRecordDecl *getEffectiveNamingClass() const {
const CXXRecordDecl *namingClass = getNamingClass();
while (namingClass->isAnonymousStructOrUnion())
namingClass = cast<CXXRecordDecl>(namingClass->getParent());
return namingClass->getCanonicalDecl();
}
private:
void initialize() {
HasInstanceContext = (isMemberAccess() &&
!getBaseObjectType().isNull() &&
getTargetDecl()->isCXXInstanceMember());
CalculatedInstanceContext = false;
InstanceContext = nullptr;
if (isMemberAccess())
DeclaringClass = FindDeclaringClass(getTargetDecl());
else
DeclaringClass = getBaseClass();
DeclaringClass = DeclaringClass->getCanonicalDecl();
}
bool HasInstanceContext : 1;
mutable bool CalculatedInstanceContext : 1;
mutable const CXXRecordDecl *InstanceContext;
const CXXRecordDecl *DeclaringClass;
};
}
static bool MightInstantiateTo(const CXXRecordDecl *From,
const CXXRecordDecl *To) {
if (From->getDeclName() != To->getDeclName())
return false;
const DeclContext *FromDC = From->getDeclContext()->getPrimaryContext();
const DeclContext *ToDC = To->getDeclContext()->getPrimaryContext();
if (FromDC == ToDC) return true;
if (FromDC->isFileContext() || ToDC->isFileContext()) return false;
return true;
}
static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived,
const CXXRecordDecl *Target) {
assert(Derived->getCanonicalDecl() == Derived);
assert(Target->getCanonicalDecl() == Target);
if (Derived == Target) return AR_accessible;
bool CheckDependent = Derived->isDependentContext();
if (CheckDependent && MightInstantiateTo(Derived, Target))
return AR_dependent;
AccessResult OnFailure = AR_inaccessible;
SmallVector<const CXXRecordDecl*, 8> Queue;
while (true) {
if (Derived->isDependentContext() && !Derived->hasDefinition() &&
!Derived->isLambda())
return AR_dependent;
for (const auto &I : Derived->bases()) {
const CXXRecordDecl *RD;
QualType T = I.getType();
if (const RecordType *RT = T->getAs<RecordType>()) {
RD = cast<CXXRecordDecl>(RT->getDecl());
} else if (const InjectedClassNameType *IT
= T->getAs<InjectedClassNameType>()) {
RD = IT->getDecl();
} else {
assert(T->isDependentType() && "non-dependent base wasn't a record?");
OnFailure = AR_dependent;
continue;
}
RD = RD->getCanonicalDecl();
if (RD == Target) return AR_accessible;
if (CheckDependent && MightInstantiateTo(RD, Target))
OnFailure = AR_dependent;
Queue.push_back(RD);
}
if (Queue.empty()) break;
Derived = Queue.pop_back_val();
}
return OnFailure;
}
static bool MightInstantiateTo(Sema &S, DeclContext *Context,
DeclContext *Friend) {
if (Friend == Context)
return true;
assert(!Friend->isDependentContext() &&
"can't handle friends with dependent contexts here");
if (!Context->isDependentContext())
return false;
if (Friend->isFileContext())
return false;
return true;
}
static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) {
if (Friend == Context)
return true;
if (!Friend->isDependentType() && !Context->isDependentType())
return false;
return true;
}
static bool MightInstantiateTo(Sema &S,
FunctionDecl *Context,
FunctionDecl *Friend) {
if (Context->getDeclName() != Friend->getDeclName())
return false;
if (!MightInstantiateTo(S,
Context->getDeclContext(),
Friend->getDeclContext()))
return false;
CanQual<FunctionProtoType> FriendTy
= S.Context.getCanonicalType(Friend->getType())
->getAs<FunctionProtoType>();
CanQual<FunctionProtoType> ContextTy
= S.Context.getCanonicalType(Context->getType())
->getAs<FunctionProtoType>();
if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
return false;
if (FriendTy->getNumParams() != ContextTy->getNumParams())
return false;
if (!MightInstantiateTo(S, ContextTy->getReturnType(),
FriendTy->getReturnType()))
return false;
for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I)
if (!MightInstantiateTo(S, ContextTy->getParamType(I),
FriendTy->getParamType(I)))
return false;
return true;
}
static bool MightInstantiateTo(Sema &S,
FunctionTemplateDecl *Context,
FunctionTemplateDecl *Friend) {
return MightInstantiateTo(S,
Context->getTemplatedDecl(),
Friend->getTemplatedDecl());
}
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
const CXXRecordDecl *Friend) {
if (EC.includesClass(Friend))
return AR_accessible;
if (EC.isDependent()) {
for (const CXXRecordDecl *Context : EC.Records) {
if (MightInstantiateTo(Context, Friend))
return AR_dependent;
}
}
return AR_inaccessible;
}
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
CanQualType Friend) {
if (const RecordType *RT = Friend->getAs<RecordType>())
return MatchesFriend(S, EC, cast<CXXRecordDecl>(RT->getDecl()));
if (Friend->isDependentType())
return AR_dependent;
return AR_inaccessible;
}
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
ClassTemplateDecl *Friend) {
AccessResult OnFailure = AR_inaccessible;
for (SmallVectorImpl<CXXRecordDecl*>::const_iterator
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
CXXRecordDecl *Record = *I;
ClassTemplateDecl *CTD;
if (isa<ClassTemplateSpecializationDecl>(Record)) {
CTD = cast<ClassTemplateSpecializationDecl>(Record)
->getSpecializedTemplate();
} else {
CTD = Record->getDescribedClassTemplate();
if (!CTD) continue;
}
if (Friend == CTD->getCanonicalDecl())
return AR_accessible;
if (!EC.isDependent())
continue;
if (CTD->getDeclName() != Friend->getDeclName())
continue;
if (!MightInstantiateTo(S, CTD->getDeclContext(),
Friend->getDeclContext()))
continue;
OnFailure = AR_dependent;
}
return OnFailure;
}
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
FunctionDecl *Friend) {
AccessResult OnFailure = AR_inaccessible;
for (SmallVectorImpl<FunctionDecl*>::const_iterator
I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
if (Friend == *I)
return AR_accessible;
if (EC.isDependent() && MightInstantiateTo(S, *I, Friend))
OnFailure = AR_dependent;
}
return OnFailure;
}
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
FunctionTemplateDecl *Friend) {
if (EC.Functions.empty()) return AR_inaccessible;
AccessResult OnFailure = AR_inaccessible;
for (SmallVectorImpl<FunctionDecl*>::const_iterator
I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
FunctionTemplateDecl *FTD = (*I)->getPrimaryTemplate();
if (!FTD)
FTD = (*I)->getDescribedFunctionTemplate();
if (!FTD)
continue;
FTD = FTD->getCanonicalDecl();
if (Friend == FTD)
return AR_accessible;
if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend))
OnFailure = AR_dependent;
}
return OnFailure;
}
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
FriendDecl *FriendD) {
if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend())
return AR_accessible;
if (TypeSourceInfo *T = FriendD->getFriendType())
return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified());
NamedDecl *Friend
= cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl());
if (isa<ClassTemplateDecl>(Friend))
return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
if (isa<FunctionTemplateDecl>(Friend))
return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
if (isa<CXXRecordDecl>(Friend))
return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
}
static AccessResult GetFriendKind(Sema &S,
const EffectiveContext &EC,
const CXXRecordDecl *Class) {
AccessResult OnFailure = AR_inaccessible;
for (auto *Friend : Class->friends()) {
switch (MatchesFriend(S, EC, Friend)) {
case AR_accessible:
return AR_accessible;
case AR_inaccessible:
continue;
case AR_dependent:
OnFailure = AR_dependent;
break;
}
}
return OnFailure;
}
namespace {
struct ProtectedFriendContext {
Sema &S;
const EffectiveContext &EC;
const CXXRecordDecl *NamingClass;
bool CheckDependent;
bool EverDependent;
SmallVector<const CXXRecordDecl*, 20> CurPath;
ProtectedFriendContext(Sema &S, const EffectiveContext &EC,
const CXXRecordDecl *InstanceContext,
const CXXRecordDecl *NamingClass)
: S(S), EC(EC), NamingClass(NamingClass),
CheckDependent(InstanceContext->isDependentContext() ||
NamingClass->isDependentContext()),
EverDependent(false) {}
bool checkFriendshipAlongPath(unsigned I) {
assert(I < CurPath.size());
for (unsigned E = CurPath.size(); I != E; ++I) {
switch (GetFriendKind(S, EC, CurPath[I])) {
case AR_accessible: return true;
case AR_inaccessible: continue;
case AR_dependent: EverDependent = true; continue;
}
}
return false;
}
bool findFriendship(const CXXRecordDecl *Cur, unsigned PrivateDepth) {
if (Cur == NamingClass)
return checkFriendshipAlongPath(PrivateDepth);
if (CheckDependent && MightInstantiateTo(Cur, NamingClass))
EverDependent = true;
for (const auto &I : Cur->bases()) {
unsigned BasePrivateDepth = PrivateDepth;
if (I.getAccessSpecifier() == AS_private)
BasePrivateDepth = CurPath.size() - 1;
const CXXRecordDecl *RD;
QualType T = I.getType();
if (const RecordType *RT = T->getAs<RecordType>()) {
RD = cast<CXXRecordDecl>(RT->getDecl());
} else if (const InjectedClassNameType *IT
= T->getAs<InjectedClassNameType>()) {
RD = IT->getDecl();
} else {
assert(T->isDependentType() && "non-dependent base wasn't a record?");
EverDependent = true;
continue;
}
CurPath.push_back(RD);
if (findFriendship(RD->getCanonicalDecl(), BasePrivateDepth))
return true;
CurPath.pop_back();
}
return false;
}
bool findFriendship(const CXXRecordDecl *Cur) {
assert(CurPath.empty());
CurPath.push_back(Cur);
return findFriendship(Cur, 0);
}
};
}
static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
const CXXRecordDecl *InstanceContext,
const CXXRecordDecl *NamingClass) {
assert(InstanceContext == nullptr ||
InstanceContext->getCanonicalDecl() == InstanceContext);
assert(NamingClass->getCanonicalDecl() == NamingClass);
if (!InstanceContext) return GetFriendKind(S, EC, NamingClass);
ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass);
if (PRC.findFriendship(InstanceContext)) return AR_accessible;
if (PRC.EverDependent) return AR_dependent;
return AR_inaccessible;
}
static AccessResult HasAccess(Sema &S,
const EffectiveContext &EC,
const CXXRecordDecl *NamingClass,
AccessSpecifier Access,
const AccessTarget &Target) {
assert(NamingClass->getCanonicalDecl() == NamingClass &&
"declaration should be canonicalized before being passed here");
if (Access == AS_public) return AR_accessible;
assert(Access == AS_private || Access == AS_protected);
AccessResult OnFailure = AR_inaccessible;
for (EffectiveContext::record_iterator
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
const CXXRecordDecl *ECRecord = *I;
if (Access == AS_private) {
if (ECRecord == NamingClass)
return AR_accessible;
if (EC.isDependent() && MightInstantiateTo(ECRecord, NamingClass))
OnFailure = AR_dependent;
} else {
assert(Access == AS_protected);
switch (IsDerivedFromInclusive(ECRecord, NamingClass)) {
case AR_accessible: break;
case AR_inaccessible: continue;
case AR_dependent: OnFailure = AR_dependent; continue;
}
if (!Target.hasInstanceContext()) {
if (!Target.isInstanceMember()) return AR_accessible;
if (S.getLangOpts().MSVCCompat && !EC.Functions.empty())
if (CXXMethodDecl* MD = dyn_cast<CXXMethodDecl>(EC.Functions.front()))
if (MD->isStatic()) return AR_accessible;
if (NamingClass == ECRecord) return AR_accessible;
continue;
}
assert(Target.isInstanceMember());
const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S);
if (!InstanceContext) {
OnFailure = AR_dependent;
continue;
}
switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) {
case AR_accessible: return AR_accessible;
case AR_inaccessible: continue;
case AR_dependent: OnFailure = AR_dependent; continue;
}
}
}
if (Access == AS_protected && Target.isInstanceMember()) {
const CXXRecordDecl *InstanceContext = nullptr;
if (Target.hasInstanceContext()) {
InstanceContext = Target.resolveInstanceContext(S);
if (!InstanceContext) return AR_dependent;
}
switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) {
case AR_accessible: return AR_accessible;
case AR_inaccessible: return OnFailure;
case AR_dependent: return AR_dependent;
}
llvm_unreachable("impossible friendship kind");
}
switch (GetFriendKind(S, EC, NamingClass)) {
case AR_accessible: return AR_accessible;
case AR_inaccessible: return OnFailure;
case AR_dependent: return AR_dependent;
}
llvm_unreachable("impossible friendship kind");
}
static CXXBasePath *FindBestPath(Sema &S,
const EffectiveContext &EC,
AccessTarget &Target,
AccessSpecifier FinalAccess,
CXXBasePaths &Paths) {
const CXXRecordDecl *Derived = Target.getNamingClass();
const CXXRecordDecl *Base = Target.getDeclaringClass();
bool isDerived = Derived->isDerivedFrom(const_cast<CXXRecordDecl*>(Base),
Paths);
assert(isDerived && "derived class not actually derived from base");
(void) isDerived;
CXXBasePath *BestPath = nullptr;
assert(FinalAccess != AS_none && "forbidden access after declaring class");
bool AnyDependent = false;
for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end();
PI != PE; ++PI) {
AccessTarget::SavedInstanceContext _ = Target.saveInstanceContext();
AccessSpecifier PathAccess = FinalAccess;
CXXBasePath::iterator I = PI->end(), E = PI->begin();
while (I != E) {
--I;
assert(PathAccess != AS_none);
if (PathAccess == AS_private) {
PathAccess = AS_none;
break;
}
const CXXRecordDecl *NC = I->Class->getCanonicalDecl();
AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
PathAccess = std::max(PathAccess, BaseAccess);
switch (HasAccess(S, EC, NC, PathAccess, Target)) {
case AR_inaccessible: break;
case AR_accessible:
PathAccess = AS_public;
Target.suppressInstanceContext();
break;
case AR_dependent:
AnyDependent = true;
goto Next;
}
}
if (BestPath == nullptr || PathAccess < BestPath->Access) {
BestPath = &*PI;
BestPath->Access = PathAccess;
if (BestPath->Access == AS_public)
return BestPath;
}
Next: ;
}
assert((!BestPath || BestPath->Access != AS_public) &&
"fell out of loop with public path");
if (AnyDependent)
return nullptr;
return BestPath;
}
static bool TryDiagnoseProtectedAccess(Sema &S, const EffectiveContext &EC,
AccessTarget &Target) {
if (!Target.isInstanceMember())
return false;
assert(Target.isMemberAccess());
const CXXRecordDecl *NamingClass = Target.getEffectiveNamingClass();
for (EffectiveContext::record_iterator
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
const CXXRecordDecl *ECRecord = *I;
switch (IsDerivedFromInclusive(ECRecord, NamingClass)) {
case AR_accessible: break;
case AR_inaccessible: continue;
case AR_dependent: continue;
}
NamedDecl *D = Target.getTargetDecl();
if (!Target.hasInstanceContext()) {
if (NamingClass == ECRecord) continue;
S.Diag(D->getLocation(), diag::note_access_protected_restricted_noobject)
<< S.Context.getTypeDeclType(ECRecord);
return true;
}
const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S);
assert(InstanceContext && "diagnosing dependent access");
switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) {
case AR_accessible: continue;
case AR_dependent: continue;
case AR_inaccessible:
break;
}
if (isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D) ||
(isa<FunctionTemplateDecl>(D) &&
isa<CXXConstructorDecl>(
cast<FunctionTemplateDecl>(D)->getTemplatedDecl()))) {
return S.Diag(D->getLocation(),
diag::note_access_protected_restricted_ctordtor)
<< isa<CXXDestructorDecl>(D->getAsFunction());
}
return S.Diag(D->getLocation(),
diag::note_access_protected_restricted_object)
<< S.Context.getTypeDeclType(ECRecord);
}
return false;
}
static void diagnoseBadDirectAccess(Sema &S,
const EffectiveContext &EC,
AccessTarget &entity) {
assert(entity.isMemberAccess());
NamedDecl *D = entity.getTargetDecl();
if (D->getAccess() == AS_protected &&
TryDiagnoseProtectedAccess(S, EC, entity))
return;
while (D->isOutOfLine()) {
NamedDecl *PrevDecl = nullptr;
if (VarDecl *VD = dyn_cast<VarDecl>(D))
PrevDecl = VD->getPreviousDecl();
else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
PrevDecl = FD->getPreviousDecl();
else if (TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(D))
PrevDecl = TND->getPreviousDecl();
else if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
if (isa<RecordDecl>(D) && cast<RecordDecl>(D)->isInjectedClassName())
break;
PrevDecl = TD->getPreviousDecl();
}
if (!PrevDecl) break;
D = PrevDecl;
}
CXXRecordDecl *DeclaringClass = FindDeclaringClass(D);
Decl *ImmediateChild;
if (D->getDeclContext() == DeclaringClass)
ImmediateChild = D;
else {
DeclContext *DC = D->getDeclContext();
while (DC->getParent() != DeclaringClass)
DC = DC->getParent();
ImmediateChild = cast<Decl>(DC);
}
bool isImplicit = true;
for (const auto *I : DeclaringClass->decls()) {
if (I == ImmediateChild) break;
if (isa<AccessSpecDecl>(I)) {
isImplicit = false;
break;
}
}
S.Diag(D->getLocation(), diag::note_access_natural)
<< (unsigned) (D->getAccess() == AS_protected)
<< isImplicit;
}
static void DiagnoseAccessPath(Sema &S,
const EffectiveContext &EC,
AccessTarget &entity) {
AccessTarget::SavedInstanceContext _ = entity.saveInstanceContext();
AccessSpecifier accessSoFar = AS_public;
if (entity.isMemberAccess()) {
NamedDecl *D = entity.getTargetDecl();
accessSoFar = D->getAccess();
const CXXRecordDecl *declaringClass = entity.getDeclaringClass();
switch (HasAccess(S, EC, declaringClass, accessSoFar, entity)) {
case AR_accessible:
accessSoFar = AS_public;
entity.suppressInstanceContext();
break;
case AR_inaccessible:
if (accessSoFar == AS_private ||
declaringClass == entity.getEffectiveNamingClass())
return diagnoseBadDirectAccess(S, EC, entity);
break;
case AR_dependent:
llvm_unreachable("cannot diagnose dependent access");
}
}
CXXBasePaths paths;
CXXBasePath &path = *FindBestPath(S, EC, entity, accessSoFar, paths);
assert(path.Access != AS_public);
CXXBasePath::iterator i = path.end(), e = path.begin();
CXXBasePath::iterator constrainingBase = i;
while (i != e) {
--i;
assert(accessSoFar != AS_none && accessSoFar != AS_private);
const CXXRecordDecl *derivingClass = i->Class->getCanonicalDecl();
const CXXBaseSpecifier *base = i->Base;
AccessSpecifier baseAccess = base->getAccessSpecifier();
if (baseAccess > accessSoFar) {
constrainingBase = i;
accessSoFar = baseAccess;
}
switch (HasAccess(S, EC, derivingClass, accessSoFar, entity)) {
case AR_inaccessible: break;
case AR_accessible:
accessSoFar = AS_public;
entity.suppressInstanceContext();
constrainingBase = nullptr;
break;
case AR_dependent:
llvm_unreachable("cannot diagnose dependent access");
}
if (accessSoFar == AS_private) {
assert(baseAccess == AS_private);
assert(constrainingBase == i);
break;
}
}
if (constrainingBase == path.end())
return diagnoseBadDirectAccess(S, EC, entity);
unsigned diagnostic;
if (entity.isMemberAccess() ||
constrainingBase + 1 != path.end()) {
diagnostic = diag::note_access_constrained_by_path;
} else {
diagnostic = diag::note_access_natural;
}
const CXXBaseSpecifier *base = constrainingBase->Base;
S.Diag(base->getSourceRange().getBegin(), diagnostic)
<< base->getSourceRange()
<< (base->getAccessSpecifier() == AS_protected)
<< (base->getAccessSpecifierAsWritten() == AS_none);
if (entity.isMemberAccess())
S.Diag(entity.getTargetDecl()->getLocation(),
diag::note_member_declared_at);
}
static void DiagnoseBadAccess(Sema &S, SourceLocation Loc,
const EffectiveContext &EC,
AccessTarget &Entity) {
const CXXRecordDecl *NamingClass = Entity.getNamingClass();
const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : nullptr);
S.Diag(Loc, Entity.getDiag())
<< (Entity.getAccess() == AS_protected)
<< (D ? D->getDeclName() : DeclarationName())
<< S.Context.getTypeDeclType(NamingClass)
<< S.Context.getTypeDeclType(DeclaringClass);
DiagnoseAccessPath(S, EC, Entity);
}
static bool IsMicrosoftUsingDeclarationAccessBug(Sema& S,
SourceLocation AccessLoc,
AccessTarget &Entity) {
if (UsingShadowDecl *Shadow =
dyn_cast<UsingShadowDecl>(Entity.getTargetDecl()))
if (UsingDecl *UD = dyn_cast<UsingDecl>(Shadow->getIntroducer())) {
const NamedDecl *OrigDecl = Entity.getTargetDecl()->getUnderlyingDecl();
if (Entity.getTargetDecl()->getAccess() == AS_private &&
(OrigDecl->getAccess() == AS_public ||
OrigDecl->getAccess() == AS_protected)) {
S.Diag(AccessLoc, diag::ext_ms_using_declaration_inaccessible)
<< UD->getQualifiedNameAsString()
<< OrigDecl->getQualifiedNameAsString();
return true;
}
}
return false;
}
static AccessResult IsAccessible(Sema &S,
const EffectiveContext &EC,
AccessTarget &Entity) {
const CXXRecordDecl *NamingClass = Entity.getEffectiveNamingClass();
AccessSpecifier UnprivilegedAccess = Entity.getAccess();
assert(UnprivilegedAccess != AS_public && "public access not weeded out");
if (UnprivilegedAccess != AS_none) {
switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) {
case AR_dependent:
return AR_dependent;
case AR_accessible: return AR_accessible;
case AR_inaccessible: break;
}
}
AccessTarget::SavedInstanceContext _ = Entity.saveInstanceContext();
AccessSpecifier FinalAccess;
if (Entity.isMemberAccess()) {
NamedDecl *Target = Entity.getTargetDecl();
const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
FinalAccess = Target->getAccess();
switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) {
case AR_accessible:
FinalAccess = AS_public;
Entity.suppressInstanceContext();
break;
case AR_inaccessible: break;
case AR_dependent: return AR_dependent; }
if (DeclaringClass == NamingClass)
return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible);
} else {
FinalAccess = AS_public;
}
assert(Entity.getDeclaringClass() != NamingClass);
CXXBasePaths Paths;
CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths);
if (!Path)
return AR_dependent;
assert(Path->Access <= UnprivilegedAccess &&
"access along best path worse than direct?");
if (Path->Access == AS_public)
return AR_accessible;
return AR_inaccessible;
}
static void DelayDependentAccess(Sema &S,
const EffectiveContext &EC,
SourceLocation Loc,
const AccessTarget &Entity) {
assert(EC.isDependent() && "delaying non-dependent access");
DeclContext *DC = EC.getInnerContext();
assert(DC->isDependentContext() && "delaying non-dependent access");
DependentDiagnostic::Create(S.Context, DC, DependentDiagnostic::Access,
Loc,
Entity.isMemberAccess(),
Entity.getAccess(),
Entity.getTargetDecl(),
Entity.getNamingClass(),
Entity.getBaseObjectType(),
Entity.getDiag());
}
static AccessResult CheckEffectiveAccess(Sema &S,
const EffectiveContext &EC,
SourceLocation Loc,
AccessTarget &Entity) {
assert(Entity.getAccess() != AS_public && "called for public access!");
switch (IsAccessible(S, EC, Entity)) {
case AR_dependent:
DelayDependentAccess(S, EC, Loc, Entity);
return AR_dependent;
case AR_inaccessible:
if (S.getLangOpts().MSVCCompat &&
IsMicrosoftUsingDeclarationAccessBug(S, Loc, Entity))
return AR_accessible;
if (!Entity.isQuiet())
DiagnoseBadAccess(S, Loc, EC, Entity);
return AR_inaccessible;
case AR_accessible:
return AR_accessible;
}
llvm_unreachable("invalid access result");
}
static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
AccessTarget &Entity) {
if (Entity.getAccess() == AS_public)
return Sema::AR_accessible;
if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
S.DelayedDiagnostics.add(DelayedDiagnostic::makeAccess(Loc, Entity));
return Sema::AR_delayed;
}
EffectiveContext EC(S.CurContext);
switch (CheckEffectiveAccess(S, EC, Loc, Entity)) {
case AR_accessible: return Sema::AR_accessible;
case AR_inaccessible: return Sema::AR_inaccessible;
case AR_dependent: return Sema::AR_dependent;
}
llvm_unreachable("invalid access result");
}
void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *D) {
DeclContext *DC = D->getDeclContext();
if (D->isLocalExternDecl()) {
DC = D->getLexicalDeclContext();
} else if (FunctionDecl *FN = dyn_cast<FunctionDecl>(D)) {
DC = FN;
} else if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) {
if (isa<DeclContext>(TD->getTemplatedDecl()))
DC = cast<DeclContext>(TD->getTemplatedDecl());
}
EffectiveContext EC(DC);
AccessTarget Target(DD.getAccessData());
if (CheckEffectiveAccess(*this, EC, DD.Loc, Target) == ::AR_inaccessible)
DD.Triggered = true;
}
void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD,
const MultiLevelTemplateArgumentList &TemplateArgs) {
SourceLocation Loc = DD.getAccessLoc();
AccessSpecifier Access = DD.getAccess();
Decl *NamingD = FindInstantiatedDecl(Loc, DD.getAccessNamingClass(),
TemplateArgs);
if (!NamingD) return;
Decl *TargetD = FindInstantiatedDecl(Loc, DD.getAccessTarget(),
TemplateArgs);
if (!TargetD) return;
if (DD.isAccessToMember()) {
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(NamingD);
NamedDecl *TargetDecl = cast<NamedDecl>(TargetD);
QualType BaseObjectType = DD.getAccessBaseObjectType();
if (!BaseObjectType.isNull()) {
BaseObjectType = SubstType(BaseObjectType, TemplateArgs, Loc,
DeclarationName());
if (BaseObjectType.isNull()) return;
}
AccessTarget Entity(Context,
AccessTarget::Member,
NamingClass,
DeclAccessPair::make(TargetDecl, Access),
BaseObjectType);
Entity.setDiag(DD.getDiagnostic());
CheckAccess(*this, Loc, Entity);
} else {
AccessTarget Entity(Context,
AccessTarget::Base,
cast<CXXRecordDecl>(TargetD),
cast<CXXRecordDecl>(NamingD),
Access);
Entity.setDiag(DD.getDiagnostic());
CheckAccess(*this, Loc, Entity);
}
}
Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
DeclAccessPair Found) {
if (!getLangOpts().AccessControl ||
!E->getNamingClass() ||
Found.getAccess() == AS_public)
return AR_accessible;
AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(),
Found, QualType());
Entity.setDiag(diag::err_access) << E->getSourceRange();
return CheckAccess(*this, E->getNameLoc(), Entity);
}
Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
DeclAccessPair Found) {
if (!getLangOpts().AccessControl ||
Found.getAccess() == AS_public)
return AR_accessible;
QualType BaseType = E->getBaseType();
if (E->isArrow())
BaseType = BaseType->castAs<PointerType>()->getPointeeType();
AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(),
Found, BaseType);
Entity.setDiag(diag::err_access) << E->getSourceRange();
return CheckAccess(*this, E->getMemberLoc(), Entity);
}
bool Sema::isMemberAccessibleForDeletion(CXXRecordDecl *NamingClass,
DeclAccessPair Found,
QualType ObjectType,
SourceLocation Loc,
const PartialDiagnostic &Diag) {
if (Found.getAccess() == AS_public || !getLangOpts().AccessControl)
return true;
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
ObjectType);
Entity.setDiag(Diag);
switch (CheckAccess(*this, Loc, Entity)) {
case AR_accessible: return true;
case AR_inaccessible: return false;
case AR_dependent: llvm_unreachable("dependent for =delete computation");
case AR_delayed: llvm_unreachable("cannot delay =delete computation");
}
llvm_unreachable("bad access result");
}
Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
CXXDestructorDecl *Dtor,
const PartialDiagnostic &PDiag,
QualType ObjectTy) {
if (!getLangOpts().AccessControl)
return AR_accessible;
AccessSpecifier Access = Dtor->getAccess();
if (Access == AS_public)
return AR_accessible;
CXXRecordDecl *NamingClass = Dtor->getParent();
if (ObjectTy.isNull()) ObjectTy = Context.getTypeDeclType(NamingClass);
AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
DeclAccessPair::make(Dtor, Access),
ObjectTy);
Entity.setDiag(PDiag);
return CheckAccess(*this, Loc, Entity);
}
Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
CXXConstructorDecl *Constructor,
DeclAccessPair Found,
const InitializedEntity &Entity,
bool IsCopyBindingRefToTemp) {
if (!getLangOpts().AccessControl || Found.getAccess() == AS_public)
return AR_accessible;
PartialDiagnostic PD(PDiag());
switch (Entity.getKind()) {
default:
PD = PDiag(IsCopyBindingRefToTemp
? diag::ext_rvalue_to_reference_access_ctor
: diag::err_access_ctor);
break;
case InitializedEntity::EK_Base:
PD = PDiag(diag::err_access_base_ctor);
PD << Entity.isInheritedVirtualBase()
<< Entity.getBaseSpecifier()->getType() << getSpecialMember(Constructor);
break;
case InitializedEntity::EK_Member: {
const FieldDecl *Field = cast<FieldDecl>(Entity.getDecl());
PD = PDiag(diag::err_access_field_ctor);
PD << Field->getType() << getSpecialMember(Constructor);
break;
}
case InitializedEntity::EK_LambdaCapture: {
StringRef VarName = Entity.getCapturedVarName();
PD = PDiag(diag::err_access_lambda_capture);
PD << VarName << Entity.getType() << getSpecialMember(Constructor);
break;
}
}
return CheckConstructorAccess(UseLoc, Constructor, Found, Entity, PD);
}
Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
CXXConstructorDecl *Constructor,
DeclAccessPair Found,
const InitializedEntity &Entity,
const PartialDiagnostic &PD) {
if (!getLangOpts().AccessControl ||
Found.getAccess() == AS_public)
return AR_accessible;
CXXRecordDecl *NamingClass = Constructor->getParent();
CXXRecordDecl *ObjectClass;
if ((Entity.getKind() == InitializedEntity::EK_Base ||
Entity.getKind() == InitializedEntity::EK_Delegating) &&
!Entity.getParent()) {
ObjectClass = cast<CXXConstructorDecl>(CurContext)->getParent();
} else if (auto *Shadow =
dyn_cast<ConstructorUsingShadowDecl>(Found.getDecl())) {
ObjectClass = Shadow->getParent();
} else {
ObjectClass = NamingClass;
}
AccessTarget AccessEntity(
Context, AccessTarget::Member, NamingClass,
DeclAccessPair::make(Constructor, Found.getAccess()),
Context.getTypeDeclType(ObjectClass));
AccessEntity.setDiag(PD);
return CheckAccess(*this, UseLoc, AccessEntity);
}
Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc,
SourceRange PlacementRange,
CXXRecordDecl *NamingClass,
DeclAccessPair Found,
bool Diagnose) {
if (!getLangOpts().AccessControl ||
!NamingClass ||
Found.getAccess() == AS_public)
return AR_accessible;
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
QualType());
if (Diagnose)
Entity.setDiag(diag::err_access)
<< PlacementRange;
return CheckAccess(*this, OpLoc, Entity);
}
Sema::AccessResult Sema::CheckMemberAccess(SourceLocation UseLoc,
CXXRecordDecl *NamingClass,
DeclAccessPair Found) {
if (!getLangOpts().AccessControl ||
!NamingClass ||
Found.getAccess() == AS_public)
return AR_accessible;
AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
Found, QualType());
return CheckAccess(*this, UseLoc, Entity);
}
Sema::AccessResult
Sema::CheckStructuredBindingMemberAccess(SourceLocation UseLoc,
CXXRecordDecl *DecomposedClass,
DeclAccessPair Field) {
if (!getLangOpts().AccessControl ||
Field.getAccess() == AS_public)
return AR_accessible;
AccessTarget Entity(Context, AccessTarget::Member, DecomposedClass, Field,
Context.getRecordType(DecomposedClass));
Entity.setDiag(diag::err_decomp_decl_inaccessible_field);
return CheckAccess(*this, UseLoc, Entity);
}
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
const SourceRange &Range,
DeclAccessPair Found) {
if (!getLangOpts().AccessControl || Found.getAccess() == AS_public)
return AR_accessible;
const RecordType *RT = ObjectExpr->getType()->castAs<RecordType>();
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
ObjectExpr->getType());
Entity.setDiag(diag::err_access) << ObjectExpr->getSourceRange() << Range;
return CheckAccess(*this, OpLoc, Entity);
}
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
Expr *ArgExpr,
DeclAccessPair Found) {
return CheckMemberOperatorAccess(
OpLoc, ObjectExpr, ArgExpr ? ArgExpr->getSourceRange() : SourceRange(),
Found);
}
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
ArrayRef<Expr *> ArgExprs,
DeclAccessPair FoundDecl) {
SourceRange R;
if (!ArgExprs.empty()) {
R = SourceRange(ArgExprs.front()->getBeginLoc(),
ArgExprs.back()->getEndLoc());
}
return CheckMemberOperatorAccess(OpLoc, ObjectExpr, R, FoundDecl);
}
Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) {
assert(isa<CXXMethodDecl>(target->getAsFunction()));
AccessSpecifier access = target->getAccess();
if (!getLangOpts().AccessControl || access == AS_public)
return AR_accessible;
CXXMethodDecl *method = cast<CXXMethodDecl>(target->getAsFunction());
AccessTarget entity(Context, AccessTarget::Member,
cast<CXXRecordDecl>(target->getDeclContext()),
DeclAccessPair::make(target, access),
QualType());
entity.setDiag(diag::err_access_friend_function)
<< (method->getQualifier() ? method->getQualifierLoc().getSourceRange()
: method->getNameInfo().getSourceRange());
EffectiveContext EC(CurContext);
switch (CheckEffectiveAccess(*this, EC, target->getLocation(), entity)) {
case ::AR_accessible: return Sema::AR_accessible;
case ::AR_inaccessible: return Sema::AR_inaccessible;
case ::AR_dependent: return Sema::AR_dependent;
}
llvm_unreachable("invalid access result");
}
Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr,
DeclAccessPair Found) {
if (!getLangOpts().AccessControl ||
Found.getAccess() == AS_none ||
Found.getAccess() == AS_public)
return AR_accessible;
OverloadExpr *Ovl = OverloadExpr::find(OvlExpr).Expression;
CXXRecordDecl *NamingClass = Ovl->getNamingClass();
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
QualType());
Entity.setDiag(diag::err_access)
<< Ovl->getSourceRange();
return CheckAccess(*this, Ovl->getNameLoc(), Entity);
}
Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc,
QualType Base,
QualType Derived,
const CXXBasePath &Path,
unsigned DiagID,
bool ForceCheck,
bool ForceUnprivileged) {
if (!ForceCheck && !getLangOpts().AccessControl)
return AR_accessible;
if (Path.Access == AS_public)
return AR_accessible;
CXXRecordDecl *BaseD, *DerivedD;
BaseD = cast<CXXRecordDecl>(Base->castAs<RecordType>()->getDecl());
DerivedD = cast<CXXRecordDecl>(Derived->castAs<RecordType>()->getDecl());
AccessTarget Entity(Context, AccessTarget::Base, BaseD, DerivedD,
Path.Access);
if (DiagID)
Entity.setDiag(DiagID) << Derived << Base;
if (ForceUnprivileged) {
switch (CheckEffectiveAccess(*this, EffectiveContext(),
AccessLoc, Entity)) {
case ::AR_accessible: return Sema::AR_accessible;
case ::AR_inaccessible: return Sema::AR_inaccessible;
case ::AR_dependent: return Sema::AR_dependent;
}
llvm_unreachable("unexpected result from CheckEffectiveAccess");
}
return CheckAccess(*this, AccessLoc, Entity);
}
void Sema::CheckLookupAccess(const LookupResult &R) {
assert(getLangOpts().AccessControl
&& "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) {
AccessTarget Entity(Context, AccessedEntity::Member,
R.getNamingClass(), I.getPair(),
R.getBaseObjectType());
Entity.setDiag(diag::err_access);
CheckAccess(*this, R.getNameLoc(), Entity);
}
}
}
bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass,
QualType BaseType) {
if (Target->isCXXClassMember() && NamingClass) {
if (!getLangOpts().CPlusPlus)
return false;
AccessTarget Entity(Context, AccessedEntity::Member, NamingClass,
DeclAccessPair::make(Target, AS_none), BaseType);
EffectiveContext EC(CurContext);
return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible;
}
if (ObjCIvarDecl *Ivar = dyn_cast<ObjCIvarDecl>(Target)) {
if (Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Public ||
Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Package)
return true;
ObjCInterfaceDecl *ClassOfMethodDecl = nullptr;
if (ObjCMethodDecl *MD = getCurMethodDecl())
ClassOfMethodDecl = MD->getClassInterface();
else if (FunctionDecl *FD = getCurFunctionDecl()) {
if (ObjCImplDecl *Impl
= dyn_cast<ObjCImplDecl>(FD->getLexicalDeclContext())) {
if (ObjCImplementationDecl *IMPD
= dyn_cast<ObjCImplementationDecl>(Impl))
ClassOfMethodDecl = IMPD->getClassInterface();
else if (ObjCCategoryImplDecl* CatImplClass
= dyn_cast<ObjCCategoryImplDecl>(Impl))
ClassOfMethodDecl = CatImplClass->getClassInterface();
}
}
if (!ClassOfMethodDecl)
return false;
if (declaresSameEntity(ClassOfMethodDecl, Ivar->getContainingInterface()))
return true;
if (Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Private)
return false;
return Ivar->getContainingInterface()->isSuperClassOf(ClassOfMethodDecl);
}
return true;
}