r314571 - [Analyzer] Synthesize function body for std::call_once
Alexander Kornienko via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 9 05:54:36 PDT 2017
Bugzilla is not accessible, so here's a reduced test case:
$ cat test-clang__BodyFarm__getBody.cc
namespace std {
template <typename d, typename e>
void call_once(d, e);
}
void g();
void f() {
std::call_once(g, false);
}
$ clang-tidy -checks=-*,clang-analyzer* test-clang__BodyFarm__getBody.cc --
-std=c++11 -w
*** SIGSEGV; stack trace: ***
PC: @ 0x2f96e3a (unknown) clang::DeclContext::lookup()
@ 0x532bb51 1152 FailureSignalHandler()
@ 0x7f42b341f9a0 (unknown) (unknown)
@ 0x2ae74c5 576 (anonymous
namespace)::ASTMaker::findMemberField()
@ 0x2ae59fc 704 create_call_once()
@ 0x2ae5040 784 clang::BodyFarm::getBody()
@ 0x2ac67af 144 clang::AnalysisDeclContext::getBody()
@ 0x1c499fa 128
clang::ento::AnyFunctionCall::getRuntimeDefinition()
@ 0x1cc6146 320
clang::ento::ExprEngine::defaultEvalCall()
@ 0x1c61033 464
clang::ento::CheckerManager::runCheckersForEvalCall()
@ 0x1cc4fc9 352 clang::ento::ExprEngine::evalCall()
@ 0x1cc4e8c 432
clang::ento::ExprEngine::VisitCallExpr()
@ 0x1c7da94 4000 clang::ento::ExprEngine::Visit()
@ 0x1c7a821 496 clang::ento::ExprEngine::ProcessStmt()
@ 0x1c7a4da 240
clang::ento::ExprEngine::processCFGElement()
@ 0x1ca8ed6 128
clang::ento::CoreEngine::HandlePostStmt()
@ 0x1ca87d6 496
clang::ento::CoreEngine::dispatchWorkItem()
@ 0x1ca8338 544
clang::ento::CoreEngine::ExecuteWorkList()
@ 0xf954d5 80
clang::ento::ExprEngine::ExecuteWorkList()
@ 0xf3c612 1056 (anonymous
namespace)::AnalysisConsumer::ActionExprEngine()
@ 0xf3c3d1 80 (anonymous
namespace)::AnalysisConsumer::RunPathSensitiveChecks()
@ 0xf3c095 288 (anonymous
namespace)::AnalysisConsumer::HandleCode()
@ 0xf2f6e3 416 (anonymous
namespace)::AnalysisConsumer::HandleDeclsCallGraph()
@ 0xf2d967 336 (anonymous
namespace)::AnalysisConsumer::HandleTranslationUnit()
@ 0x1366eae 80
clang::MultiplexConsumer::HandleTranslationUnit()
@ 0x1dc6ae6 288 clang::ParseAST()
@ 0x135475a 80
clang::ASTFrontendAction::ExecuteAction()
@ 0x13541f0 112 clang::FrontendAction::Execute()
@ 0x1169822 496
clang::CompilerInstance::ExecuteAction()
@ 0x1032ba2 464
clang::tooling::FrontendActionFactory::runInvocation()
@ 0x1032a43 160
clang::tooling::ToolInvocation::runInvocation()
@ 0x1031306 1840 clang::tooling::ToolInvocation::run()
@ 0x1033c30 1664 clang::tooling::ClangTool::run()
On Sat, Oct 7, 2017 at 12:56 PM, Alexander Kornienko <alexfh at google.com>
wrote:
> This revision might be the cause of https://bugs.llvm.org/show_
> bug.cgi?id=34869. I'm still working on a reduced test case.
>
> On Sat, Sep 30, 2017 at 2:03 AM, George Karpenkov via cfe-commits <
> cfe-commits at lists.llvm.org> wrote:
>
>> 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->getLambdaCallOpe
>> rator();
>> + 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}}
>> +}
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20171009/785600ce/attachment-0001.html>
More information about the cfe-commits
mailing list