Просмотр исходного кода

[analyzer] Add a checker option to detect nested dead stores

Enables the users to specify an optional flag which would warn for more dead
stores.
Previously it ignored if the dead store happened e.g. in an if condition.

if ((X = generate())) { // dead store to X
}

This patch introduces the `WarnForDeadNestedAssignments` option to the checker,
which is `false` by default - so this change would not affect any previous
users.
I have updated the code, tests and the docs as well. If I missed something, tell
me.

I also ran the analysis on Clang which generated 14 more reports compared to the
unmodified version. All of them seemed reasonable for me.

Related previous patches:
rGf224820b45c6847b91071da8d7ade59f373b96f3

Reviewers: NoQ, krememek, Szelethus, baloghadamsoftware

Reviewed By: Szelethus

Patch by Balázs Benics!

Differential Revision: https://reviews.llvm.org/D66733

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@370767 91177308-0d34-0410-b5e6-96231b3b80d8
Kristof Umann 6 лет назад
Родитель
Сommit
cfb041434e

+ 9 - 0
docs/analyzer/checkers.rst

@@ -319,6 +319,15 @@ Check for values stored to variables that are never read afterwards.
    x = 1; // warn
    x = 1; // warn
  }
  }
 
 
+The ``WarnForDeadNestedAssignments`` option enables the checker to emit
+warnings for nested dead assignments. You can disable with the
+``-analyzer-config deadcode.DeadStores:WarnForDeadNestedAssignments=false``.
+*Defaults to true*.
+
+Would warn for this e.g.:
+if ((y = make_int())) {
+}
+
 .. _nullability-checkers:
 .. _nullability-checkers:
 
 
 nullability
 nullability

+ 8 - 0
include/clang/StaticAnalyzer/Checkers/Checkers.td

@@ -648,6 +648,14 @@ let ParentPackage = DeadCode in {
 def DeadStoresChecker : Checker<"DeadStores">,
 def DeadStoresChecker : Checker<"DeadStores">,
   HelpText<"Check for values stored to variables that are never read "
   HelpText<"Check for values stored to variables that are never read "
            "afterwards">,
            "afterwards">,
+  CheckerOptions<[
+    CmdLineOption<Boolean,
+                  "WarnForDeadNestedAssignments",
+                  "Warns for deadstores in nested assignments."
+                  "E.g.: if ((P = f())) where P is unused.",
+                  "true",
+                  Released>
+  ]>,
   Documentation<HasDocumentation>;
   Documentation<HasDocumentation>;
 
 
 } // end DeadCode
 } // end DeadCode

+ 24 - 9
lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp

@@ -130,6 +130,7 @@ class DeadStoreObs : public LiveVariables::Observer {
   std::unique_ptr<ReachableCode> reachableCode;
   std::unique_ptr<ReachableCode> reachableCode;
   const CFGBlock *currentBlock;
   const CFGBlock *currentBlock;
   std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
   std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
+  const bool WarnForDeadNestedAssignments;
 
 
   enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
   enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
 
 
@@ -137,9 +138,11 @@ public:
   DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
   DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
                const CheckerBase *checker, AnalysisDeclContext *ac,
                const CheckerBase *checker, AnalysisDeclContext *ac,
                ParentMap &parents,
                ParentMap &parents,
-               llvm::SmallPtrSet<const VarDecl *, 20> &escaped)
+               llvm::SmallPtrSet<const VarDecl *, 20> &escaped,
+               bool warnForDeadNestedAssignments)
       : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
       : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
-        Escaped(escaped), currentBlock(nullptr) {}
+        Escaped(escaped), currentBlock(nullptr),
+        WarnForDeadNestedAssignments(warnForDeadNestedAssignments) {}
 
 
   ~DeadStoreObs() override {}
   ~DeadStoreObs() override {}
 
 
@@ -217,11 +220,16 @@ public:
         os << "Value stored to '" << *V << "' is never read";
         os << "Value stored to '" << *V << "' is never read";
         break;
         break;
 
 
+      // eg.: f((x = foo()))
       case Enclosing:
       case Enclosing:
-        // Don't report issues in this case, e.g.: "if (x = foo())",
-        // where 'x' is unused later.  We have yet to see a case where
-        // this is a real bug.
-        return;
+        if (!WarnForDeadNestedAssignments)
+          return;
+        BugType = "Dead nested assignment";
+        os << "Although the value stored to '" << *V
+           << "' is used in the enclosing expression, the value is never "
+              "actually read from '"
+           << *V << "'";
+        break;
     }
     }
 
 
     BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
     BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
@@ -474,6 +482,8 @@ public:
 namespace {
 namespace {
 class DeadStoresChecker : public Checker<check::ASTCodeBody> {
 class DeadStoresChecker : public Checker<check::ASTCodeBody> {
 public:
 public:
+  bool WarnForDeadNestedAssignments = true;
+
   void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
   void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
                         BugReporter &BR) const {
                         BugReporter &BR) const {
 
 
@@ -491,15 +501,20 @@ public:
       ParentMap &pmap = mgr.getParentMap(D);
       ParentMap &pmap = mgr.getParentMap(D);
       FindEscaped FS;
       FindEscaped FS;
       cfg.VisitBlockStmts(FS);
       cfg.VisitBlockStmts(FS);
-      DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped);
+      DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped,
+                     WarnForDeadNestedAssignments);
       L->runOnAllBlocks(A);
       L->runOnAllBlocks(A);
     }
     }
   }
   }
 };
 };
 }
 }
 
 
-void ento::registerDeadStoresChecker(CheckerManager &mgr) {
-  mgr.registerChecker<DeadStoresChecker>();
+void ento::registerDeadStoresChecker(CheckerManager &Mgr) {
+  auto Chk = Mgr.registerChecker<DeadStoresChecker>();
+
+  const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
+  Chk->WarnForDeadNestedAssignments =
+      AnOpts.getCheckerBooleanOption(Chk, "WarnForDeadNestedAssignments");
 }
 }
 
 
 bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) {
 bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) {

+ 2 - 1
test/Analysis/analyzer-config.c

@@ -30,6 +30,7 @@
 // CHECK-NEXT: ctu-dir = ""
 // CHECK-NEXT: ctu-dir = ""
 // CHECK-NEXT: ctu-import-threshold = 100
 // CHECK-NEXT: ctu-import-threshold = 100
 // CHECK-NEXT: ctu-index-name = externalDefMap.txt
 // CHECK-NEXT: ctu-index-name = externalDefMap.txt
+// CHECK-NEXT: deadcode.DeadStores:WarnForDeadNestedAssignments = true
 // CHECK-NEXT: debug.AnalysisOrder:* = false
 // CHECK-NEXT: debug.AnalysisOrder:* = false
 // CHECK-NEXT: debug.AnalysisOrder:Bind = false
 // CHECK-NEXT: debug.AnalysisOrder:Bind = false
 // CHECK-NEXT: debug.AnalysisOrder:EndFunction = false
 // CHECK-NEXT: debug.AnalysisOrder:EndFunction = false
@@ -93,4 +94,4 @@
 // CHECK-NEXT: unroll-loops = false
 // CHECK-NEXT: unroll-loops = false
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 90
+// CHECK-NEXT: num-entries = 91

+ 173 - 124
test/Analysis/dead-stores.c

@@ -1,102 +1,110 @@
-// RUN: %clang_analyze_cc1 -Wunused-variable -analyzer-checker=core,deadcode.DeadStores -fblocks -verify -Wno-unreachable-code -analyzer-opt-analyze-nested-blocks %s
-// RUN: %clang_analyze_cc1 -Wunused-variable -analyzer-checker=core,deadcode.DeadStores -analyzer-store=region -fblocks -verify -Wno-unreachable-code -analyzer-opt-analyze-nested-blocks %s
+// RUN: %clang_analyze_cc1 -Wunused-variable -fblocks -Wno-unreachable-code     \
+// RUN:  -analyzer-checker=core,deadcode.DeadStores                             \
+// RUN:  -analyzer-config deadcode.DeadStores:WarnForDeadNestedAssignments=false\
+// RUN:  -analyzer-opt-analyze-nested-blocks -verify=non-nested %s
+//
+// RUN: %clang_analyze_cc1 -Wunused-variable -fblocks -Wno-unreachable-code     \
+// RUN:  -analyzer-checker=core,deadcode.DeadStores                             \
+// RUN:  -analyzer-config deadcode.DeadStores:WarnForDeadNestedAssignments=false\
+// RUN:  -analyzer-opt-analyze-nested-blocks -verify=non-nested                 \
+// RUN:  -analyzer-store=region %s
+//
+// RUN: %clang_analyze_cc1 -Wunused-variable -fblocks -Wno-unreachable-code     \
+// RUN:  -analyzer-checker=core,deadcode.DeadStores                             \
+// RUN:  -analyzer-opt-analyze-nested-blocks -verify=non-nested,nested %s
 
 
 void f1() {
 void f1() {
-  int k, y; // expected-warning{{unused variable 'k'}} expected-warning{{unused variable 'y'}}
-  int abc=1;
-  long idx=abc+3*5; // expected-warning {{never read}} expected-warning{{unused variable 'idx'}}
+  int k, y; // non-nested-warning {{unused variable 'k'}}
+            // non-nested-warning@-1 {{unused variable 'y'}}
+  int abc = 1;
+  long idx = abc + 3 * 5; // non-nested-warning {{never read}}
+                          // non-nested-warning@-1 {{unused variable 'idx'}}
 }
 }
 
 
 void f2(void *b) {
 void f2(void *b) {
- char *c = (char*)b; // no-warning
- char *d = b+1; // expected-warning {{never read}} expected-warning{{unused variable 'd'}}
- printf("%s", c); // expected-warning{{implicitly declaring library function 'printf' with type 'int (const char *, ...)'}} \
- // expected-note{{include the header <stdio.h> or explicitly provide a declaration for 'printf'}}
+  char *c = (char *)b; // no-warning
+  char *d = b + 1;     // non-nested-warning {{never read}}
+                       // non-nested-warning@-1 {{unused variable 'd'}}
+  printf("%s", c);
+  // non-nested-warning@-1 {{implicitly declaring library function 'printf' with type 'int (const char *, ...)'}}
+  // non-nested-note@-2 {{include the header <stdio.h> or explicitly provide a declaration for 'printf'}}
 }
 }
 
 
 int f();
 int f();
-
 void f3() {
 void f3() {
   int r;
   int r;
   if ((r = f()) != 0) { // no-warning
   if ((r = f()) != 0) { // no-warning
-    int y = r; // no-warning
+    int y = r;          // no-warning
     printf("the error is: %d\n", y);
     printf("the error is: %d\n", y);
   }
   }
 }
 }
 
 
 void f4(int k) {
 void f4(int k) {
-  
   k = 1;
   k = 1;
-  
   if (k)
   if (k)
     f1();
     f1();
-    
-  k = 2;  // expected-warning {{never read}}
+  k = 2; // non-nested-warning {{never read}}
 }
 }
-  
-void f5() {
-
-  int x = 4; // no-warning
-  int *p = &x; // expected-warning{{never read}} expected-warning{{unused variable 'p'}}
 
 
+void f5() {
+  int x = 4;   // no-warning
+  int *p = &x; // non-nested-warning {{never read}}
+               // non-nested-warning@-1 {{unused variable 'p'}}
 }
 }
 
 
-//
 int f6() {
 int f6() {
-  
   int x = 4;
   int x = 4;
   ++x; // no-warning
   ++x; // no-warning
   return 1;
   return 1;
 }
 }
 
 
-int f7(int *p) {  
+int f7(int *p) {
   // This is allowed for defensive programming.
   // This is allowed for defensive programming.
-  p = 0; // no-warning  
+  p = 0; // no-warning
   return 1;
   return 1;
 }
 }
 
 
-int f7b(int *p) {  
+int f7b(int *p) {
   // This is allowed for defensive programming.
   // This is allowed for defensive programming.
-  p = (0); // no-warning  
+  p = (0); // no-warning
   return 1;
   return 1;
 }
 }
 
 
-int f7c(int *p) {  
+int f7c(int *p) {
   // This is allowed for defensive programming.
   // This is allowed for defensive programming.
-  p = (void*) 0; // no-warning  
+  p = (void *)0; // no-warning
   return 1;
   return 1;
 }
 }
 
 
-int f7d(int *p) {  
+int f7d(int *p) {
   // This is allowed for defensive programming.
   // This is allowed for defensive programming.
-  p = (void*) (0); // no-warning  
+  p = (void *)(0); // no-warning
   return 1;
   return 1;
 }
 }
 
 
-// Don't warn for dead stores in nested expressions.  We have yet
-// to see a real bug in this scenario.
+// Warn for dead stores in nested expressions.
 int f8(int *p) {
 int f8(int *p) {
   extern int *baz();
   extern int *baz();
-  if ((p = baz())) // no-warning
+  if ((p = baz())) // nested-warning {{Although the value stored}}
     return 1;
     return 1;
   return 0;
   return 0;
 }
 }
 
 
 int f9() {
 int f9() {
   int x = 4;
   int x = 4;
-  x = x + 10; // expected-warning{{never read}}
+  x = x + 10; // non-nested-warning {{never read}}
   return 1;
   return 1;
 }
 }
 
 
 int f10() {
 int f10() {
   int x = 4;
   int x = 4;
-  x = 10 + x; // expected-warning{{never read}}
+  x = 10 + x; // non-nested-warning {{never read}}
   return 1;
   return 1;
 }
 }
 
 
 int f11() {
 int f11() {
   int x = 4;
   int x = 4;
-  return x++; // expected-warning{{never read}}
+  return x++; // non-nested-warning {{never read}}
 }
 }
 
 
 int f11b() {
 int f11b() {
@@ -105,38 +113,38 @@ int f11b() {
 }
 }
 
 
 int f12a(int y) {
 int f12a(int y) {
-  int x = y;  // expected-warning{{unused variable 'x'}}
+  int x = y; // non-nested-warning {{unused variable 'x'}}
   return 1;
   return 1;
 }
 }
+
 int f12b(int y) {
 int f12b(int y) {
-  int x __attribute__((unused)) = y;  // no-warning
+  int x __attribute__((unused)) = y; // no-warning
   return 1;
   return 1;
 }
 }
+
 int f12c(int y) {
 int f12c(int y) {
   // Allow initialiation of scalar variables by parameters as a form of
   // Allow initialiation of scalar variables by parameters as a form of
   // defensive programming.
   // defensive programming.
-  int x = y;  // no-warning
+  int x = y; // no-warning
   x = 1;
   x = 1;
   return x;
   return x;
 }
 }
 
 
 // Filed with PR 2630.  This code should produce no warnings.
 // Filed with PR 2630.  This code should produce no warnings.
-int f13(void)
-{
+int f13(void) {
   int a = 1;
   int a = 1;
   int b, c = b = a + a;
   int b, c = b = a + a;
 
 
   if (b > 0)
   if (b > 0)
     return (0);
     return (0);
-
   return (a + b + c);
   return (a + b + c);
 }
 }
 
 
 // Filed with PR 2763.
 // Filed with PR 2763.
 int f14(int count) {
 int f14(int count) {
   int index, nextLineIndex;
   int index, nextLineIndex;
-  for (index = 0; index < count; index = nextLineIndex+1) {
-    nextLineIndex = index+1;  // no-warning
+  for (index = 0; index < count; index = nextLineIndex + 1) {
+    nextLineIndex = index + 1; // no-warning
     continue;
     continue;
   }
   }
   return index;
   return index;
@@ -144,16 +152,15 @@ int f14(int count) {
 
 
 // Test case for <rdar://problem/6248086>
 // Test case for <rdar://problem/6248086>
 void f15(unsigned x, unsigned y) {
 void f15(unsigned x, unsigned y) {
-  int count = x * y;   // no-warning
-  int z[count]; // expected-warning{{unused variable 'z'}}
+  int count = x * y; // no-warning
+  int z[count];      // non-nested-warning {{unused variable 'z'}}
 }
 }
 
 
-// Don't warn for dead stores in nested expressions.  We have yet
-// to see a real bug in this scenario.
+// Warn for dead stores in nested expressions.
 int f16(int x) {
 int f16(int x) {
   x = x * 2;
   x = x * 2;
-  x = sizeof(int [x = (x || x + 1) * 2])
-      ? 5 : 8;
+  x = sizeof(int[x = (x || x + 1) * 2]) ? 5 : 8;
+  // nested-warning@-1 {{Although the value stored}}
   return x;
   return x;
 }
 }
 
 
@@ -168,39 +175,39 @@ void f17() {
 // what that value is actually used.  In other words, don't say "Although the
 // what that value is actually used.  In other words, don't say "Although the
 // value stored to 'x' is used...".
 // value stored to 'x' is used...".
 int f18() {
 int f18() {
-   int x = 0; // no-warning
-   if (1)
-      x = 10;  // expected-warning{{Value stored to 'x' is never read}}
-   while (1)
-      x = 10;  // expected-warning{{Value stored to 'x' is never read}}
-   // unreachable.
-   do
-      x = 10;   // no-warning
-   while (1);
-   return (x = 10); // no-warning
+  int x = 0; // no-warning
+  if (1)
+    x = 10; // non-nested-warning {{Value stored to 'x' is never read}}
+  while (1)
+    x = 10; // non-nested-warning {{Value stored to 'x' is never read}}
+  // unreachable.
+  do
+    x = 10; // no-warning
+  while (1);
+  return (x = 10); // no-warning
 }
 }
 
 
 int f18_a() {
 int f18_a() {
-   int x = 0; // no-warning
-   return (x = 10); // no-warning
+  int x = 0;       // no-warning
+  return (x = 10); // nested-warning {{Although the value stored}}
 }
 }
 
 
 void f18_b() {
 void f18_b() {
-   int x = 0; // no-warning
-   if (1)
-      x = 10;  // expected-warning{{Value stored to 'x' is never read}}
+  int x = 0; // no-warning
+  if (1)
+    x = 10; // non-nested-warning {{Value stored to 'x' is never read}}
 }
 }
 
 
 void f18_c() {
 void f18_c() {
   int x = 0;
   int x = 0;
   while (1)
   while (1)
-     x = 10;  // expected-warning{{Value stored to 'x' is never read}}
+    x = 10; // non-nested-warning {{Value stored to 'x' is never read}}
 }
 }
 
 
 void f18_d() {
 void f18_d() {
   int x = 0; // no-warning
   int x = 0; // no-warning
   do
   do
-     x = 10;   // expected-warning{{Value stored to 'x' is never read}}
+    x = 10; // non-nested-warning {{Value stored to 'x' is never read}}
   while (1);
   while (1);
 }
 }
 
 
@@ -208,7 +215,7 @@ void f18_d() {
 //  http://llvm.org/bugs/show_bug.cgi?id=3514
 //  http://llvm.org/bugs/show_bug.cgi?id=3514
 extern const int MyConstant;
 extern const int MyConstant;
 int f19(void) {
 int f19(void) {
-  int x = MyConstant;  // no-warning
+  int x = MyConstant; // no-warning
   x = 1;
   x = 1;
   return x;
   return x;
 }
 }
@@ -217,7 +224,7 @@ int f19b(void) { // This case is the same as f19.
   const int MyConstant = 0;
   const int MyConstant = 0;
   int x = MyConstant; // no-warning
   int x = MyConstant; // no-warning
   x = 1;
   x = 1;
-  return x;  
+  return x;
 }
 }
 
 
 void f20(void) {
 void f20(void) {
@@ -228,8 +235,7 @@ void f20(void) {
 void halt() __attribute__((noreturn));
 void halt() __attribute__((noreturn));
 int f21() {
 int f21() {
   int x = 4;
   int x = 4;
-  
-  x = x + 1; // expected-warning{{never read}}
+  x = x + 1; // non-nested-warning {{never read}}
   if (1) {
   if (1) {
     halt();
     halt();
     (void)x;
     (void)x;
@@ -261,7 +267,7 @@ void f22() {
   int y19 = 4;
   int y19 = 4;
   int y20 = 4;
   int y20 = 4;
 
 
-  x = x + 1; // expected-warning{{never read}}
+  x = x + 1; // non-nested-warning {{never read}}
   ++y1;
   ++y1;
   ++y2;
   ++y2;
   ++y3;
   ++y3;
@@ -309,13 +315,13 @@ void f22() {
     } else
     } else
       (void)x;
       (void)x;
     (void)x;
     (void)x;
-  break;
+    break;
   case 4:
   case 4:
-    0 ? : ((void)y4, ({ return; }));
+    0 ?: ((void)y4, ({ return; }));
     (void)x;
     (void)x;
     break;
     break;
   case 5:
   case 5:
-    1 ? : (void)x;
+    1 ?: (void)x;
     0 ? (void)x : ((void)y5, ({ return; }));
     0 ? (void)x : ((void)y5, ({ return; }));
     (void)x;
     (void)x;
     break;
     break;
@@ -326,11 +332,13 @@ void f22() {
   case 7:
   case 7:
     (void)(0 && x);
     (void)(0 && x);
     (void)y7;
     (void)y7;
-    (void)(0 || (y8, ({ return; }), 1));  // expected-warning {{expression result unused}}
+    (void)(0 || (y8, ({ return; }), 1));
+    // non-nested-warning@-1 {{expression result unused}}
     (void)x;
     (void)x;
     break;
     break;
   case 8:
   case 8:
-    (void)(1 && (y9, ({ return; }), 1));  // expected-warning {{expression result unused}}
+    (void)(1 && (y9, ({ return; }), 1));
+    // non-nested-warning@-1 {{expression result unused}}
     (void)x;
     (void)x;
     break;
     break;
   case 9:
   case 9:
@@ -365,16 +373,16 @@ void f22() {
     for (;;) {
     for (;;) {
       (void)y16;
       (void)y16;
     }
     }
-    (void)x;    
+    (void)x;
     break;
     break;
   case 15:
   case 15:
-    for (;1;) {
+    for (; 1;) {
       (void)y17;
       (void)y17;
     }
     }
     (void)x;
     (void)x;
     break;
     break;
   case 16:
   case 16:
-    for (;0;) {
+    for (; 0;) {
       (void)x;
       (void)x;
     }
     }
     (void)y18;
     (void)y18;
@@ -390,28 +398,34 @@ void f22() {
   }
   }
 }
 }
 
 
-void f23_aux(const char* s);
+void f23_aux(const char *s);
 void f23(int argc, char **argv) {
 void f23(int argc, char **argv) {
   int shouldLog = (argc > 1); // no-warning
   int shouldLog = (argc > 1); // no-warning
-  ^{ 
-     if (shouldLog) f23_aux("I did too use it!\n");
-     else f23_aux("I shouldn't log.  Wait.. d'oh!\n");
+  ^{
+    if (shouldLog)
+      f23_aux("I did too use it!\n");
+    else
+      f23_aux("I shouldn't log.  Wait.. d'oh!\n");
   }();
   }();
 }
 }
 
 
 void f23_pos(int argc, char **argv) {
 void f23_pos(int argc, char **argv) {
-  int shouldLog = (argc > 1); // expected-warning{{Value stored to 'shouldLog' during its initialization is never read}} expected-warning{{unused variable 'shouldLog'}}
-  ^{ 
-     f23_aux("I did too use it!\n");
-  }();  
+  int shouldLog = (argc > 1);
+  // non-nested-warning@-1 {{Value stored to 'shouldLog' during its initialization is never read}}
+  // non-nested-warning@-2 {{unused variable 'shouldLog'}}
+  ^{
+    f23_aux("I did too use it!\n");
+  }();
 }
 }
 
 
 void f24_A(int y) {
 void f24_A(int y) {
   // FIXME: One day this should be reported as dead since 'z = x + y' is dead.
   // FIXME: One day this should be reported as dead since 'z = x + y' is dead.
   int x = (y > 2); // no-warning
   int x = (y > 2); // no-warning
-  ^ {
-      int z = x + y; // expected-warning{{Value stored to 'z' during its initialization is never read}} expected-warning{{unused variable 'z'}}
-  }();  
+  ^{
+    int z = x + y;
+    // non-nested-warning@-1 {{Value stored to 'z' during its initialization is never read}}
+    // non-nested-warning@-2 {{unused variable 'z'}}
+  }();
 }
 }
 
 
 void f24_B(int y) {
 void f24_B(int y) {
@@ -426,7 +440,7 @@ void f24_B(int y) {
 int f24_C(int y) {
 int f24_C(int y) {
   // FIXME: One day this should be reported as dead since 'x' is just overwritten.
   // FIXME: One day this should be reported as dead since 'x' is just overwritten.
   __block int x = (y > 2); // no-warning
   __block int x = (y > 2); // no-warning
-  ^{ 
+  ^{
     x = 5; // no-warning
     x = 5; // no-warning
   }();
   }();
   return x;
   return x;
@@ -434,32 +448,35 @@ int f24_C(int y) {
 
 
 int f24_D(int y) {
 int f24_D(int y) {
   __block int x = (y > 2); // no-warning
   __block int x = (y > 2); // no-warning
-  ^{ 
+  ^{
     if (y > 4)
     if (y > 4)
       x = 5; // no-warning
       x = 5; // no-warning
   }();
   }();
   return x;
   return x;
 }
 }
 
 
-// This example shows that writing to a variable captured by a block means that it might
-// not be dead.
+// This example shows that writing to a variable captured by a block means that
+// it might not be dead.
 int f25(int y) {
 int f25(int y) {
   __block int x = (y > 2);
   __block int x = (y > 2);
   __block int z = 0;
   __block int z = 0;
-  void (^foo)() = ^{ z = x + y; };
+  void (^foo)() = ^{
+    z = x + y;
+  };
   x = 4; // no-warning
   x = 4; // no-warning
   foo();
   foo();
-  return z; 
+  return z;
 }
 }
 
 
-// This test is mostly the same as 'f25', but shows that the heuristic of pruning out dead
-// stores for variables that are just marked '__block' is overly conservative.
+// This test is mostly the same as 'f25', but shows that the heuristic of
+// pruning out dead stores for variables that are just marked '__block' is
+// overly conservative.
 int f25_b(int y) {
 int f25_b(int y) {
   // FIXME: we should eventually report a dead store here.
   // FIXME: we should eventually report a dead store here.
   __block int x = (y > 2);
   __block int x = (y > 2);
   __block int z = 0;
   __block int z = 0;
   x = 4; // no-warning
   x = 4; // no-warning
-  return z; 
+  return z;
 }
 }
 
 
 int f26_nestedblocks() {
 int f26_nestedblocks() {
@@ -468,10 +485,10 @@ int f26_nestedblocks() {
   __block int y = 0;
   __block int y = 0;
   ^{
   ^{
     int k;
     int k;
-    k = 1; // expected-warning{{Value stored to 'k' is never read}}
+    k = 1; // non-nested-warning {{Value stored to 'k' is never read}}
     ^{
     ^{
-        y = z + 1;
-     }();
+      y = z + 1;
+    }();
   }();
   }();
   return y;
   return y;
 }
 }
@@ -480,11 +497,13 @@ int f26_nestedblocks() {
 // placed within the increment code of for loops.
 // placed within the increment code of for loops.
 void rdar8014335() {
 void rdar8014335() {
   for (int i = 0 ; i != 10 ; ({ break; })) {
   for (int i = 0 ; i != 10 ; ({ break; })) {
-    for ( ; ; ({ ++i; break; })) ; // expected-warning {{'break' is bound to current loop, GCC binds it to the enclosing loop}}
+    for (;; ({ ++i; break; }))
+      ;
+    // non-nested-warning@-2 {{'break' is bound to current loop, GCC binds it to the enclosing loop}}
     // Note that the next value stored to 'i' is never executed
     // Note that the next value stored to 'i' is never executed
     // because the next statement to be executed is the 'break'
     // because the next statement to be executed is the 'break'
     // in the increment code of the first loop.
     // in the increment code of the first loop.
-    i = i * 3; // expected-warning{{Value stored to 'i' is never read}}
+    i = i * 3; // non-nested-warning {{Value stored to 'i' is never read}}
   }
   }
 }
 }
 
 
@@ -517,10 +536,8 @@ void rdar8405222_aux(int i);
 void rdar8405222() {
 void rdar8405222() {
   const int show = 0;
   const int show = 0;
   int i = 0;
   int i = 0;
-    
   if (show)
   if (show)
-      i = 5; // no-warning
-
+    i = 5; // no-warning
   if (show)
   if (show)
     rdar8405222_aux(i);
     rdar8405222_aux(i);
 }
 }
@@ -529,13 +546,13 @@ void rdar8405222() {
 // silencing heuristics.
 // silencing heuristics.
 int radar11185138_foo() {
 int radar11185138_foo() {
   int x, y;
   int x, y;
-  x = y = 0; // expected-warning {{never read}}
+  x = y = 0; // non-nested-warning {{never read}}
   return y;
   return y;
 }
 }
 
 
 int rdar11185138_bar() {
 int rdar11185138_bar() {
   int y;
   int y;
-  int x = y = 0; // no-warning
+  int x = y = 0; // nested-warning {{Although the value stored}}
   x = 2;
   x = 2;
   y = 2;
   y = 2;
   return x + y;
   return x + y;
@@ -550,26 +567,58 @@ int *radar11185138_baz() {
 int getInt();
 int getInt();
 int *getPtr();
 int *getPtr();
 void testBOComma() {
 void testBOComma() {
-  int x0 = (getInt(), 0); // expected-warning{{unused variable 'x0'}}
-  int x1 = (getInt(), getInt()); // expected-warning {{Value stored to 'x1' during its initialization is never read}} // expected-warning{{unused variable 'x1'}}
-  int x2 = (getInt(), getInt(), getInt()); //expected-warning{{Value stored to 'x2' during its initialization is never read}} // expected-warning{{unused variable 'x2'}}
+  int x0 = (getInt(), 0); // non-nested-warning {{unused variable 'x0'}}
+  int x1 = (getInt(), getInt());
+  // non-nested-warning@-1 {{Value stored to 'x1' during its initialization is never read}}
+  // non-nested-warning@-2 {{unused variable 'x1'}}
+
+  int x2 = (getInt(), getInt(), getInt());
+  // non-nested-warning@-1 {{Value stored to 'x2' during its initialization is never read}}
+  // non-nested-warning@-2 {{unused variable 'x2'}}
+
   int x3;
   int x3;
-  x3 = (getInt(), getInt(), 0); // expected-warning{{Value stored to 'x3' is never read}}
-  int x4 = (getInt(), (getInt(), 0)); // expected-warning{{unused variable 'x4'}}
+  x3 = (getInt(), getInt(), 0);
+  // non-nested-warning@-1 {{Value stored to 'x3' is never read}}
+
+  int x4 = (getInt(), (getInt(), 0));
+  // non-nested-warning@-1 {{unused variable 'x4'}}
+
   int y;
   int y;
-  int x5 = (getInt(), (y = 0)); // expected-warning{{unused variable 'x5'}}
-  int x6 = (getInt(), (y = getInt())); //expected-warning {{Value stored to 'x6' during its initialization is never read}} // expected-warning{{unused variable 'x6'}}
-  int x7 = 0, x8 = getInt(); //expected-warning {{Value stored to 'x8' during its initialization is never read}} // expected-warning{{unused variable 'x8'}} // expected-warning{{unused variable 'x7'}}
-  int x9 = getInt(), x10 = 0; //expected-warning {{Value stored to 'x9' during its initialization is never read}} // expected-warning{{unused variable 'x9'}}  // expected-warning{{unused variable 'x10'}}
-  int m = getInt(), mm, mmm; //expected-warning {{Value stored to 'm' during its initialization is never read}} // expected-warning{{unused variable 'm'}} // expected-warning{{unused variable 'mm'}} // expected-warning{{unused variable 'mmm'}}
-  int n, nn = getInt(); //expected-warning {{Value stored to 'nn' during its initialization is never read}} // expected-warning{{unused variable 'n'}} // expected-warning{{unused variable 'nn'}}
+  int x5 = (getInt(), (y = 0));
+  // non-nested-warning@-1 {{unused variable 'x5'}}
+  // nested-warning@-2 {{Although the value stored}}
+
+  int x6 = (getInt(), (y = getInt()));
+  // non-nested-warning@-1 {{Value stored to 'x6' during its initialization is never read}}
+  // non-nested-warning@-2 {{unused variable 'x6'}}
+  // nested-warning@-3 {{Although the value stored}}
+
+  int x7 = 0, x8 = getInt();
+  // non-nested-warning@-1 {{Value stored to 'x8' during its initialization is never read}}
+  // non-nested-warning@-2 {{unused variable 'x8'}}
+  // non-nested-warning@-3 {{unused variable 'x7'}}
+
+  int x9 = getInt(), x10 = 0;
+  // non-nested-warning@-1 {{Value stored to 'x9' during its initialization is never read}}
+  // non-nested-warning@-2 {{unused variable 'x9'}}
+  // non-nested-warning@-3 {{unused variable 'x10'}}
+
+  int m = getInt(), mm, mmm;
+  // non-nested-warning@-1 {{Value stored to 'm' during its initialization is never read}}
+  // non-nested-warning@-2 {{unused variable 'm'}}
+  // non-nested-warning@-3 {{unused variable 'mm'}}
+  // non-nested-warning@-4 {{unused variable 'mmm'}}
+
+  int n, nn = getInt();
+  // non-nested-warning@-1 {{Value stored to 'nn' during its initialization is never read}}
+  // non-nested-warning@-2 {{unused variable 'n'}}
+  // non-nested-warning@-3 {{unused variable 'nn'}}
 
 
   int *p;
   int *p;
   p = (getPtr(), (int *)0); // no warning
   p = (getPtr(), (int *)0); // no warning
-
 }
 }
 
 
 void testVolatile() {
 void testVolatile() {
-    volatile int v;
-    v = 0; // no warning
+  volatile int v;
+  v = 0; // no warning
 }
 }

+ 44 - 29
test/Analysis/dead-stores.cpp

@@ -1,15 +1,26 @@
-// RUN: %clang_analyze_cc1 -fcxx-exceptions -fexceptions -fblocks -std=c++11 -analyzer-checker=deadcode.DeadStores -verify -Wno-unreachable-code %s
-// RUN: %clang_analyze_cc1 -fcxx-exceptions -fexceptions -fblocks -std=c++11 -analyzer-store=region -analyzer-checker=deadcode.DeadStores -verify -Wno-unreachable-code %s
+// RUN: %clang_analyze_cc1 -fcxx-exceptions -fexceptions -fblocks -std=c++11    \
+// RUN:  -analyzer-checker=deadcode.DeadStores -Wno-unreachable-code            \
+// RUN:  -analyzer-config deadcode.DeadStores:WarnForDeadNestedAssignments=false\
+// RUN:  -verify=non-nested %s
+//
+// RUN: %clang_analyze_cc1 -fcxx-exceptions -fexceptions -fblocks -std=c++11    \
+// RUN:  -analyzer-store=region -analyzer-checker=deadcode.DeadStores           \
+// RUN:  -analyzer-config deadcode.DeadStores:WarnForDeadNestedAssignments=false\
+// RUN:  -Wno-unreachable-code -verify=non-nested %s
+//
+// RUN: %clang_analyze_cc1 -fcxx-exceptions -fexceptions -fblocks -std=c++11    \
+// RUN:  -analyzer-checker=deadcode.DeadStores -Wno-unreachable-code            \
+// RUN:  -verify=non-nested,nested %s
 
 
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
 // Basic dead store checking (but in C++ mode).
 // Basic dead store checking (but in C++ mode).
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
 
 
 int j;
 int j;
+int make_int();
 void test1() {
 void test1() {
   int x = 4;
   int x = 4;
-
-  x = x + 1; // expected-warning{{never read}}
+  x = x + 1; // non-nested-warning {{never read}}
 
 
   switch (j) {
   switch (j) {
   case 1:
   case 1:
@@ -17,6 +28,11 @@ void test1() {
     (void)x;
     (void)x;
     break;
     break;
   }
   }
+
+  int y;
+  (void)y;
+  if ((y = make_int())) // nested-warning {{Although the value stored}}
+    return;
 }
 }
 
 
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
@@ -25,6 +41,7 @@ void test1() {
 
 
 class Test2 {
 class Test2 {
   int &x;
   int &x;
+
 public:
 public:
   Test2(int &y) : x(y) {}
   Test2(int &y) : x(y) {}
   ~Test2() { ++x; }
   ~Test2() { ++x; }
@@ -66,17 +83,17 @@ void test2_b() {
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
 
 
 void test3_a(int x) {
 void test3_a(int x) {
-   x = x + 1; // expected-warning{{never read}}
+  x = x + 1; // non-nested-warning {{never read}}
 }
 }
 
 
 void test3_b(int &x) {
 void test3_b(int &x) {
-  x = x + 1; // no-warninge
+  x = x + 1; // no-warning
 }
 }
 
 
 void test3_c(int x) {
 void test3_c(int x) {
   int &y = x;
   int &y = x;
-  // Shows the limitation of dead stores tracking.  The write is really
-  // dead since the value cannot escape the function.
+  // Shows the limitation of dead stores tracking. The write is really dead
+  // since the value cannot escape the function.
   ++y; // no-warning
   ++y; // no-warning
 }
 }
 
 
@@ -94,7 +111,7 @@ void test3_e(int &x) {
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
 
 
 static void test_new(unsigned n) {
 static void test_new(unsigned n) {
-  char **p = new char* [n]; // expected-warning{{never read}}
+  char **p = new char *[n]; // non-nested-warning {{never read}}
 }
 }
 
 
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
@@ -102,11 +119,11 @@ static void test_new(unsigned n) {
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
 
 
 namespace foo {
 namespace foo {
-  int test_4(int x) {
-    x = 2; // expected-warning{{Value stored to 'x' is never read}}
-    x = 2;
-    return x;
-  }
+int test_4(int x) {
+  x = 2; // non-nested-warning {{Value stored to 'x' is never read}}
+  x = 2;
+  return x;
+}
 }
 }
 
 
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
@@ -119,42 +136,39 @@ int test_5() {
   try {
   try {
     x = 2; // no-warning
     x = 2; // no-warning
     test_5_Aux();
     test_5_Aux();
-  }
-  catch (int z) {
+  } catch (int z) {
     return x + z;
     return x + z;
   }
   }
   return 1;
   return 1;
 }
 }
 
 
-
 int test_6_aux(unsigned x);
 int test_6_aux(unsigned x);
-
 void test_6() {
 void test_6() {
-  unsigned currDestLen = 0;  // no-warning
+  unsigned currDestLen = 0; // no-warning
   try {
   try {
     while (test_6_aux(currDestLen)) {
     while (test_6_aux(currDestLen)) {
       currDestLen += 2; // no-warning
       currDestLen += 2; // no-warning
-    } 
+    }
+  } catch (void *) {
   }
   }
-  catch (void *) {}
 }
 }
 
 
 void test_6b() {
 void test_6b() {
-  unsigned currDestLen = 0;  // no-warning
+  unsigned currDestLen = 0; // no-warning
   try {
   try {
     while (test_6_aux(currDestLen)) {
     while (test_6_aux(currDestLen)) {
-      currDestLen += 2; // expected-warning {{Value stored to 'currDestLen' is never read}}
+      currDestLen += 2;
+      // non-nested-warning@-1 {{Value stored to 'currDestLen' is never read}}
       break;
       break;
-    } 
+    }
+  } catch (void *) {
   }
   }
-  catch (void *) {}
 }
 }
 
 
-
 void testCXX11Using() {
 void testCXX11Using() {
   using Int = int;
   using Int = int;
   Int value;
   Int value;
-  value = 1; // expected-warning {{never read}}
+  value = 1; // non-nested-warning {{never read}}
 }
 }
 
 
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
@@ -177,13 +191,14 @@ int radar_13213575() {
 template <class T>
 template <class T>
 void test_block_in_dependent_context(typename T::some_t someArray) {
 void test_block_in_dependent_context(typename T::some_t someArray) {
   ^{
   ^{
-     int i = someArray[0]; // no-warning
+    int i = someArray[0]; // no-warning
   }();
   }();
 }
 }
 
 
 void test_block_in_non_dependent_context(int *someArray) {
 void test_block_in_non_dependent_context(int *someArray) {
   ^{
   ^{
-     int i = someArray[0]; // expected-warning {{Value stored to 'i' during its initialization is never read}}
+    int i = someArray[0];
+    // non-nested-warning@-1 {{Value stored to 'i' during its initialization is never read}}
   }();
   }();
 }
 }
 
 

+ 2 - 2
test/Analysis/dead-stores.m

@@ -1,5 +1,4 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core -analyzer-checker=deadcode.DeadStores,osx.cocoa.RetainCount -fblocks -verify -Wno-objc-root-class %s
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core -analyzer-checker=deadcode.DeadStores,osx.cocoa.RetainCount -fblocks -verify -Wno-objc-root-class %s
-// expected-no-diagnostics
 
 
 typedef signed char BOOL;
 typedef signed char BOOL;
 typedef unsigned int NSUInteger;
 typedef unsigned int NSUInteger;
@@ -72,7 +71,8 @@ void foo_rdar8527823();
 
 
 @implementation Rdar7947686_B
 @implementation Rdar7947686_B
 - (id) init {
 - (id) init {
-  id x = (self = [super init]); // no-warning
+  id x = (self = [super init]);
+  // expected-warning@-1 {{Although the value stored to 'self'}}
   return x;
   return x;
 }
 }
 @end
 @end