Index: cfe/trunk/lib/Analysis/BodyFarm.cpp =================================================================== --- cfe/trunk/lib/Analysis/BodyFarm.cpp +++ cfe/trunk/lib/Analysis/BodyFarm.cpp @@ -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 @@ CompoundStmt *makeCompound(ArrayRef); /// 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 @@ /// 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 @@ /// 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 @@ 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(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(D), + RefersToEnclosingVariableOrCapture, SourceLocation(), Type, VK_LValue); return DR; } @@ -125,8 +161,38 @@ } ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) { - return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue, - const_cast(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(Arg), + /* CXXCastPath= */ nullptr, + /* ExprValueKind= */ VK_RValue); } Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) { @@ -161,12 +227,196 @@ 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 &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 &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(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 +/// 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 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(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 @@ 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 @@ 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 @@ 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(Name) .Case("dispatch_sync", create_dispatch_sync) .Case("dispatch_once", create_dispatch_once) Index: cfe/trunk/test/Analysis/call_once.cpp =================================================================== --- cfe/trunk/test/Analysis/call_once.cpp +++ cfe/trunk/test/Analysis/call_once.cpp @@ -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 +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 function; // undefined +template +struct function<_Rp(_ArgTypes...)> { + _Rp operator()(_ArgTypes...) const; + template + 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 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}} +}