r314571 - [Analyzer] Synthesize function body for std::call_once

George Karpenkov via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 29 17:03:22 PDT 2017


Author: george.karpenkov
Date: Fri Sep 29 17:03:22 2017
New Revision: 314571

URL: http://llvm.org/viewvc/llvm-project?rev=314571&view=rev
Log:
[Analyzer] Synthesize function body for std::call_once

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

Added:
    cfe/trunk/test/Analysis/call_once.cpp
Modified:
    cfe/trunk/lib/Analysis/BodyFarm.cpp

Modified: cfe/trunk/lib/Analysis/BodyFarm.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/BodyFarm.cpp?rev=314571&r1=314570&r2=314571&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/BodyFarm.cpp (original)
+++ cfe/trunk/lib/Analysis/BodyFarm.cpp Fri Sep 29 17:03:22 2017
@@ -14,11 +14,18 @@
 
 #include "BodyFarm.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
+#include "clang/AST/NestedNameSpecifier.h"
 #include "clang/Analysis/CodeInjector.h"
+#include "clang/Basic/OperatorKinds.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "body-farm"
 
 using namespace clang;
 
@@ -55,7 +62,9 @@ public:
   CompoundStmt *makeCompound(ArrayRef<Stmt*>);
   
   /// Create a new DeclRefExpr for the referenced variable.
-  DeclRefExpr *makeDeclRefExpr(const VarDecl *D);
+  DeclRefExpr *makeDeclRefExpr(const VarDecl *D,
+                               bool RefersToEnclosingVariableOrCapture = false,
+                               bool GetNonReferenceType = false);
   
   /// Create a new UnaryOperator representing a dereference.
   UnaryOperator *makeDereference(const Expr *Arg, QualType Ty);
@@ -66,9 +75,24 @@ public:
   /// Create an implicit cast to a builtin boolean type.
   ImplicitCastExpr *makeIntegralCastToBoolean(const Expr *Arg);
   
-  // Create an implicit cast for lvalue-to-rvaluate conversions.
+  /// Create an implicit cast for lvalue-to-rvaluate conversions.
   ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty);
   
+  /// Create an implicit cast for lvalue-to-rvaluate conversions.
+  ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg,
+                                       bool GetNonReferenceType = false);
+
+  /// Make RValue out of variable declaration, creating a temporary
+  /// DeclRefExpr in the process.
+  ImplicitCastExpr *
+  makeLvalueToRvalue(const VarDecl *Decl,
+                     bool RefersToEnclosingVariableOrCapture = false,
+                     bool GetNonReferenceType = false);
+
+  /// Create an implicit cast of the given type.
+  ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty,
+                                     CastKind CK = CK_LValueToRValue);
+
   /// Create an Objective-C bool literal.
   ObjCBoolLiteralExpr *makeObjCBool(bool Val);
 
@@ -78,6 +102,18 @@ public:
   /// Create a Return statement.
   ReturnStmt *makeReturn(const Expr *RetVal);
   
+  /// Create an integer literal.
+  IntegerLiteral *makeIntegerLiteral(uint64_t value);
+
+  /// Create a member expression.
+  MemberExpr *makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
+                                   bool IsArrow = false,
+                                   ExprValueKind ValueKind = VK_LValue);
+
+  /// Returns a *first* member field of a record declaration with a given name.
+  /// \return an nullptr if no member with such a name exists.
+  NamedDecl *findMemberField(const CXXRecordDecl *RD, StringRef Name);
+
 private:
   ASTContext &C;
 };
@@ -106,16 +142,16 @@ CompoundStmt *ASTMaker::makeCompound(Arr
   return new (C) CompoundStmt(C, Stmts, SourceLocation(), SourceLocation());
 }
 
-DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) {
-  DeclRefExpr *DR =
-    DeclRefExpr::Create(/* Ctx = */ C,
-                        /* QualifierLoc = */ NestedNameSpecifierLoc(),
-                        /* TemplateKWLoc = */ SourceLocation(),
-                        /* D = */ const_cast<VarDecl*>(D),
-                        /* RefersToEnclosingVariableOrCapture = */ false,
-                        /* NameLoc = */ SourceLocation(),
-                        /* T = */ D->getType(),
-                        /* VK = */ VK_LValue);
+DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D,
+                                       bool RefersToEnclosingVariableOrCapture,
+                                       bool GetNonReferenceType) {
+  auto Type = D->getType();
+  if (GetNonReferenceType)
+    Type = Type.getNonReferenceType();
+
+  DeclRefExpr *DR = DeclRefExpr::Create(
+      C, NestedNameSpecifierLoc(), SourceLocation(), const_cast<VarDecl *>(D),
+      RefersToEnclosingVariableOrCapture, SourceLocation(), Type, VK_LValue);
   return DR;
 }
 
@@ -125,8 +161,38 @@ UnaryOperator *ASTMaker::makeDereference
 }
 
 ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) {
-  return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue,
-                                  const_cast<Expr*>(Arg), nullptr, VK_RValue);
+  return makeImplicitCast(Arg, Ty, CK_LValueToRValue);
+}
+
+ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg,
+                                               bool GetNonReferenceType) {
+
+  QualType Type = Arg->getType();
+  if (GetNonReferenceType)
+    Type = Type.getNonReferenceType();
+  return makeImplicitCast(Arg, Type, CK_LValueToRValue);
+}
+
+ImplicitCastExpr *
+ASTMaker::makeLvalueToRvalue(const VarDecl *Arg,
+                             bool RefersToEnclosingVariableOrCapture,
+                             bool GetNonReferenceType) {
+  auto Type = Arg->getType();
+  if (GetNonReferenceType)
+    Type = Type.getNonReferenceType();
+  return makeLvalueToRvalue(makeDeclRefExpr(Arg,
+                                            RefersToEnclosingVariableOrCapture,
+                                            GetNonReferenceType),
+                            Type);
+}
+
+ImplicitCastExpr *ASTMaker::makeImplicitCast(const Expr *Arg, QualType Ty,
+                                             CastKind CK) {
+  return ImplicitCastExpr::Create(C, Ty,
+                                  /* CastKind= */ CK,
+                                  /* Expr= */ const_cast<Expr *>(Arg),
+                                  /* CXXCastPath= */ nullptr,
+                                  /* ExprValueKind= */ VK_RValue);
 }
 
 Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) {
@@ -161,12 +227,196 @@ ReturnStmt *ASTMaker::makeReturn(const E
                             nullptr);
 }
 
+IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t value) {
+  return IntegerLiteral::Create(C,
+                                llvm::APInt(
+                                    /*numBits=*/C.getTypeSize(C.IntTy), value),
+                                /*QualType=*/C.IntTy, SourceLocation());
+}
+
+MemberExpr *ASTMaker::makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
+                                           bool IsArrow,
+                                           ExprValueKind ValueKind) {
+
+  DeclAccessPair FoundDecl = DeclAccessPair::make(MemberDecl, AS_public);
+  return MemberExpr::Create(
+      C, base, IsArrow, SourceLocation(), NestedNameSpecifierLoc(),
+      SourceLocation(), MemberDecl, FoundDecl,
+      DeclarationNameInfo(MemberDecl->getDeclName(), SourceLocation()),
+      /* TemplateArgumentListInfo= */ nullptr, MemberDecl->getType(), ValueKind,
+      OK_Ordinary);
+}
+
+NamedDecl *ASTMaker::findMemberField(const CXXRecordDecl *RD, StringRef Name) {
+
+  CXXBasePaths Paths(
+      /* FindAmbiguities=*/false,
+      /* RecordPaths=*/false,
+      /* DetectVirtual= */ false);
+  const IdentifierInfo &II = C.Idents.get(Name);
+  DeclarationName DeclName = C.DeclarationNames.getIdentifier(&II);
+
+  DeclContextLookupResult Decls = RD->lookup(DeclName);
+  for (NamedDecl *FoundDecl : Decls)
+    if (!FoundDecl->getDeclContext()->isFunctionOrMethod())
+      return FoundDecl;
+
+  return nullptr;
+}
+
 //===----------------------------------------------------------------------===//
 // Creation functions for faux ASTs.
 //===----------------------------------------------------------------------===//
 
 typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);
 
+static CallExpr *
+create_call_once_funcptr_call(ASTContext &C, ASTMaker M,
+                              const ParmVarDecl *Callback,
+                              SmallVectorImpl<Expr *> &CallArgs) {
+
+  return new (C) CallExpr(
+      /*ASTContext=*/C,
+      /*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Callback),
+      /*args=*/CallArgs,
+      /*QualType=*/C.VoidTy,
+      /*ExprValueType=*/VK_RValue,
+      /*SourceLocation=*/SourceLocation());
+}
+
+static CallExpr *
+create_call_once_lambda_call(ASTContext &C, ASTMaker M,
+                             const ParmVarDecl *Callback, QualType CallbackType,
+                             SmallVectorImpl<Expr *> &CallArgs) {
+
+  CXXRecordDecl *CallbackDecl = CallbackType->getAsCXXRecordDecl();
+
+  assert(CallbackDecl != nullptr);
+  assert(CallbackDecl->isLambda());
+  FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator();
+  assert(callOperatorDecl != nullptr);
+
+  DeclRefExpr *callOperatorDeclRef =
+      DeclRefExpr::Create(/* Ctx = */ C,
+                          /* QualifierLoc = */ NestedNameSpecifierLoc(),
+                          /* TemplateKWLoc = */ SourceLocation(),
+                          const_cast<FunctionDecl *>(callOperatorDecl),
+                          /* RefersToEnclosingVariableOrCapture= */ false,
+                          /* NameLoc = */ SourceLocation(),
+                          /* T = */ callOperatorDecl->getType(),
+                          /* VK = */ VK_LValue);
+
+  CallArgs.insert(
+      CallArgs.begin(),
+      M.makeDeclRefExpr(Callback,
+                        /* RefersToEnclosingVariableOrCapture= */ true,
+                        /* GetNonReferenceType= */ true));
+
+  return new (C)
+      CXXOperatorCallExpr(/*AstContext=*/C, OO_Call, callOperatorDeclRef,
+                          /*args=*/CallArgs,
+                          /*QualType=*/C.VoidTy,
+                          /*ExprValueType=*/VK_RValue,
+                          /*SourceLocation=*/SourceLocation(), FPOptions());
+}
+
+/// Create a fake body for std::call_once.
+/// Emulates the following function body:
+///
+/// \code
+/// typedef struct once_flag_s {
+///   unsigned long __state = 0;
+/// } once_flag;
+/// template<class Callable>
+/// void call_once(once_flag& o, Callable func) {
+///   if (!o.__state) {
+///     func();
+///   }
+///   o.__state = 1;
+/// }
+/// \endcode
+static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
+  DEBUG(llvm::dbgs() << "Generating body for call_once\n");
+
+  // We need at least two parameters.
+  if (D->param_size() < 2)
+    return nullptr;
+
+  ASTMaker M(C);
+
+  const ParmVarDecl *Flag = D->getParamDecl(0);
+  const ParmVarDecl *Callback = D->getParamDecl(1);
+  QualType CallbackType = Callback->getType().getNonReferenceType();
+
+  SmallVector<Expr *, 5> CallArgs;
+
+  // All arguments past first two ones are passed to the callback.
+  for (unsigned int i = 2; i < D->getNumParams(); i++)
+    CallArgs.push_back(M.makeLvalueToRvalue(D->getParamDecl(i)));
+
+  CallExpr *CallbackCall;
+  if (CallbackType->getAsCXXRecordDecl() &&
+      CallbackType->getAsCXXRecordDecl()->isLambda()) {
+
+    CallbackCall =
+        create_call_once_lambda_call(C, M, Callback, CallbackType, CallArgs);
+  } else {
+
+    // Function pointer case.
+    CallbackCall = create_call_once_funcptr_call(C, M, Callback, CallArgs);
+  }
+
+  QualType FlagType = Flag->getType().getNonReferenceType();
+  DeclRefExpr *FlagDecl =
+      M.makeDeclRefExpr(Flag,
+                        /* RefersToEnclosingVariableOrCapture=*/true,
+                        /* GetNonReferenceType=*/true);
+
+  CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl();
+
+  // Note: here we are assuming libc++ implementation of call_once,
+  // which has a struct with a field `__state_`.
+  // Body farming might not work for other `call_once` implementations.
+  NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_");
+  ValueDecl *FieldDecl;
+  if (FoundDecl) {
+    FieldDecl = dyn_cast<ValueDecl>(FoundDecl);
+  } else {
+    DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, "
+                       << "unable to synthesize call_once body, ignoring "
+                       << "the call.\n");
+    return nullptr;
+  }
+
+  MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FieldDecl);
+  assert(Deref->isLValue());
+  QualType DerefType = Deref->getType();
+
+  // Negation predicate.
+  UnaryOperator *FlagCheck = new (C) UnaryOperator(
+      /* input= */
+      M.makeImplicitCast(M.makeLvalueToRvalue(Deref, DerefType), DerefType,
+                         CK_IntegralToBoolean),
+      /* opc= */ UO_LNot,
+      /* QualType= */ C.IntTy,
+      /* ExprValueKind= */ VK_RValue,
+      /* ExprObjectKind= */ OK_Ordinary, SourceLocation());
+
+  // Create assignment.
+  BinaryOperator *FlagAssignment = M.makeAssignment(
+      Deref, M.makeIntegralCast(M.makeIntegerLiteral(1), DerefType), DerefType);
+
+  IfStmt *Out = new (C)
+      IfStmt(C, SourceLocation(),
+             /* IsConstexpr= */ false,
+             /* init= */ nullptr,
+             /* var= */ nullptr,
+             /* cond= */ FlagCheck,
+             /* then= */ M.makeCompound({CallbackCall, FlagAssignment}));
+
+  return Out;
+}
+
 /// Create a fake body for dispatch_once.
 static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
   // Check if we have at least two parameters.
@@ -202,15 +452,17 @@ static Stmt *create_dispatch_once(ASTCon
   ASTMaker M(C);
   
   // (1) Create the call.
-  DeclRefExpr *DR = M.makeDeclRefExpr(Block);
-  ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);
-  CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue,
-                                  SourceLocation());
+  CallExpr *CE = new (C) CallExpr(
+      /*ASTContext=*/C,
+      /*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block),
+      /*args=*/None,
+      /*QualType=*/C.VoidTy,
+      /*ExprValueType=*/VK_RValue,
+      /*SourceLocation=*/SourceLocation());
 
   // (2) Create the assignment to the predicate.
-  IntegerLiteral *IL =
-    IntegerLiteral::Create(C, llvm::APInt(C.getTypeSize(C.IntTy), (uint64_t) 1),
-                           C.IntTy, SourceLocation());
+  IntegerLiteral *IL = M.makeIntegerLiteral(1);
+
   BinaryOperator *B =
     M.makeAssignment(
        M.makeDereference(
@@ -234,13 +486,20 @@ static Stmt *create_dispatch_once(ASTCon
         PredicateTy),
     PredicateTy);
   
-  UnaryOperator *UO = new (C) UnaryOperator(LValToRval, UO_LNot, C.IntTy,
-                                           VK_RValue, OK_Ordinary,
-                                           SourceLocation());
+  UnaryOperator *UO = new (C) UnaryOperator(
+      /* input= */ LValToRval,
+      /* opc= */ UO_LNot,
+      /* QualType= */ C.IntTy,
+      /* ExprValueKind= */ VK_RValue,
+      /* ExprObjectKind= */ OK_Ordinary, SourceLocation());
   
   // (5) Create the 'if' statement.
-  IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr,
-                              UO, CS);
+  IfStmt *If = new (C) IfStmt(C, SourceLocation(),
+                              /* IsConstexpr= */ false,
+                              /* init= */ nullptr,
+                              /* var= */ nullptr,
+                              /* cond= */ UO,
+                              /* then= */ CS);
   return If;
 }
 
@@ -370,8 +629,9 @@ Stmt *BodyFarm::getBody(const FunctionDe
   if (Name.startswith("OSAtomicCompareAndSwap") ||
       Name.startswith("objc_atomicCompareAndSwap")) {
     FF = create_OSAtomicCompareAndSwap;
-  }
-  else {
+  } else if (Name == "call_once" && D->getDeclContext()->isStdNamespace()) {
+    FF = create_call_once;
+  } else {
     FF = llvm::StringSwitch<FunctionFarmer>(Name)
           .Case("dispatch_sync", create_dispatch_sync)
           .Case("dispatch_once", create_dispatch_once)

Added: cfe/trunk/test/Analysis/call_once.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/call_once.cpp?rev=314571&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/call_once.cpp (added)
+++ cfe/trunk/test/Analysis/call_once.cpp Fri Sep 29 17:03:22 2017
@@ -0,0 +1,233 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -w -verify %s
+
+void clang_analyzer_eval(bool);
+
+// Faking std::std::call_once implementation.
+namespace std {
+typedef struct once_flag_s {
+  unsigned long __state_ = 0;
+} once_flag;
+
+template <class Callable, class... Args>
+void call_once(once_flag &o, Callable func, Args... args);
+} // namespace std
+
+// Check with Lambdas.
+void test_called_warning() {
+  std::once_flag g_initialize;
+  int z;
+
+  std::call_once(g_initialize, [&] {
+    int *x = nullptr;
+    int y = *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+    z = 200;
+  });
+}
+
+void test_called_on_path_inside_no_warning() {
+  std::once_flag g_initialize;
+
+  int *x = nullptr;
+  int y = 100;
+  int z;
+
+  std::call_once(g_initialize, [&] {
+    z = 200;
+    x = &z;
+  });
+
+  *x = 100; // no-warning
+  clang_analyzer_eval(z == 100); // expected-warning{{TRUE}}
+}
+
+void test_called_on_path_no_warning() {
+  std::once_flag g_initialize;
+
+  int *x = nullptr;
+  int y = 100;
+
+  std::call_once(g_initialize, [&] {
+    x = &y;
+  });
+
+  *x = 100; // no-warning
+}
+
+void test_called_on_path_warning() {
+  std::once_flag g_initialize;
+
+  int y = 100;
+  int *x = &y;
+
+  std::call_once(g_initialize, [&] {
+    x = nullptr;
+  });
+
+  *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+}
+
+void test_called_once_warning() {
+  std::once_flag g_initialize;
+
+  int *x = nullptr;
+  int y = 100;
+
+  std::call_once(g_initialize, [&] {
+    x = nullptr;
+  });
+
+  std::call_once(g_initialize, [&] {
+    x = &y;
+  });
+
+  *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+}
+
+void test_called_once_no_warning() {
+  std::once_flag g_initialize;
+
+  int *x = nullptr;
+  int y = 100;
+
+  std::call_once(g_initialize, [&] {
+    x = &y;
+  });
+
+  std::call_once(g_initialize, [&] {
+    x = nullptr;
+  });
+
+  *x = 100; // no-warning
+}
+
+static int global = 0;
+void funcPointer() {
+  global = 1;
+}
+
+void test_func_pointers() {
+  static std::once_flag flag;
+  std::call_once(flag, &funcPointer);
+  clang_analyzer_eval(global == 1); // expected-warning{{TRUE}}
+}
+
+template <class _Fp>
+class function; // undefined
+template <class _Rp, class... _ArgTypes>
+struct function<_Rp(_ArgTypes...)> {
+  _Rp operator()(_ArgTypes...) const;
+  template <class _Fp>
+  function(_Fp);
+};
+
+// Note: currently we do not support calls to std::function,
+// but the analyzer should not crash either.
+void test_function_objects_warning() {
+  int x = 0;
+  int *y = &x;
+
+  std::once_flag flag;
+
+  function<void()> func = [&]() {
+    y = nullptr;
+  };
+
+  std::call_once(flag, func);
+
+  func();
+  int z = *y;
+}
+
+void test_param_passing_lambda() {
+  std::once_flag flag;
+  int x = 120;
+  int y = 0;
+
+  std::call_once(flag, [&](int p) {
+    y = p;
+  },
+                 x);
+
+  clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
+}
+
+void test_param_passing_lambda_false() {
+  std::once_flag flag;
+  int x = 120;
+
+  std::call_once(flag, [&](int p) {
+    x = 0;
+  },
+                 x);
+
+  clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
+}
+
+void test_param_passing_stored_lambda() {
+  std::once_flag flag;
+  int x = 120;
+  int y = 0;
+
+  auto lambda = [&](int p) {
+    y = p;
+  };
+
+  std::call_once(flag, lambda, x);
+  clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
+}
+
+void test_multiparam_passing_lambda() {
+  std::once_flag flag;
+  int x = 120;
+
+  std::call_once(flag, [&](int a, int b, int c) {
+    x = a + b + c;
+  },
+                 1, 2, 3);
+
+  clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
+  clang_analyzer_eval(x == 6); // expected-warning{{TRUE}}
+}
+
+static int global2 = 0;
+void test_param_passing_lambda_global() {
+  std::once_flag flag;
+  global2 = 0;
+  std::call_once(flag, [&](int a, int b, int c) {
+    global2 = a + b + c;
+  },
+                 1, 2, 3);
+  clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}}
+}
+
+static int global3 = 0;
+void funcptr(int a, int b, int c) {
+  global3 = a + b + c;
+}
+
+void test_param_passing_funcptr() {
+  std::once_flag flag;
+  global3 = 0;
+
+  std::call_once(flag, &funcptr, 1, 2, 3);
+
+  clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}}
+}
+
+void test_blocks() {
+  global3 = 0;
+  std::once_flag flag;
+  std::call_once(flag, ^{
+    global3 = 120;
+  });
+  clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}}
+}
+
+int call_once() {
+  return 5;
+}
+
+void test_non_std_call_once() {
+  int x = call_once();
+  clang_analyzer_eval(x == 5); // expected-warning{{TRUE}}
+}




More information about the cfe-commits mailing list