Index: lib/Analysis/BodyFarm.cpp =================================================================== --- lib/Analysis/BodyFarm.cpp +++ lib/Analysis/BodyFarm.cpp @@ -1,4 +1,4 @@ -//== BodyFarm.cpp - Factory for conjuring up fake bodies ----------*- C++ -*-// +///== BodyFarm.cpp - Factory for conjuring up fake bodies ----------*- C++ -*-// // // The LLVM Compiler Infrastructure // @@ -17,6 +17,10 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/Basic/OperatorKinds.h" #include "clang/Analysis/CodeInjector.h" #include "llvm/ADT/StringSwitch.h" @@ -55,7 +59,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,8 +72,20 @@ /// 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); + + // TODO: a somewhat questionable shortcut. + 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, // TODO: shouldn't Type be implied from Arg? + CastKind CK=CK_LValueToRValue); /// Create an Objective-C bool literal. ObjCBoolLiteralExpr *makeObjCBool(bool Val); @@ -77,6 +95,23 @@ /// 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 member of a record declaration with a given name. + /// \return an empty optional if no member with such a name + /// exists OR if multiple members do.. + Optional findMember( + const CXXRecordDecl* RD, + StringRef Name); private: ASTContext &C; @@ -106,15 +141,22 @@ return new (C) CompoundStmt(C, Stmts, SourceLocation(), SourceLocation()); } -DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) { +DeclRefExpr *ASTMaker::makeDeclRefExpr( + const VarDecl *D, + bool RefersToEnclosingVariableOrCapture, + bool GetNonReferenceType) { + auto Type = D->getType(); + if (GetNonReferenceType) + Type = Type.getNonReferenceType(); + DeclRefExpr *DR = DeclRefExpr::Create(/* Ctx = */ C, /* QualifierLoc = */ NestedNameSpecifierLoc(), /* TemplateKWLoc = */ SourceLocation(), - /* D = */ const_cast(D), - /* RefersToEnclosingVariableOrCapture = */ false, + const_cast(D), + RefersToEnclosingVariableOrCapture, /* NameLoc = */ SourceLocation(), - /* T = */ D->getType(), + /* T = */ Type, /* VK = */ VK_LValue); return DR; } @@ -125,8 +167,33 @@ } 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 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 +228,236 @@ nullptr); } +IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t value) { + return IntegerLiteral::Create( + /*ASTContext=*/C, + /*APInt=*/llvm::APInt( + /*numBits=*/C.getTypeSize(C.IntTy), + /*val=*/ 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); +} + + +Optional ASTMaker::findMember( + 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); + if (Decls.size() == 1) { + return Optional(Decls.front()); + } else if (Decls.size() > 1) { + return Optional(); + } + + RD->lookupInBases( + [DeclName](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) { + return CXXRecordDecl::FindOrdinaryMember(Specifier, Path, DeclName); + }, Paths); + + NamedDecl* FoundDecl = nullptr; + for (CXXBasePath Pth : Paths) { + for (NamedDecl* Decl : Pth.Decls) { + if (!FoundDecl) { + FoundDecl = Decl; + } else { + return Optional(); + } + } + } + + if (FoundDecl) { + return Optional(); + } + return Optional(FoundDecl); +} + //===----------------------------------------------------------------------===// // 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, + std::vector &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, + std::vector &CallArgs) { + + CXXRecordDecl *CallbackDecl = Callback->getType()->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) { + + // 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();; + + std::vector CallArgs; + + // All arguments past first two ones are passed to the callback. + for (unsigned int i=2; igetNumParams(); 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, CallArgs); + } else { + + // Any other cases? + // TODO: Instead, recognize this case specifically and return nullptr + // in the "else" branch. + + // 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(); + + // TODO: fail gracefully if no value is present. + // TODO: fail gracefully if value is not a ValueDecl*. + ValueDecl* FieldDecl = dyn_cast( + M.findMember(FlagCXXDecl, "__state").getValue()); + + 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. @@ -198,19 +489,21 @@ // block(); // } // } - + 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( @@ -219,11 +512,11 @@ PredicateTy), M.makeIntegralCast(IL, PredicateTy), PredicateTy); - + // (3) Create the compound statement. Stmt *Stmts[] = { B, CE }; CompoundStmt *CS = M.makeCompound(Stmts); - + // (4) Create the 'if' condition. ImplicitCastExpr *LValToRval = M.makeLvalueToRvalue( @@ -233,14 +526,24 @@ PredicateQPtrTy), 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; } @@ -375,6 +678,7 @@ FF = llvm::StringSwitch(Name) .Case("dispatch_sync", create_dispatch_sync) .Case("dispatch_once", create_dispatch_once) + .Case("call_once", create_call_once) .Default(nullptr); } Index: test/Analysis/call_once.cpp =================================================================== --- /dev/null +++ test/Analysis/call_once.cpp @@ -0,0 +1,208 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.ExprInspection -w -verify %s + +typedef struct once_flag_s { + unsigned long __state = 0; +} once_flag; + +void clang_analyzer_eval(bool); +void clang_analyzer_printState(); + +template +void call_once(once_flag& o, Callable func, Args... args); + +// Check with Lambdas. +void test_called_warning() { + once_flag g_initialize; + int z; + + + 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() { + once_flag g_initialize; + + int *x = nullptr; + int y = 100; + int z; + + 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() { + once_flag g_initialize; + + int *x = nullptr; + int y = 100; + + call_once(g_initialize, [&] { + x = &y; + }); + + *x = 100; // no-warning +} + +void test_called_on_path_warning() { + once_flag g_initialize; + + int y = 100; + int *x = &y; + + call_once(g_initialize, [&] { + x = nullptr; + }); + + *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} +} + +void test_called_once_warning() { + once_flag g_initialize; + + int *x = nullptr; + int y = 100; + + call_once(g_initialize, [&] { + x = nullptr; + }); + + call_once(g_initialize, [&] { + x = &y; + }); + + *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} +} + +void test_called_once_no_warning() { + once_flag g_initialize; + + int *x = nullptr; + int y = 100; + + call_once(g_initialize, [&] { + x = &y; + }); + + call_once(g_initialize, [&] { + x = nullptr; + }); + + *x = 100; // no-warning +} + +static int global = 0; +void funcPointer() { + global = 1; +} + +void test_func_pointers() { + static once_flag flag; + 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; + + once_flag flag; + + function func = [&]() { + y = nullptr; + }; + + call_once(flag, func); + + func(); + int z = *y; +} + +void test_param_passing_lambda() { + once_flag flag; + int x = 120; + int y = 0; + + call_once(flag, [&](int p) { + y = p; + }, x); + + clang_analyzer_eval(y == 120); // expected-warning{{TRUE}} +} + +void test_param_passing_lambda_false() { + once_flag flag; + int x = 120; + + call_once(flag, [&](int p) { + x = 0; + }, x); + + clang_analyzer_eval(x == 120); // expected-warning{{FALSE}} +} + +void test_param_passing_stored_lambda() { + once_flag flag; + int x = 120; + int y = 0; + + auto lambda = [&](int p) { + y = p; + }; + + call_once(flag, lambda, x); + clang_analyzer_eval(y == 120); // expected-warning{{TRUE}} +} + +void test_multiparam_passing_lambda() { + once_flag flag; + int x = 120; + + 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() { + once_flag flag; + global2 = 0; + 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; // TODO: no need to rename/introduce new variables. +void funcptr(int a, int b, int c) { + global3 = a + b + c; +} + +void test_param_passing_funcptr() { + once_flag flag; + global3 = 0; + + call_once(flag, &funcptr, 1, 2, 3); + + clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}} +}