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 86d8678..4fbc42c 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -281,9 +281,55 @@
   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 @@
   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 @@
   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 @@
                         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 26bafa5..f3c1039 100644
--- a/lib/Sema/SemaAccess.cpp
+++ b/lib/Sema/SemaAccess.cpp
@@ -172,8 +172,25 @@
   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 @@
   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 40169b6..a391a0e 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -2067,7 +2067,47 @@
 /// on the warning stack.
 Action::ParsingDeclStackState Sema::PushParsingDeclaration() {
   ParsingDeclDepth++;
-  return (ParsingDeclStackState) DelayedDeprecationWarnings.size();
+  return (ParsingDeclStackState) DelayedDiagnostics.size();
+}
+
+void Sema::PopParsingDeclaration(ParsingDeclStackState S, DeclPtrTy Ctx) {
+  assert(ParsingDeclDepth > 0 && "empty ParsingDeclaration stack");
+  ParsingDeclDepth--;
+
+  if (DelayedDiagnostics.empty())
+    return;
+
+  unsigned SavedIndex = (unsigned) S;
+  assert(SavedIndex <= DelayedDiagnostics.size() &&
+         "saved index is out of bounds");
+
+  // 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;
+      }
+    }
+  }
+
+  DelayedDiagnostics.set_size(SavedIndex);
 }
 
 static bool isDeclDeprecated(Decl *D) {
@@ -2078,37 +2118,20 @@
   return false;
 }
 
-void Sema::PopParsingDeclaration(ParsingDeclStackState S, DeclPtrTy Ctx) {
-  assert(ParsingDeclDepth > 0 && "empty ParsingDeclaration stack");
-  ParsingDeclDepth--;
-
-  if (DelayedDeprecationWarnings.empty())
+void Sema::HandleDelayedDeprecationCheck(Sema::DelayedDiagnostic &DD,
+                                         Decl *Ctx) {
+  if (isDeclDeprecated(Ctx))
     return;
 
-  unsigned SavedIndex = (unsigned) S;
-  assert(SavedIndex <= DelayedDeprecationWarnings.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;
-      }
-    }
-  }
-
-  DelayedDeprecationWarnings.set_size(SavedIndex);
+  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;
   }