140 lines
4.8 KiB
C++
140 lines
4.8 KiB
C++
//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===//
|
|
//
|
|
// 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.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Sema.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/CXXInheritance.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
using namespace clang;
|
|
|
|
/// 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).
|
|
bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
|
|
NamedDecl *PrevMemberDecl,
|
|
AccessSpecifier LexicalAS) {
|
|
if (!PrevMemberDecl) {
|
|
// Use the lexical access specifier.
|
|
MemberDecl->setAccess(LexicalAS);
|
|
return false;
|
|
}
|
|
|
|
// 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()) {
|
|
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;
|
|
}
|
|
|
|
/// 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(
|
|
QualType Derived, QualType Base, CXXBasePaths &Paths, bool NoPrivileges) {
|
|
Base = Context.getCanonicalType(Base).getUnqualifiedType();
|
|
assert(!Paths.isAmbiguous(Base) &&
|
|
"Can't check base class access if set of paths is ambiguous");
|
|
assert(Paths.isRecordingPaths() &&
|
|
"Can't check base class access without recorded paths");
|
|
|
|
|
|
const CXXBaseSpecifier *InaccessibleBase = 0;
|
|
|
|
const CXXRecordDecl *CurrentClassDecl = 0;
|
|
if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(getCurFunctionDecl()))
|
|
CurrentClassDecl = MD->getParent();
|
|
|
|
for (CXXBasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end();
|
|
Path != PathsEnd; ++Path) {
|
|
|
|
bool FoundInaccessibleBase = false;
|
|
|
|
for (CXXBasePath::const_iterator Element = Path->begin(),
|
|
ElementEnd = Path->end(); Element != ElementEnd; ++Element) {
|
|
const CXXBaseSpecifier *Base = Element->Base;
|
|
|
|
switch (Base->getAccessSpecifier()) {
|
|
default:
|
|
assert(0 && "invalid access specifier");
|
|
case AS_public:
|
|
// Nothing to do.
|
|
break;
|
|
case AS_private:
|
|
// FIXME: Check if the current function/class is a friend.
|
|
if (NoPrivileges || CurrentClassDecl != Element->Class)
|
|
FoundInaccessibleBase = true;
|
|
break;
|
|
case AS_protected:
|
|
// FIXME: Implement
|
|
break;
|
|
}
|
|
|
|
if (FoundInaccessibleBase) {
|
|
InaccessibleBase = Base;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!FoundInaccessibleBase) {
|
|
// We found a path to the base, our work here is done.
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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]
|
|
bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base,
|
|
unsigned InaccessibleBaseID,
|
|
CXXBasePaths &Paths, SourceLocation AccessLoc,
|
|
DeclarationName Name) {
|
|
|
|
if (!getLangOptions().AccessControl)
|
|
return false;
|
|
const CXXBaseSpecifier *InaccessibleBase = FindInaccessibleBase(
|
|
Derived, Base, Paths);
|
|
|
|
if (InaccessibleBase) {
|
|
Diag(AccessLoc, InaccessibleBaseID)
|
|
<< Derived << Base << Name;
|
|
|
|
AccessSpecifier AS = InaccessibleBase->getAccessSpecifierAsWritten();
|
|
|
|
// If there's no written access specifier, then the inheritance specifier
|
|
// is implicitly private.
|
|
if (AS == AS_none)
|
|
Diag(InaccessibleBase->getSourceRange().getBegin(),
|
|
diag::note_inheritance_implicitly_private_here);
|
|
else
|
|
Diag(InaccessibleBase->getSourceRange().getBegin(),
|
|
diag::note_inheritance_specifier_here) << AS;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|