<div dir="ltr">This revision might be the cause of <a href="https://bugs.llvm.org/show_bug.cgi?id=34869">https://bugs.llvm.org/show_bug.cgi?id=34869</a>. I'm still working on a reduced test case.</div><div class="gmail_extra"><br><div class="gmail_quote">On Sat, Sep 30, 2017 at 2:03 AM, George Karpenkov via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: george.karpenkov<br>
Date: Fri Sep 29 17:03:22 2017<br>
New Revision: 314571<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=314571&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project?rev=314571&view=rev</a><br>
Log:<br>
[Analyzer] Synthesize function body for std::call_once<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D37840" rel="noreferrer" target="_blank">https://reviews.llvm.org/<wbr>D37840</a><br>
<br>
Added:<br>
cfe/trunk/test/Analysis/call_<wbr>once.cpp<br>
Modified:<br>
cfe/trunk/lib/Analysis/<wbr>BodyFarm.cpp<br>
<br>
Modified: cfe/trunk/lib/Analysis/<wbr>BodyFarm.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/BodyFarm.cpp?rev=314571&r1=314570&r2=314571&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/<wbr>Analysis/BodyFarm.cpp?rev=<wbr>314571&r1=314570&r2=314571&<wbr>view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Analysis/<wbr>BodyFarm.cpp (original)<br>
+++ cfe/trunk/lib/Analysis/<wbr>BodyFarm.cpp Fri Sep 29 17:03:22 2017<br>
@@ -14,11 +14,18 @@<br>
<br>
#include "BodyFarm.h"<br>
#include "clang/AST/ASTContext.h"<br>
+#include "clang/AST/CXXInheritance.h"<br>
#include "clang/AST/Decl.h"<br>
#include "clang/AST/Expr.h"<br>
+#include "clang/AST/ExprCXX.h"<br>
#include "clang/AST/ExprObjC.h"<br>
+#include "clang/AST/<wbr>NestedNameSpecifier.h"<br>
#include "clang/Analysis/CodeInjector.<wbr>h"<br>
+#include "clang/Basic/OperatorKinds.h"<br>
#include "llvm/ADT/StringSwitch.h"<br>
+#include "llvm/Support/Debug.h"<br>
+<br>
+#define DEBUG_TYPE "body-farm"<br>
<br>
using namespace clang;<br>
<br>
@@ -55,7 +62,9 @@ public:<br>
CompoundStmt *makeCompound(ArrayRef<Stmt*>)<wbr>;<br>
<br>
/// Create a new DeclRefExpr for the referenced variable.<br>
- DeclRefExpr *makeDeclRefExpr(const VarDecl *D);<br>
+ DeclRefExpr *makeDeclRefExpr(const VarDecl *D,<br>
+ bool RefersToEnclosingVariableOrCap<wbr>ture = false,<br>
+ bool GetNonReferenceType = false);<br>
<br>
/// Create a new UnaryOperator representing a dereference.<br>
UnaryOperator *makeDereference(const Expr *Arg, QualType Ty);<br>
@@ -66,9 +75,24 @@ public:<br>
/// Create an implicit cast to a builtin boolean type.<br>
ImplicitCastExpr *makeIntegralCastToBoolean(<wbr>const Expr *Arg);<br>
<br>
- // Create an implicit cast for lvalue-to-rvaluate conversions.<br>
+ /// Create an implicit cast for lvalue-to-rvaluate conversions.<br>
ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty);<br>
<br>
+ /// Create an implicit cast for lvalue-to-rvaluate conversions.<br>
+ ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg,<br>
+ bool GetNonReferenceType = false);<br>
+<br>
+ /// Make RValue out of variable declaration, creating a temporary<br>
+ /// DeclRefExpr in the process.<br>
+ ImplicitCastExpr *<br>
+ makeLvalueToRvalue(const VarDecl *Decl,<br>
+ bool RefersToEnclosingVariableOrCap<wbr>ture = false,<br>
+ bool GetNonReferenceType = false);<br>
+<br>
+ /// Create an implicit cast of the given type.<br>
+ ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty,<br>
+ CastKind CK = CK_LValueToRValue);<br>
+<br>
/// Create an Objective-C bool literal.<br>
ObjCBoolLiteralExpr *makeObjCBool(bool Val);<br>
<br>
@@ -78,6 +102,18 @@ public:<br>
/// Create a Return statement.<br>
ReturnStmt *makeReturn(const Expr *RetVal);<br>
<br>
+ /// Create an integer literal.<br>
+ IntegerLiteral *makeIntegerLiteral(uint64_t value);<br>
+<br>
+ /// Create a member expression.<br>
+ MemberExpr *makeMemberExpression(Expr *base, ValueDecl *MemberDecl,<br>
+ bool IsArrow = false,<br>
+ ExprValueKind ValueKind = VK_LValue);<br>
+<br>
+ /// Returns a *first* member field of a record declaration with a given name.<br>
+ /// \return an nullptr if no member with such a name exists.<br>
+ NamedDecl *findMemberField(const CXXRecordDecl *RD, StringRef Name);<br>
+<br>
private:<br>
ASTContext &C;<br>
};<br>
@@ -106,16 +142,16 @@ CompoundStmt *ASTMaker::makeCompound(Arr<br>
return new (C) CompoundStmt(C, Stmts, SourceLocation(), SourceLocation());<br>
}<br>
<br>
-DeclRefExpr *ASTMaker::makeDeclRefExpr(<wbr>const VarDecl *D) {<br>
- DeclRefExpr *DR =<br>
- DeclRefExpr::Create(/* Ctx = */ C,<br>
- /* QualifierLoc = */ NestedNameSpecifierLoc(),<br>
- /* TemplateKWLoc = */ SourceLocation(),<br>
- /* D = */ const_cast<VarDecl*>(D),<br>
- /* RefersToEnclosingVariableOrCap<wbr>ture = */ false,<br>
- /* NameLoc = */ SourceLocation(),<br>
- /* T = */ D->getType(),<br>
- /* VK = */ VK_LValue);<br>
+DeclRefExpr *ASTMaker::makeDeclRefExpr(<wbr>const VarDecl *D,<br>
+ bool RefersToEnclosingVariableOrCap<wbr>ture,<br>
+ bool GetNonReferenceType) {<br>
+ auto Type = D->getType();<br>
+ if (GetNonReferenceType)<br>
+ Type = Type.getNonReferenceType();<br>
+<br>
+ DeclRefExpr *DR = DeclRefExpr::Create(<br>
+ C, NestedNameSpecifierLoc(), SourceLocation(), const_cast<VarDecl *>(D),<br>
+ RefersToEnclosingVariableOrCap<wbr>ture, SourceLocation(), Type, VK_LValue);<br>
return DR;<br>
}<br>
<br>
@@ -125,8 +161,38 @@ UnaryOperator *ASTMaker::makeDereference<br>
}<br>
<br>
ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(<wbr>const Expr *Arg, QualType Ty) {<br>
- return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue,<br>
- const_cast<Expr*>(Arg), nullptr, VK_RValue);<br>
+ return makeImplicitCast(Arg, Ty, CK_LValueToRValue);<br>
+}<br>
+<br>
+ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(<wbr>const Expr *Arg,<br>
+ bool GetNonReferenceType) {<br>
+<br>
+ QualType Type = Arg->getType();<br>
+ if (GetNonReferenceType)<br>
+ Type = Type.getNonReferenceType();<br>
+ return makeImplicitCast(Arg, Type, CK_LValueToRValue);<br>
+}<br>
+<br>
+ImplicitCastExpr *<br>
+ASTMaker::makeLvalueToRvalue(<wbr>const VarDecl *Arg,<br>
+ bool RefersToEnclosingVariableOrCap<wbr>ture,<br>
+ bool GetNonReferenceType) {<br>
+ auto Type = Arg->getType();<br>
+ if (GetNonReferenceType)<br>
+ Type = Type.getNonReferenceType();<br>
+ return makeLvalueToRvalue(<wbr>makeDeclRefExpr(Arg,<br>
+ RefersToEnclosingVariableOrCap<wbr>ture,<br>
+ GetNonReferenceType),<br>
+ Type);<br>
+}<br>
+<br>
+ImplicitCastExpr *ASTMaker::makeImplicitCast(<wbr>const Expr *Arg, QualType Ty,<br>
+ CastKind CK) {<br>
+ return ImplicitCastExpr::Create(C, Ty,<br>
+ /* CastKind= */ CK,<br>
+ /* Expr= */ const_cast<Expr *>(Arg),<br>
+ /* CXXCastPath= */ nullptr,<br>
+ /* ExprValueKind= */ VK_RValue);<br>
}<br>
<br>
Expr *ASTMaker::makeIntegralCast(<wbr>const Expr *Arg, QualType Ty) {<br>
@@ -161,12 +227,196 @@ ReturnStmt *ASTMaker::makeReturn(const E<br>
nullptr);<br>
}<br>
<br>
+IntegerLiteral *ASTMaker::makeIntegerLiteral(<wbr>uint64_t value) {<br>
+ return IntegerLiteral::Create(C,<br>
+ llvm::APInt(<br>
+ /*numBits=*/C.getTypeSize(C.<wbr>IntTy), value),<br>
+ /*QualType=*/C.IntTy, SourceLocation());<br>
+}<br>
+<br>
+MemberExpr *ASTMaker::<wbr>makeMemberExpression(Expr *base, ValueDecl *MemberDecl,<br>
+ bool IsArrow,<br>
+ ExprValueKind ValueKind) {<br>
+<br>
+ DeclAccessPair FoundDecl = DeclAccessPair::make(<wbr>MemberDecl, AS_public);<br>
+ return MemberExpr::Create(<br>
+ C, base, IsArrow, SourceLocation(), NestedNameSpecifierLoc(),<br>
+ SourceLocation(), MemberDecl, FoundDecl,<br>
+ DeclarationNameInfo(<wbr>MemberDecl->getDeclName(), SourceLocation()),<br>
+ /* TemplateArgumentListInfo= */ nullptr, MemberDecl->getType(), ValueKind,<br>
+ OK_Ordinary);<br>
+}<br>
+<br>
+NamedDecl *ASTMaker::findMemberField(<wbr>const CXXRecordDecl *RD, StringRef Name) {<br>
+<br>
+ CXXBasePaths Paths(<br>
+ /* FindAmbiguities=*/false,<br>
+ /* RecordPaths=*/false,<br>
+ /* DetectVirtual= */ false);<br>
+ const IdentifierInfo &II = C.Idents.get(Name);<br>
+ DeclarationName DeclName = C.DeclarationNames.<wbr>getIdentifier(&II);<br>
+<br>
+ DeclContextLookupResult Decls = RD->lookup(DeclName);<br>
+ for (NamedDecl *FoundDecl : Decls)<br>
+ if (!FoundDecl->getDeclContext()-<wbr>>isFunctionOrMethod())<br>
+ return FoundDecl;<br>
+<br>
+ return nullptr;<br>
+}<br>
+<br>
//===-------------------------<wbr>------------------------------<wbr>---------------===//<br>
// Creation functions for faux ASTs.<br>
//===-------------------------<wbr>------------------------------<wbr>---------------===//<br>
<br>
typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);<br>
<br>
+static CallExpr *<br>
+create_call_once_funcptr_<wbr>call(ASTContext &C, ASTMaker M,<br>
+ const ParmVarDecl *Callback,<br>
+ SmallVectorImpl<Expr *> &CallArgs) {<br>
+<br>
+ return new (C) CallExpr(<br>
+ /*ASTContext=*/C,<br>
+ /*StmtClass=*/M.<wbr>makeLvalueToRvalue(/*Expr=*/<wbr>Callback),<br>
+ /*args=*/CallArgs,<br>
+ /*QualType=*/C.VoidTy,<br>
+ /*ExprValueType=*/VK_RValue,<br>
+ /*SourceLocation=*/<wbr>SourceLocation());<br>
+}<br>
+<br>
+static CallExpr *<br>
+create_call_once_lambda_call(<wbr>ASTContext &C, ASTMaker M,<br>
+ const ParmVarDecl *Callback, QualType CallbackType,<br>
+ SmallVectorImpl<Expr *> &CallArgs) {<br>
+<br>
+ CXXRecordDecl *CallbackDecl = CallbackType-><wbr>getAsCXXRecordDecl();<br>
+<br>
+ assert(CallbackDecl != nullptr);<br>
+ assert(CallbackDecl->isLambda(<wbr>));<br>
+ FunctionDecl *callOperatorDecl = CallbackDecl-><wbr>getLambdaCallOperator();<br>
+ assert(callOperatorDecl != nullptr);<br>
+<br>
+ DeclRefExpr *callOperatorDeclRef =<br>
+ DeclRefExpr::Create(/* Ctx = */ C,<br>
+ /* QualifierLoc = */ NestedNameSpecifierLoc(),<br>
+ /* TemplateKWLoc = */ SourceLocation(),<br>
+ const_cast<FunctionDecl *>(callOperatorDecl),<br>
+ /* RefersToEnclosingVariableOrCap<wbr>ture= */ false,<br>
+ /* NameLoc = */ SourceLocation(),<br>
+ /* T = */ callOperatorDecl->getType(),<br>
+ /* VK = */ VK_LValue);<br>
+<br>
+ CallArgs.insert(<br>
+ CallArgs.begin(),<br>
+ M.makeDeclRefExpr(Callback,<br>
+ /* RefersToEnclosingVariableOrCap<wbr>ture= */ true,<br>
+ /* GetNonReferenceType= */ true));<br>
+<br>
+ return new (C)<br>
+ CXXOperatorCallExpr(/*<wbr>AstContext=*/C, OO_Call, callOperatorDeclRef,<br>
+ /*args=*/CallArgs,<br>
+ /*QualType=*/C.VoidTy,<br>
+ /*ExprValueType=*/VK_RValue,<br>
+ /*SourceLocation=*/<wbr>SourceLocation(), FPOptions());<br>
+}<br>
+<br>
+/// Create a fake body for std::call_once.<br>
+/// Emulates the following function body:<br>
+///<br>
+/// \code<br>
+/// typedef struct once_flag_s {<br>
+/// unsigned long __state = 0;<br>
+/// } once_flag;<br>
+/// template<class Callable><br>
+/// void call_once(once_flag& o, Callable func) {<br>
+/// if (!o.__state) {<br>
+/// func();<br>
+/// }<br>
+/// o.__state = 1;<br>
+/// }<br>
+/// \endcode<br>
+static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {<br>
+ DEBUG(llvm::dbgs() << "Generating body for call_once\n");<br>
+<br>
+ // We need at least two parameters.<br>
+ if (D->param_size() < 2)<br>
+ return nullptr;<br>
+<br>
+ ASTMaker M(C);<br>
+<br>
+ const ParmVarDecl *Flag = D->getParamDecl(0);<br>
+ const ParmVarDecl *Callback = D->getParamDecl(1);<br>
+ QualType CallbackType = Callback->getType().<wbr>getNonReferenceType();<br>
+<br>
+ SmallVector<Expr *, 5> CallArgs;<br>
+<br>
+ // All arguments past first two ones are passed to the callback.<br>
+ for (unsigned int i = 2; i < D->getNumParams(); i++)<br>
+ CallArgs.push_back(M.<wbr>makeLvalueToRvalue(D-><wbr>getParamDecl(i)));<br>
+<br>
+ CallExpr *CallbackCall;<br>
+ if (CallbackType-><wbr>getAsCXXRecordDecl() &&<br>
+ CallbackType-><wbr>getAsCXXRecordDecl()-><wbr>isLambda()) {<br>
+<br>
+ CallbackCall =<br>
+ create_call_once_lambda_call(<wbr>C, M, Callback, CallbackType, CallArgs);<br>
+ } else {<br>
+<br>
+ // Function pointer case.<br>
+ CallbackCall = create_call_once_funcptr_call(<wbr>C, M, Callback, CallArgs);<br>
+ }<br>
+<br>
+ QualType FlagType = Flag->getType().<wbr>getNonReferenceType();<br>
+ DeclRefExpr *FlagDecl =<br>
+ M.makeDeclRefExpr(Flag,<br>
+ /* RefersToEnclosingVariableOrCap<wbr>ture=*/true,<br>
+ /* GetNonReferenceType=*/true);<br>
+<br>
+ CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl()<wbr>;<br>
+<br>
+ // Note: here we are assuming libc++ implementation of call_once,<br>
+ // which has a struct with a field `__state_`.<br>
+ // Body farming might not work for other `call_once` implementations.<br>
+ NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_");<br>
+ ValueDecl *FieldDecl;<br>
+ if (FoundDecl) {<br>
+ FieldDecl = dyn_cast<ValueDecl>(FoundDecl)<wbr>;<br>
+ } else {<br>
+ DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, "<br>
+ << "unable to synthesize call_once body, ignoring "<br>
+ << "the call.\n");<br>
+ return nullptr;<br>
+ }<br>
+<br>
+ MemberExpr *Deref = M.makeMemberExpression(<wbr>FlagDecl, FieldDecl);<br>
+ assert(Deref->isLValue());<br>
+ QualType DerefType = Deref->getType();<br>
+<br>
+ // Negation predicate.<br>
+ UnaryOperator *FlagCheck = new (C) UnaryOperator(<br>
+ /* input= */<br>
+ M.makeImplicitCast(M.<wbr>makeLvalueToRvalue(Deref, DerefType), DerefType,<br>
+ CK_IntegralToBoolean),<br>
+ /* opc= */ UO_LNot,<br>
+ /* QualType= */ C.IntTy,<br>
+ /* ExprValueKind= */ VK_RValue,<br>
+ /* ExprObjectKind= */ OK_Ordinary, SourceLocation());<br>
+<br>
+ // Create assignment.<br>
+ BinaryOperator *FlagAssignment = M.makeAssignment(<br>
+ Deref, M.makeIntegralCast(M.<wbr>makeIntegerLiteral(1), DerefType), DerefType);<br>
+<br>
+ IfStmt *Out = new (C)<br>
+ IfStmt(C, SourceLocation(),<br>
+ /* IsConstexpr= */ false,<br>
+ /* init= */ nullptr,<br>
+ /* var= */ nullptr,<br>
+ /* cond= */ FlagCheck,<br>
+ /* then= */ M.makeCompound({CallbackCall, FlagAssignment}));<br>
+<br>
+ return Out;<br>
+}<br>
+<br>
/// Create a fake body for dispatch_once.<br>
static Stmt *create_dispatch_once(<wbr>ASTContext &C, const FunctionDecl *D) {<br>
// Check if we have at least two parameters.<br>
@@ -202,15 +452,17 @@ static Stmt *create_dispatch_once(ASTCon<br>
ASTMaker M(C);<br>
<br>
// (1) Create the call.<br>
- DeclRefExpr *DR = M.makeDeclRefExpr(Block);<br>
- ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);<br>
- CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue,<br>
- SourceLocation());<br>
+ CallExpr *CE = new (C) CallExpr(<br>
+ /*ASTContext=*/C,<br>
+ /*StmtClass=*/M.<wbr>makeLvalueToRvalue(/*Expr=*/<wbr>Block),<br>
+ /*args=*/None,<br>
+ /*QualType=*/C.VoidTy,<br>
+ /*ExprValueType=*/VK_RValue,<br>
+ /*SourceLocation=*/<wbr>SourceLocation());<br>
<br>
// (2) Create the assignment to the predicate.<br>
- IntegerLiteral *IL =<br>
- IntegerLiteral::Create(C, llvm::APInt(C.getTypeSize(C.<wbr>IntTy), (uint64_t) 1),<br>
- C.IntTy, SourceLocation());<br>
+ IntegerLiteral *IL = M.makeIntegerLiteral(1);<br>
+<br>
BinaryOperator *B =<br>
M.makeAssignment(<br>
M.makeDereference(<br>
@@ -234,13 +486,20 @@ static Stmt *create_dispatch_once(ASTCon<br>
PredicateTy),<br>
PredicateTy);<br>
<br>
- UnaryOperator *UO = new (C) UnaryOperator(LValToRval, UO_LNot, C.IntTy,<br>
- VK_RValue, OK_Ordinary,<br>
- SourceLocation());<br>
+ UnaryOperator *UO = new (C) UnaryOperator(<br>
+ /* input= */ LValToRval,<br>
+ /* opc= */ UO_LNot,<br>
+ /* QualType= */ C.IntTy,<br>
+ /* ExprValueKind= */ VK_RValue,<br>
+ /* ExprObjectKind= */ OK_Ordinary, SourceLocation());<br>
<br>
// (5) Create the 'if' statement.<br>
- IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr,<br>
- UO, CS);<br>
+ IfStmt *If = new (C) IfStmt(C, SourceLocation(),<br>
+ /* IsConstexpr= */ false,<br>
+ /* init= */ nullptr,<br>
+ /* var= */ nullptr,<br>
+ /* cond= */ UO,<br>
+ /* then= */ CS);<br>
return If;<br>
}<br>
<br>
@@ -370,8 +629,9 @@ Stmt *BodyFarm::getBody(const FunctionDe<br>
if (Name.startswith("<wbr>OSAtomicCompareAndSwap") ||<br>
Name.startswith("objc_<wbr>atomicCompareAndSwap")) {<br>
FF = create_OSAtomicCompareAndSwap;<br>
- }<br>
- else {<br>
+ } else if (Name == "call_once" && D->getDeclContext()-><wbr>isStdNamespace()) {<br>
+ FF = create_call_once;<br>
+ } else {<br>
FF = llvm::StringSwitch<<wbr>FunctionFarmer>(Name)<br>
.Case("dispatch_sync", create_dispatch_sync)<br>
.Case("dispatch_once", create_dispatch_once)<br>
<br>
Added: cfe/trunk/test/Analysis/call_<wbr>once.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/call_once.cpp?rev=314571&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/test/<wbr>Analysis/call_once.cpp?rev=<wbr>314571&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/test/Analysis/call_<wbr>once.cpp (added)<br>
+++ cfe/trunk/test/Analysis/call_<wbr>once.cpp Fri Sep 29 17:03:22 2017<br>
@@ -0,0 +1,233 @@<br>
+// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.<wbr>ExprInspection -w -verify %s<br>
+<br>
+void clang_analyzer_eval(bool);<br>
+<br>
+// Faking std::std::call_once implementation.<br>
+namespace std {<br>
+typedef struct once_flag_s {<br>
+ unsigned long __state_ = 0;<br>
+} once_flag;<br>
+<br>
+template <class Callable, class... Args><br>
+void call_once(once_flag &o, Callable func, Args... args);<br>
+} // namespace std<br>
+<br>
+// Check with Lambdas.<br>
+void test_called_warning() {<br>
+ std::once_flag g_initialize;<br>
+ int z;<br>
+<br>
+ std::call_once(g_initialize, [&] {<br>
+ int *x = nullptr;<br>
+ int y = *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}<br>
+ z = 200;<br>
+ });<br>
+}<br>
+<br>
+void test_called_on_path_inside_no_<wbr>warning() {<br>
+ std::once_flag g_initialize;<br>
+<br>
+ int *x = nullptr;<br>
+ int y = 100;<br>
+ int z;<br>
+<br>
+ std::call_once(g_initialize, [&] {<br>
+ z = 200;<br>
+ x = &z;<br>
+ });<br>
+<br>
+ *x = 100; // no-warning<br>
+ clang_analyzer_eval(z == 100); // expected-warning{{TRUE}}<br>
+}<br>
+<br>
+void test_called_on_path_no_<wbr>warning() {<br>
+ std::once_flag g_initialize;<br>
+<br>
+ int *x = nullptr;<br>
+ int y = 100;<br>
+<br>
+ std::call_once(g_initialize, [&] {<br>
+ x = &y;<br>
+ });<br>
+<br>
+ *x = 100; // no-warning<br>
+}<br>
+<br>
+void test_called_on_path_warning() {<br>
+ std::once_flag g_initialize;<br>
+<br>
+ int y = 100;<br>
+ int *x = &y;<br>
+<br>
+ std::call_once(g_initialize, [&] {<br>
+ x = nullptr;<br>
+ });<br>
+<br>
+ *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}<br>
+}<br>
+<br>
+void test_called_once_warning() {<br>
+ std::once_flag g_initialize;<br>
+<br>
+ int *x = nullptr;<br>
+ int y = 100;<br>
+<br>
+ std::call_once(g_initialize, [&] {<br>
+ x = nullptr;<br>
+ });<br>
+<br>
+ std::call_once(g_initialize, [&] {<br>
+ x = &y;<br>
+ });<br>
+<br>
+ *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}<br>
+}<br>
+<br>
+void test_called_once_no_warning() {<br>
+ std::once_flag g_initialize;<br>
+<br>
+ int *x = nullptr;<br>
+ int y = 100;<br>
+<br>
+ std::call_once(g_initialize, [&] {<br>
+ x = &y;<br>
+ });<br>
+<br>
+ std::call_once(g_initialize, [&] {<br>
+ x = nullptr;<br>
+ });<br>
+<br>
+ *x = 100; // no-warning<br>
+}<br>
+<br>
+static int global = 0;<br>
+void funcPointer() {<br>
+ global = 1;<br>
+}<br>
+<br>
+void test_func_pointers() {<br>
+ static std::once_flag flag;<br>
+ std::call_once(flag, &funcPointer);<br>
+ clang_analyzer_eval(global == 1); // expected-warning{{TRUE}}<br>
+}<br>
+<br>
+template <class _Fp><br>
+class function; // undefined<br>
+template <class _Rp, class... _ArgTypes><br>
+struct function<_Rp(_ArgTypes...)> {<br>
+ _Rp operator()(_ArgTypes...) const;<br>
+ template <class _Fp><br>
+ function(_Fp);<br>
+};<br>
+<br>
+// Note: currently we do not support calls to std::function,<br>
+// but the analyzer should not crash either.<br>
+void test_function_objects_warning(<wbr>) {<br>
+ int x = 0;<br>
+ int *y = &x;<br>
+<br>
+ std::once_flag flag;<br>
+<br>
+ function<void()> func = [&]() {<br>
+ y = nullptr;<br>
+ };<br>
+<br>
+ std::call_once(flag, func);<br>
+<br>
+ func();<br>
+ int z = *y;<br>
+}<br>
+<br>
+void test_param_passing_lambda() {<br>
+ std::once_flag flag;<br>
+ int x = 120;<br>
+ int y = 0;<br>
+<br>
+ std::call_once(flag, [&](int p) {<br>
+ y = p;<br>
+ },<br>
+ x);<br>
+<br>
+ clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}<br>
+}<br>
+<br>
+void test_param_passing_lambda_<wbr>false() {<br>
+ std::once_flag flag;<br>
+ int x = 120;<br>
+<br>
+ std::call_once(flag, [&](int p) {<br>
+ x = 0;<br>
+ },<br>
+ x);<br>
+<br>
+ clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}<br>
+}<br>
+<br>
+void test_param_passing_stored_<wbr>lambda() {<br>
+ std::once_flag flag;<br>
+ int x = 120;<br>
+ int y = 0;<br>
+<br>
+ auto lambda = [&](int p) {<br>
+ y = p;<br>
+ };<br>
+<br>
+ std::call_once(flag, lambda, x);<br>
+ clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}<br>
+}<br>
+<br>
+void test_multiparam_passing_<wbr>lambda() {<br>
+ std::once_flag flag;<br>
+ int x = 120;<br>
+<br>
+ std::call_once(flag, [&](int a, int b, int c) {<br>
+ x = a + b + c;<br>
+ },<br>
+ 1, 2, 3);<br>
+<br>
+ clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}<br>
+ clang_analyzer_eval(x == 6); // expected-warning{{TRUE}}<br>
+}<br>
+<br>
+static int global2 = 0;<br>
+void test_param_passing_lambda_<wbr>global() {<br>
+ std::once_flag flag;<br>
+ global2 = 0;<br>
+ std::call_once(flag, [&](int a, int b, int c) {<br>
+ global2 = a + b + c;<br>
+ },<br>
+ 1, 2, 3);<br>
+ clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}}<br>
+}<br>
+<br>
+static int global3 = 0;<br>
+void funcptr(int a, int b, int c) {<br>
+ global3 = a + b + c;<br>
+}<br>
+<br>
+void test_param_passing_funcptr() {<br>
+ std::once_flag flag;<br>
+ global3 = 0;<br>
+<br>
+ std::call_once(flag, &funcptr, 1, 2, 3);<br>
+<br>
+ clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}}<br>
+}<br>
+<br>
+void test_blocks() {<br>
+ global3 = 0;<br>
+ std::once_flag flag;<br>
+ std::call_once(flag, ^{<br>
+ global3 = 120;<br>
+ });<br>
+ clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}}<br>
+}<br>
+<br>
+int call_once() {<br>
+ return 5;<br>
+}<br>
+<br>
+void test_non_std_call_once() {<br>
+ int x = call_once();<br>
+ clang_analyzer_eval(x == 5); // expected-warning{{TRUE}}<br>
+}<br>
<br>
<br>
______________________________<wbr>_________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>