diff options
-rw-r--r-- | clang/lib/Sema/Sema.h | 60 | ||||
-rw-r--r-- | clang/lib/Sema/SemaAccess.cpp | 37 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 67 | ||||
-rw-r--r-- | clang/test/CXX/class.access/p6.cpp | 26 |
4 files changed, 162 insertions, 28 deletions
diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 86d867858ba..4fbc42cb0f5 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -281,9 +281,55 @@ public: llvm::DenseMap<DeclarationName, VarDecl *> TentativeDefinitions; std::vector<DeclarationName> TentativeDefinitionList; - /// \brief The collection of delayed deprecation warnings. - llvm::SmallVector<std::pair<SourceLocation,NamedDecl*>, 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<DelayedDiagnostic, 8> 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/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index 26bafa525d8..f3c10392f7c 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/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<CXXRecordDecl>(DC) && cast<CXXRecordDecl>(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/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 40169b635d4..a391a0eaed1 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/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<DeprecatedAttr>()) - return true; - } while ((D = cast_or_null<Decl>(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<Decl>())) { - 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<Decl>() : 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<DeprecatedAttr>()) + return true; + } while ((D = cast_or_null<Decl>(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/clang/test/CXX/class.access/p6.cpp b/clang/test/CXX/class.access/p6.cpp new file mode 100644 index 00000000000..5c0dcf34d21 --- /dev/null +++ b/clang/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() { } +} |