From: John McCall Date: Wed, 27 Jan 2010 03:50:35 +0000 (+0000) Subject: Implement access-check delays for out-of-line member definitions X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2f514480c448708ec382a684cf5e035d3a827ec8;p=clang Implement access-check delays for out-of-line member definitions using the same framework we use for deprecation warnings. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94659 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 86d867858b..4fbc42cb0f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -281,9 +281,55 @@ public: llvm::DenseMap TentativeDefinitions; std::vector TentativeDefinitionList; - /// \brief The collection of delayed deprecation warnings. - llvm::SmallVector, 8> - DelayedDeprecationWarnings; + struct DelayedDiagnostic { + enum DDKind { Deprecation, Access }; + + unsigned char Kind; // actually a DDKind + bool Triggered; + + SourceLocation Loc; + + union { + /// Deprecation. + struct { NamedDecl *Decl; } DeprecationData; + + /// Access control. + struct { + NamedDecl *Decl; + AccessSpecifier Access; + CXXRecordDecl *NamingClass; + } AccessData; + }; + + static DelayedDiagnostic makeDeprecation(SourceLocation Loc, + NamedDecl *D) { + DelayedDiagnostic DD; + DD.Kind = Deprecation; + DD.Triggered = false; + DD.Loc = Loc; + DD.DeprecationData.Decl = D; + return DD; + } + + static DelayedDiagnostic makeAccess(SourceLocation Loc, + NamedDecl *Decl, + AccessSpecifier AS, + CXXRecordDecl *NamingClass) { + DelayedDiagnostic DD; + DD.Kind = Access; + DD.Triggered = false; + DD.Loc = Loc; + DD.AccessData.Decl = Decl; + DD.AccessData.Access = AS; + DD.AccessData.NamingClass = NamingClass; + return DD; + } + + }; + + /// \brief The stack of diagnostics that were delayed due to being + /// produced during the parsing of a declaration. + llvm::SmallVector DelayedDiagnostics; /// \brief The depth of the current ParsingDeclaration stack. /// If nonzero, we are currently parsing a declaration (and @@ -1482,6 +1528,8 @@ public: void PopParsingDeclaration(ParsingDeclStackState S, DeclPtrTy D); void EmitDeprecationWarning(NamedDecl *D, SourceLocation Loc); + void HandleDelayedDeprecationCheck(DelayedDiagnostic &DD, Decl *Ctx); + //===--------------------------------------------------------------------===// // Expression Parsing Callbacks: SemaExpr.cpp. @@ -2386,6 +2434,11 @@ public: bool CheckAccess(const LookupResult &R, NamedDecl *D, AccessSpecifier Access); void CheckAccess(const LookupResult &R); + void HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx); + bool CheckEffectiveAccess(DeclContext *EffectiveContext, + const LookupResult &R, NamedDecl *D, + AccessSpecifier Access); + bool CheckBaseClassAccess(QualType Derived, QualType Base, unsigned InaccessibleBaseID, CXXBasePaths& Paths, SourceLocation AccessLoc, @@ -4008,7 +4061,6 @@ private: const PartialDiagnostic &PD, bool Equality = false); void CheckImplicitConversion(Expr *E, QualType Target); - }; //===--------------------------------------------------------------------===// diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index 26bafa525d..f3c10392f7 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -172,8 +172,25 @@ bool Sema::CheckAccess(const LookupResult &R, NamedDecl *D, if (Access == AS_public) return false; - // Otherwise, derive the current class context. - DeclContext *DC = CurContext; + // 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; while (isa(DC) && cast(DC)->isAnonymousStructOrUnion()) DC = DC->getParent(); @@ -233,6 +250,22 @@ bool Sema::CheckAccess(const LookupResult &R, NamedDecl *D, return true; } +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; +} + bool Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, NamedDecl *D, AccessSpecifier Access) { if (!getLangOptions().AccessControl || !E->getNamingClass()) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 40169b635d..a391a0eaed 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -2067,48 +2067,71 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { /// on the warning stack. Action::ParsingDeclStackState Sema::PushParsingDeclaration() { ParsingDeclDepth++; - return (ParsingDeclStackState) DelayedDeprecationWarnings.size(); -} - -static bool isDeclDeprecated(Decl *D) { - do { - if (D->hasAttr()) - return true; - } while ((D = cast_or_null(D->getDeclContext()))); - return false; + return (ParsingDeclStackState) DelayedDiagnostics.size(); } void Sema::PopParsingDeclaration(ParsingDeclStackState S, DeclPtrTy Ctx) { assert(ParsingDeclDepth > 0 && "empty ParsingDeclaration stack"); ParsingDeclDepth--; - if (DelayedDeprecationWarnings.empty()) + if (DelayedDiagnostics.empty()) return; unsigned SavedIndex = (unsigned) S; - assert(SavedIndex <= DelayedDeprecationWarnings.size() && + assert(SavedIndex <= DelayedDiagnostics.size() && "saved index is out of bounds"); - if (Ctx && !isDeclDeprecated(Ctx.getAs())) { - for (unsigned I = 0, E = DelayedDeprecationWarnings.size(); I != E; ++I) { - SourceLocation Loc = DelayedDeprecationWarnings[I].first; - NamedDecl *&ND = DelayedDeprecationWarnings[I].second; - if (ND) { - Diag(Loc, diag::warn_deprecated) << ND->getDeclName(); - - // Prevent this from triggering multiple times. - ND = 0; + // We only want to actually emit delayed diagnostics when we + // successfully parsed a decl. + Decl *D = Ctx ? Ctx.getAs() : 0; + if (D) { + // We really do want to start with 0 here. We get one push for a + // decl spec and another for each declarator; in a decl group like: + // deprecated_typedef foo, *bar, baz(); + // only the declarator pops will be passed decls. This is correct; + // we really do need to consider delayed diagnostics from the decl spec + // for each of the different declarations. + for (unsigned I = 0, E = DelayedDiagnostics.size(); I != E; ++I) { + if (DelayedDiagnostics[I].Triggered) + continue; + + switch (DelayedDiagnostics[I].Kind) { + case DelayedDiagnostic::Deprecation: + HandleDelayedDeprecationCheck(DelayedDiagnostics[I], D); + break; + + case DelayedDiagnostic::Access: + HandleDelayedAccessCheck(DelayedDiagnostics[I], D); + break; } } } - DelayedDeprecationWarnings.set_size(SavedIndex); + DelayedDiagnostics.set_size(SavedIndex); +} + +static bool isDeclDeprecated(Decl *D) { + do { + if (D->hasAttr()) + return true; + } while ((D = cast_or_null(D->getDeclContext()))); + return false; +} + +void Sema::HandleDelayedDeprecationCheck(Sema::DelayedDiagnostic &DD, + Decl *Ctx) { + if (isDeclDeprecated(Ctx)) + return; + + DD.Triggered = true; + Diag(DD.Loc, diag::warn_deprecated) + << DD.DeprecationData.Decl->getDeclName(); } void Sema::EmitDeprecationWarning(NamedDecl *D, SourceLocation Loc) { // Delay if we're currently parsing a declaration. if (ParsingDeclDepth) { - DelayedDeprecationWarnings.push_back(std::make_pair(Loc, D)); + DelayedDiagnostics.push_back(DelayedDiagnostic::makeDeprecation(Loc, D)); return; } diff --git a/test/CXX/class.access/p6.cpp b/test/CXX/class.access/p6.cpp new file mode 100644 index 0000000000..5c0dcf34d2 --- /dev/null +++ b/test/CXX/class.access/p6.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fsyntax-only -faccess-control -verify %s + +// C++0x [class.access]p6: +// All access controls in [class.access] affect the ability to +// access a class member name from a particular scope. For purposes +// of access control, the base-specifiers of a class and the +// definitions of class members that appear outside of the class +// definition are considered to be within the scope of that +// class. In particular, access controls apply as usual to member +// names accessed as part of a function return type, even though it +// is not possible to determine the access privileges of that use +// without first parsing the rest of the function +// declarator. Similarly, access control for implicit calls to the +// constructors, the conversion functions, or the destructor called +// to create and destroy a static data member is performed as if +// these calls appeared in the scope of the member's class. + +namespace test0 { + class A { + typedef int type; // expected-note {{declared private here}} + type foo(); + }; + + A::type foo() { } // expected-error {{access to private member}} + A::type A::foo() { } +}