]> granicus.if.org Git - clang/commitdiff
Invalidate static locals when escaping lambdas
authorGeorge Karpenkov <ekarpenkov@apple.com>
Fri, 10 Aug 2018 18:28:04 +0000 (18:28 +0000)
committerGeorge Karpenkov <ekarpenkov@apple.com>
Fri, 10 Aug 2018 18:28:04 +0000 (18:28 +0000)
Lambdas can affect static locals even without an explicit capture.

rdar://39537031

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@339459 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Core/RegionStore.cpp
test/Analysis/lambdas.cpp

index db6449e6d5f344cac6741943880c865327b45988..821b7f0edbe853b7f37d5c329a9823f3234de84a 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "clang/AST/Attr.h"
 #include "clang/AST/CharUnits.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/LiveVariables.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Basic/TargetInfo.h"
@@ -1033,6 +1034,32 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
       B = B.remove(baseR);
   }
 
+  if (const auto *TO = dyn_cast<TypedValueRegion>(baseR)) {
+    if (const auto *RD = TO->getValueType()->getAsCXXRecordDecl()) {
+
+      // Lambdas can affect all static local variables without explicitly
+      // capturing those.
+      // We invalidate all static locals referenced inside the lambda body.
+      if (RD->isLambda() && RD->getLambdaCallOperator()->getBody()) {
+        using namespace ast_matchers;
+
+        const char *DeclBind = "DeclBind";
+        StatementMatcher RefToStatic = stmt(hasDescendant(declRefExpr(
+              to(varDecl(hasStaticStorageDuration()).bind(DeclBind)))));
+        auto Matches =
+            match(RefToStatic, *RD->getLambdaCallOperator()->getBody(),
+                  RD->getASTContext());
+
+        for (BoundNodes &Match : Matches) {
+          auto *VD = Match.getNodeAs<VarDecl>(DeclBind);
+          const VarRegion *ToInvalidate =
+              RM.getRegionManager().getVarRegion(VD, LCtx);
+          AddToWorkList(ToInvalidate);
+        }
+      }
+    }
+  }
+
   // BlockDataRegion?  If so, invalidate captured variables that are passed
   // by reference.
   if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(baseR)) {
index 320ba2aabc227f39f54a00ef5d1e9efc76ec073c..fdd1c61164f4c6bbac806f6c87a333f4cbec7e45 100644 (file)
@@ -1,10 +1,26 @@
 // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,deadcode,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -analyzer-config inline-lambdas=false -DNO_INLINING=1 -verify %s
 // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
 // RUN: FileCheck --input-file=%t %s
 
 void clang_analyzer_warnIfReached();
 void clang_analyzer_eval(int);
 
+#ifdef NO_INLINING
+
+// expected-no-diagnostics
+
+int& invalidate_static_on_unknown_lambda() {
+  static int* z;
+  auto f = [] {
+    z = nullptr;
+  }; // should invalidate "z" when inlining is disabled.
+  f();
+  return *z; // no-warning
+}
+
+#else
+
 struct X { X(const X&); };
 void f(X x) { (void) [x]{}; }
 
@@ -348,6 +364,18 @@ void testCapturedConstExprFloat() {
   lambda();
 }
 
+void escape(void*);
+
+int& invalidate_static_on_unknown_lambda() {
+  static int* z;
+  auto lambda = [] {
+    static float zz;
+    z = new int(120);
+  };
+  escape(&lambda);
+  return *z; // no-warning
+}
+
 
 static int b = 0;
 
@@ -365,6 +393,8 @@ int f() {
   return 0;
 }
 
+#endif
+
 // CHECK: [B2 (ENTRY)]
 // CHECK:   Succs (1): B1
 // CHECK: [B1]