]> granicus.if.org Git - clang/commitdiff
Enhance Sema::DiagRuntimeBehavior() to delay some diagnostics to see if the related...
authorTed Kremenek <kremenek@apple.com>
Wed, 23 Feb 2011 01:52:04 +0000 (01:52 +0000)
committerTed Kremenek <kremenek@apple.com>
Wed, 23 Feb 2011 01:52:04 +0000 (01:52 +0000)
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

include/clang/Basic/PartialDiagnostic.h
include/clang/Sema/ScopeInfo.h
lib/Sema/AnalysisBasedWarnings.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaStmt.cpp
test/Sema/i-c-e.c
test/SemaCXX/array-bounds.cpp

index d00195b326741bb3f0cab6ed02489e73e1492d3e..c63619440551205c295455e3235c527afb4cbd2c 100644 (file)
@@ -75,7 +75,7 @@ public:
   /// \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;
index b0bb95509a91fc77271348d3c3c4eae4002285c3..51297ae40205d0c8664207f015c83a00516e1772 100644 (file)
@@ -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 @@ class SwitchStmt;
 
 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 @@ public:
   /// 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;
index adfa690a643cc4008e66dd1894f0aeb75a494383..6a422242a9d4d9bd69da42d278488545b1ebd9fa 100644 (file)
@@ -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 @@ clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) : S(s) {
         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 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
   //     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 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
   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 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
   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 =
index 0827597abdfbc8487c4fe9daf5f018ad5b55a357..0c39e132539393ebd69534ec28b7df3087043182 100644 (file)
@@ -48,6 +48,7 @@ void FunctionScopeInfo::Clear() {
   SwitchStack.clear();
   Returns.clear();
   ErrorTrap.reset();
+  PossiblyUnreachableDiags.clear();
 }
 
 BlockScopeInfo::~BlockScopeInfo() { }
@@ -639,9 +640,19 @@ void Sema::PopFunctionOrBlockScope(const AnalysisBasedWarnings::Policy *WP,
   // 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/
index ac747910dc747dfdff0dd5e841eca7fb67c54aa2..9e2b21aca213b4521c59479d97c61e030b3bd0b6 100644 (file)
@@ -387,13 +387,13 @@ bool Sema::DefaultVariadicArgumentPromotion(Expr *&Expr, VariadicCallType CT,
     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 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
       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 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
               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 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
       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 @@ QualType Sema::CheckVectorCompareOperands(Expr *&lex, Expr *&rex,
     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 @@ QualType Sema::CheckAssignmentOperands(Expr *LHS, Expr *&RHS,
         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 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *stmt,
 
   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:
index 94ba93d5c2218d903c03619879d057f2fe54d35f..0abd79a696f8e876fc2d43e31113de6738a5cec7 100644 (file)
@@ -146,7 +146,7 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
     }
   }
 
-  DiagRuntimeBehavior(Loc, S, PDiag(DiagID) << R1 << R2);
+  DiagRuntimeBehavior(Loc, 0, PDiag(DiagID) << R1 << R2);
 }
 
 StmtResult
index 4c2962d4b21a894988e791e0c9e749af1874195e..d0a6c529c16006c938326b312a1a8059e8284e38 100644 (file)
@@ -60,7 +60,9 @@ int comma3[(1,2)]; // expected-warning {{size of static array must be an integer
 // 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}}
index 0286c01d85ffddaede0e8ec899cfd81b152159df..ee7882daeaaffa2a8c202f3ff9c3d23ee5c7902a 100644 (file)
@@ -63,11 +63,11 @@ void test() {
 }
 
 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 {{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 @@ void test_templates() {
 #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 @@ int test_pr9240() {
   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>();
+}
+