Enhance Sema::DiagRuntimeBehavior() to delay some diagnostics to see if the related code is reachable.  This suppresses some
diagnostics that occur in unreachable code (e.g., -Warray-bound).

We only pay the cost of doing the reachability analysis when we issue one of these diagnostics.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126290 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/PartialDiagnostic.h b/include/clang/Basic/PartialDiagnostic.h
index d00195b..c636194 100644
--- a/include/clang/Basic/PartialDiagnostic.h
+++ b/include/clang/Basic/PartialDiagnostic.h
@@ -75,7 +75,7 @@
   /// \brief An allocator for Storage objects, which uses a small cache to 
   /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
   class StorageAllocator {
-    static const unsigned NumCached = 4;
+    static const unsigned NumCached = 16;
     Storage Cached[NumCached];
     Storage *FreeList[NumCached];
     unsigned NumFreeListEntries;
diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h
index b0bb955..51297ae 100644
--- a/include/clang/Sema/ScopeInfo.h
+++ b/include/clang/Sema/ScopeInfo.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_SEMA_SCOPE_INFO_H
 
 #include "clang/AST/Type.h"
+#include "clang/Basic/PartialDiagnostic.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/SetVector.h"
@@ -30,6 +31,17 @@
 
 namespace sema {
 
+class PossiblyUnreachableDiag {
+public:
+  PartialDiagnostic PD;
+  SourceLocation Loc;
+  const Stmt *stmt;
+  
+  PossiblyUnreachableDiag(const PartialDiagnostic &PD, SourceLocation Loc,
+                          const Stmt *stmt)
+    : PD(PD), Loc(Loc), stmt(stmt) {}
+};
+    
 /// \brief Retains information about a function, method, or block that is
 /// currently being parsed.
 class FunctionScopeInfo {
@@ -60,6 +72,11 @@
   /// block, if there is any chance of applying the named return value
   /// optimization.
   llvm::SmallVector<ReturnStmt*, 4> Returns;
+  
+  /// \brief A list of PartialDiagnostics created but delayed within the
+  /// current function scope.  These diagnostics are vetted for reachability
+  /// prior to being emitted.
+  llvm::SmallVector<PossiblyUnreachableDiag, 4> PossiblyUnreachableDiags;
 
   void setHasBranchIntoScope() {
     HasBranchIntoScope = true;
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
index adfa690..6a42224 100644
--- a/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/lib/Sema/AnalysisBasedWarnings.cpp
@@ -15,6 +15,7 @@
 
 #include "clang/Sema/AnalysisBasedWarnings.h"
 #include "clang/Sema/SemaInternal.h"
+#include "clang/Sema/ScopeInfo.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/AST/DeclObjC.h"
@@ -26,6 +27,8 @@
 #include "clang/Analysis/AnalysisContext.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/Analyses/ReachableCode.h"
+#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
+#include "clang/Analysis/CFGStmtMap.h"
 #include "clang/Analysis/Analyses/UninitializedValuesV2.h"
 #include "llvm/ADT/BitVector.h"
 #include "llvm/Support/Casting.h"
@@ -478,6 +481,16 @@
         Diagnostic::Ignored);
 }
 
+static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {
+  for (llvm::SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
+       i = fscope->PossiblyUnreachableDiags.begin(),
+       e = fscope->PossiblyUnreachableDiags.end();
+       i != e; ++i) {
+    const sema::PossiblyUnreachableDiag &D = *i;
+    S.Diag(D.Loc, D.PD);
+  }
+}
+
 void clang::sema::
 AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
                                      sema::FunctionScopeInfo *fscope,
@@ -491,9 +504,6 @@
   //     time.
   Diagnostic &Diags = S.getDiagnostics();
 
-  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
-    return;
-
   // Do not do any analysis for declarations in system headers if we are
   // going to just ignore them.
   if (Diags.getSuppressSystemWarnings() &&
@@ -504,6 +514,12 @@
   if (cast<DeclContext>(D)->isDependentContext())
     return;
 
+  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) {
+    // Flush out any possibly unreachable diagnostics.
+    flushDiagnostics(S, fscope);
+    return;
+  }
+  
   const Stmt *Body = D->getBody();
   assert(Body);
 
@@ -512,6 +528,34 @@
   AnalysisContext AC(D, 0, /*useUnoptimizedCFG=*/false, /*addehedges=*/false,
                      /*addImplicitDtors=*/true, /*addInitializers=*/true);
 
+  // Emit delayed diagnostics.
+  if (!fscope->PossiblyUnreachableDiags.empty()) {
+    bool analyzed = false;
+    if (CFGReachabilityAnalysis *cra = AC.getCFGReachablityAnalysis())
+      if (CFGStmtMap *csm = AC.getCFGStmtMap()) {
+        analyzed = true;
+        for (llvm::SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
+             i = fscope->PossiblyUnreachableDiags.begin(),
+             e = fscope->PossiblyUnreachableDiags.end();
+             i != e; ++i) {
+          const sema::PossiblyUnreachableDiag &D = *i;
+          if (const CFGBlock *blk = csm->getBlock(D.stmt)) {
+            // Can this block be reached from the entrance?
+            if (cra->isReachable(&AC.getCFG()->getEntry(), blk))
+              S.Diag(D.Loc, D.PD);
+          }
+          else {
+            // Emit the warning anyway if we cannot map to a basic block.
+            S.Diag(D.Loc, D.PD);
+          }
+        }
+      }
+
+    if (!analyzed)
+      flushDiagnostics(S, fscope);
+  }
+  
+  
   // Warning: check missing 'return'
   if (P.enableCheckFallThrough) {
     const CheckFallThroughDiagnostics &CD =
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 0827597..0c39e13 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -48,6 +48,7 @@
   SwitchStack.clear();
   Returns.clear();
   ErrorTrap.reset();
+  PossiblyUnreachableDiags.clear();
 }
 
 BlockScopeInfo::~BlockScopeInfo() { }
@@ -639,9 +640,19 @@
   // Issue any analysis-based warnings.
   if (WP && D)
     AnalysisWarnings.IssueWarnings(*WP, Scope, D, blkExpr);
+  else {
+    for (llvm::SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
+         i = Scope->PossiblyUnreachableDiags.begin(),
+         e = Scope->PossiblyUnreachableDiags.end();
+         i != e; ++i) {
+      const sema::PossiblyUnreachableDiag &D = *i;
+      Diag(D.Loc, D.PD);
+    }
+  }
 
-  if (FunctionScopes.back() != Scope)
+  if (FunctionScopes.back() != Scope) {
     delete Scope;
+  }
 }
 
 /// \brief Determine whether any errors occurred within this function/method/
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index ac74791..9e2b21a 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -387,13 +387,13 @@
     return false;
   
   if (Expr->getType()->isObjCObjectType() &&
-      DiagRuntimeBehavior(Expr->getLocStart(), Expr,
+      DiagRuntimeBehavior(Expr->getLocStart(), 0,
         PDiag(diag::err_cannot_pass_objc_interface_to_vararg)
           << Expr->getType() << CT))
     return true;
 
   if (!Expr->getType()->isPODType() &&
-      DiagRuntimeBehavior(Expr->getLocStart(), Expr,
+      DiagRuntimeBehavior(Expr->getLocStart(), 0,
                           PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg)
                             << Expr->getType() << CT))
     return true;
@@ -6721,7 +6721,7 @@
       if (DeclRefExpr* DRR = dyn_cast<DeclRefExpr>(RHSStripped)) {
         if (DRL->getDecl() == DRR->getDecl() &&
             !IsWithinTemplateSpecialization(DRL->getDecl())) {
-          DiagRuntimeBehavior(Loc, lex, PDiag(diag::warn_comparison_always)
+          DiagRuntimeBehavior(Loc, 0, PDiag(diag::warn_comparison_always)
                               << 0 // self-
                               << (Opc == BO_EQ
                                   || Opc == BO_LE
@@ -6743,7 +6743,7 @@
               always_evals_to = 2; // e.g. array1 <= array2
               break;
             }
-            DiagRuntimeBehavior(Loc, lex, PDiag(diag::warn_comparison_always)
+            DiagRuntimeBehavior(Loc, 0, PDiag(diag::warn_comparison_always)
                                 << 1 // array
                                 << always_evals_to);
         }
@@ -6784,7 +6784,7 @@
       default: assert(false && "Invalid comparison operator");
       }
 
-      DiagRuntimeBehavior(Loc, literalString,
+      DiagRuntimeBehavior(Loc, 0,
         PDiag(diag::warn_stringcompare)
           << isa<ObjCEncodeExpr>(literalStringStripped)
           << literalString->getSourceRange());
@@ -7094,7 +7094,7 @@
     if (DeclRefExpr* DRL = dyn_cast<DeclRefExpr>(lex->IgnoreParens()))
       if (DeclRefExpr* DRR = dyn_cast<DeclRefExpr>(rex->IgnoreParens()))
         if (DRL->getDecl() == DRR->getDecl())
-          DiagRuntimeBehavior(Loc, rex,
+          DiagRuntimeBehavior(Loc, 0,
                               PDiag(diag::warn_comparison_always)
                                 << 0 // self-
                                 << 2 // "a constant"
@@ -7355,9 +7355,11 @@
         UO->getSubExpr()->IgnoreParenCasts()->
           isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull) &&
         !UO->getType().isVolatileQualified()) {
-    Diag(UO->getOperatorLoc(), diag::warn_indirection_through_null)
-        << UO->getSubExpr()->getSourceRange();
-    Diag(UO->getOperatorLoc(), diag::note_indirection_through_null);
+    DiagRuntimeBehavior(UO->getOperatorLoc(), UO,
+                        PDiag(diag::warn_indirection_through_null)
+                          << UO->getSubExpr()->getSourceRange());
+    DiagRuntimeBehavior(UO->getOperatorLoc(), UO,
+                        PDiag(diag::note_indirection_through_null));
   }
   
   // Check for trivial buffer overflows.
@@ -9475,7 +9477,13 @@
 
   case PotentiallyEvaluated:
   case PotentiallyEvaluatedIfUsed:
-    Diag(Loc, PD);
+    if (stmt && getCurFunctionOrMethodDecl()) {
+      FunctionScopes.back()->PossiblyUnreachableDiags.
+        push_back(sema::PossiblyUnreachableDiag(PD, Loc, stmt));
+    }
+    else
+      Diag(Loc, PD);
+      
     return true;
 
   case PotentiallyPotentiallyEvaluated:
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 94ba93d..0abd79a 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -146,7 +146,7 @@
     }
   }
 
-  DiagRuntimeBehavior(Loc, S, PDiag(DiagID) << R1 << R2);
+  DiagRuntimeBehavior(Loc, 0, PDiag(DiagID) << R1 << R2);
 }
 
 StmtResult
diff --git a/test/Sema/i-c-e.c b/test/Sema/i-c-e.c
index 4c2962d..d0a6c52 100644
--- a/test/Sema/i-c-e.c
+++ b/test/Sema/i-c-e.c
@@ -60,7 +60,9 @@
 // Pointer + __builtin_constant_p
 char pbcp[__builtin_constant_p(4) ? (intptr_t)&expr : 0]; // expected-error {{variable length array declaration not allowed at file scope}}
 
-int illegaldiv1[1 || 1/0];  // expected-warning {{division by zero is undefined}}
+int illegaldiv1a[1 || 1/0];  // expected-warning {{division by zero is undefined}}
+int illegaldiv1b[1 && 1/0];  // expected-warning {{division by zero is undefined}} expected-error{{variable length array declaration not allowed at file scope}}
+
 int illegaldiv2[1/0]; // expected-error {{variable length array declaration not allowed at file scope}} \
                       // expected-warning {{division by zero is undefined}}
 int illegaldiv3[INT_MIN / -1]; // expected-error {{variable length array declaration not allowed at file scope}}
diff --git a/test/SemaCXX/array-bounds.cpp b/test/SemaCXX/array-bounds.cpp
index 0286c01..ee7882d 100644
--- a/test/SemaCXX/array-bounds.cpp
+++ b/test/SemaCXX/array-bounds.cpp
@@ -63,11 +63,11 @@
 }
 
 template <int I> struct S {
-  char arr[I]; // expected-note 3 {{declared here}}
+  char arr[I]; // expected-note 2 {{declared here}}
 };
 template <int I> void f() {
   S<3> s;
-  s.arr[4] = 0; // expected-warning 2 {{array index of '4' indexes past the end of an array (that contains 3 elements)}}
+  s.arr[4] = 0; // expected-warning {{array index of '4' indexes past the end of an array (that contains 3 elements)}}
   s.arr[I] = 0; // expected-warning {{array index of '5' indexes past the end of an array (that contains 3 elements)}}
 }
 
@@ -79,9 +79,8 @@
 #define ARR_IN_MACRO(flag, arr, idx) flag ? arr[idx] : 1
 
 int test_no_warn_macro_unreachable() {
-  int arr[SIZE]; // expected-note 2 {{array 'arr' declared here}}
-  // FIXME: We don't want to warn for the first case.
-  return ARR_IN_MACRO(0, arr, SIZE) + // expected-warning{{array index of '10' indexes past the end of an array (that contains 10 elements)}}
+  int arr[SIZE]; // expected-note {{array 'arr' declared here}}
+  return ARR_IN_MACRO(0, arr, SIZE) + // no-warning
          ARR_IN_MACRO(1, arr, SIZE); // expected-warning{{array index of '10' indexes past the end of an array (that contains 10 elements)}}
 }
 
@@ -91,3 +90,15 @@
   return array[(unsigned long long) 100]; // expected-warning {{array index of '100' indexes past the end of an array (that contains 100 elements)}}
 }
 
+template <bool extendArray>
+void myFunc() {
+    int arr[3 + (extendArray ? 1 : 0)];
+
+    if (extendArray)
+        arr[3] = 42;
+}
+
+void f() {
+    myFunc<false>();
+}
+