18beac285SDevin Coughlin //==- GTestChecker.cpp - Model gtest API --*- C++ -*-==//
28beac285SDevin Coughlin //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68beac285SDevin Coughlin //
78beac285SDevin Coughlin //===----------------------------------------------------------------------===//
88beac285SDevin Coughlin //
9e17f6215SDevin Coughlin // This checker models the behavior of un-inlined APIs from the gtest
108beac285SDevin Coughlin // unit-testing library to avoid false positives when using assertions from
118beac285SDevin Coughlin // that library.
128beac285SDevin Coughlin //
138beac285SDevin Coughlin //===----------------------------------------------------------------------===//
148beac285SDevin Coughlin 
1576a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
168beac285SDevin Coughlin #include "clang/AST/Expr.h"
178beac285SDevin Coughlin #include "clang/Basic/LangOptions.h"
188beac285SDevin Coughlin #include "clang/StaticAnalyzer/Core/Checker.h"
198beac285SDevin Coughlin #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
208beac285SDevin Coughlin #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
218beac285SDevin Coughlin #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
228beac285SDevin Coughlin #include "llvm/Support/raw_ostream.h"
238beac285SDevin Coughlin 
248beac285SDevin Coughlin using namespace clang;
258beac285SDevin Coughlin using namespace ento;
268beac285SDevin Coughlin 
278beac285SDevin Coughlin // Modeling of un-inlined AssertionResult constructors
288beac285SDevin Coughlin //
298beac285SDevin Coughlin // The gtest unit testing API provides macros for assertions that expand
308beac285SDevin Coughlin // into an if statement that calls a series of constructors and returns
318beac285SDevin Coughlin // when the "assertion" is false.
328beac285SDevin Coughlin //
338beac285SDevin Coughlin // For example,
348beac285SDevin Coughlin //
358beac285SDevin Coughlin //   ASSERT_TRUE(a == b)
368beac285SDevin Coughlin //
378beac285SDevin Coughlin // expands into:
388beac285SDevin Coughlin //
398beac285SDevin Coughlin //   switch (0)
408beac285SDevin Coughlin //   case 0:
418beac285SDevin Coughlin //   default:
428beac285SDevin Coughlin //     if (const ::testing::AssertionResult gtest_ar_ =
438beac285SDevin Coughlin //             ::testing::AssertionResult((a == b)))
448beac285SDevin Coughlin //       ;
458beac285SDevin Coughlin //     else
468beac285SDevin Coughlin //       return ::testing::internal::AssertHelper(
478beac285SDevin Coughlin //                  ::testing::TestPartResult::kFatalFailure,
488beac285SDevin Coughlin //                  "<path to project>",
498beac285SDevin Coughlin //                  <line number>,
508beac285SDevin Coughlin //                  ::testing::internal::GetBoolAssertionFailureMessage(
518beac285SDevin Coughlin //                      gtest_ar_, "a == b", "false", "true")
528beac285SDevin Coughlin //                      .c_str()) = ::testing::Message();
538beac285SDevin Coughlin //
548beac285SDevin Coughlin // where AssertionResult is defined similarly to
558beac285SDevin Coughlin //
568beac285SDevin Coughlin //   class AssertionResult {
578beac285SDevin Coughlin //   public:
588beac285SDevin Coughlin //     AssertionResult(const AssertionResult& other);
598beac285SDevin Coughlin //     explicit AssertionResult(bool success) : success_(success) {}
608beac285SDevin Coughlin //     operator bool() const { return success_; }
618beac285SDevin Coughlin //     ...
628beac285SDevin Coughlin //     private:
638beac285SDevin Coughlin //     bool success_;
648beac285SDevin Coughlin //   };
658beac285SDevin Coughlin //
668beac285SDevin Coughlin // In order for the analyzer to correctly handle this assertion, it needs to
678beac285SDevin Coughlin // know that the boolean value of the expression "a == b" is stored the
688beac285SDevin Coughlin // 'success_' field of the original AssertionResult temporary and propagated
698beac285SDevin Coughlin // (via the copy constructor) into the 'success_' field of the object stored
708beac285SDevin Coughlin // in 'gtest_ar_'.  That boolean value will then be returned from the bool
718beac285SDevin Coughlin // conversion method in the if statement. This guarantees that the assertion
728beac285SDevin Coughlin // holds when the return path is not taken.
738beac285SDevin Coughlin //
748beac285SDevin Coughlin // If the success value is not properly propagated, then the eager case split
758beac285SDevin Coughlin // on evaluating the expression can cause pernicious false positives
768beac285SDevin Coughlin // on the non-return path:
778beac285SDevin Coughlin //
788beac285SDevin Coughlin //   ASSERT(ptr != NULL)
798beac285SDevin Coughlin //   *ptr = 7; // False positive null pointer dereference here
808beac285SDevin Coughlin //
818beac285SDevin Coughlin // Unfortunately, the bool constructor cannot be inlined (because its
828beac285SDevin Coughlin // implementation is not present in the headers) and the copy constructor is
838beac285SDevin Coughlin // not inlined (because it is constructed into a temporary and the analyzer
848beac285SDevin Coughlin // does not inline these since it does not yet reliably call temporary
858beac285SDevin Coughlin // destructors).
868beac285SDevin Coughlin //
87e17f6215SDevin Coughlin // This checker compensates for the missing inlining by propagating the
888beac285SDevin Coughlin // _success value across the bool and copy constructors so the assertion behaves
898beac285SDevin Coughlin // as expected.
908beac285SDevin Coughlin 
918beac285SDevin Coughlin namespace {
928beac285SDevin Coughlin class GTestChecker : public Checker<check::PostCall> {
938beac285SDevin Coughlin 
948beac285SDevin Coughlin   mutable IdentifierInfo *AssertionResultII;
958beac285SDevin Coughlin   mutable IdentifierInfo *SuccessII;
968beac285SDevin Coughlin 
978beac285SDevin Coughlin public:
988beac285SDevin Coughlin   GTestChecker();
998beac285SDevin Coughlin 
1008beac285SDevin Coughlin   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
1018beac285SDevin Coughlin 
1028beac285SDevin Coughlin private:
1038beac285SDevin Coughlin   void modelAssertionResultBoolConstructor(const CXXConstructorCall *Call,
104e17f6215SDevin Coughlin                                            bool IsRef, CheckerContext &C) const;
1058beac285SDevin Coughlin 
1068beac285SDevin Coughlin   void modelAssertionResultCopyConstructor(const CXXConstructorCall *Call,
1078beac285SDevin Coughlin                                            CheckerContext &C) const;
1088beac285SDevin Coughlin 
1098beac285SDevin Coughlin   void initIdentifierInfo(ASTContext &Ctx) const;
1108beac285SDevin Coughlin 
1118beac285SDevin Coughlin   SVal
1128beac285SDevin Coughlin   getAssertionResultSuccessFieldValue(const CXXRecordDecl *AssertionResultDecl,
1138beac285SDevin Coughlin                                       SVal Instance,
1148beac285SDevin Coughlin                                       ProgramStateRef State) const;
1158beac285SDevin Coughlin 
1168beac285SDevin Coughlin   static ProgramStateRef assumeValuesEqual(SVal Val1, SVal Val2,
1178beac285SDevin Coughlin                                            ProgramStateRef State,
1188beac285SDevin Coughlin                                            CheckerContext &C);
1198beac285SDevin Coughlin };
1208beac285SDevin Coughlin } // End anonymous namespace.
1218beac285SDevin Coughlin 
GTestChecker()1228beac285SDevin Coughlin GTestChecker::GTestChecker() : AssertionResultII(nullptr), SuccessII(nullptr) {}
1238beac285SDevin Coughlin 
124e17f6215SDevin Coughlin /// Model a call to an un-inlined AssertionResult(bool) or
125e17f6215SDevin Coughlin /// AssertionResult(bool &, ...).
1268beac285SDevin Coughlin /// To do so, constrain the value of the newly-constructed instance's 'success_'
1278beac285SDevin Coughlin /// field to be equal to the passed-in boolean value.
128e17f6215SDevin Coughlin ///
129e17f6215SDevin Coughlin /// \param IsRef Whether the boolean parameter is a reference or not.
modelAssertionResultBoolConstructor(const CXXConstructorCall * Call,bool IsRef,CheckerContext & C) const1308beac285SDevin Coughlin void GTestChecker::modelAssertionResultBoolConstructor(
131e17f6215SDevin Coughlin     const CXXConstructorCall *Call, bool IsRef, CheckerContext &C) const {
132e17f6215SDevin Coughlin   assert(Call->getNumArgs() >= 1 && Call->getNumArgs() <= 2);
1338beac285SDevin Coughlin 
134e17f6215SDevin Coughlin   ProgramStateRef State = C.getState();
1358beac285SDevin Coughlin   SVal BooleanArgVal = Call->getArgSVal(0);
136e17f6215SDevin Coughlin   if (IsRef) {
137e17f6215SDevin Coughlin     // The argument is a reference, so load from it to get the boolean value.
13896ccb690SBalazs Benics     if (!isa<Loc>(BooleanArgVal))
1398beac285SDevin Coughlin       return;
1408beac285SDevin Coughlin     BooleanArgVal = C.getState()->getSVal(BooleanArgVal.castAs<Loc>());
1418beac285SDevin Coughlin   }
1428beac285SDevin Coughlin 
1438beac285SDevin Coughlin   SVal ThisVal = Call->getCXXThisVal();
1448beac285SDevin Coughlin 
1458beac285SDevin Coughlin   SVal ThisSuccess = getAssertionResultSuccessFieldValue(
1468beac285SDevin Coughlin       Call->getDecl()->getParent(), ThisVal, State);
1478beac285SDevin Coughlin 
1488beac285SDevin Coughlin   State = assumeValuesEqual(ThisSuccess, BooleanArgVal, State, C);
1498beac285SDevin Coughlin   C.addTransition(State);
1508beac285SDevin Coughlin }
1518beac285SDevin Coughlin 
1528beac285SDevin Coughlin /// Model a call to an un-inlined AssertionResult copy constructor:
1538beac285SDevin Coughlin ///
1548beac285SDevin Coughlin ///   AssertionResult(const &AssertionResult other)
1558beac285SDevin Coughlin ///
1568beac285SDevin Coughlin /// To do so, constrain the value of the newly-constructed instance's
1578beac285SDevin Coughlin /// 'success_' field to be equal to the value of the pass-in instance's
1588beac285SDevin Coughlin /// 'success_' field.
modelAssertionResultCopyConstructor(const CXXConstructorCall * Call,CheckerContext & C) const1598beac285SDevin Coughlin void GTestChecker::modelAssertionResultCopyConstructor(
1608beac285SDevin Coughlin     const CXXConstructorCall *Call, CheckerContext &C) const {
161e17f6215SDevin Coughlin   assert(Call->getNumArgs() == 1);
1628beac285SDevin Coughlin 
16356939f7eSHiroshi Inoue   // The first parameter of the copy constructor must be the other
1648beac285SDevin Coughlin   // instance to initialize this instances fields from.
1658beac285SDevin Coughlin   SVal OtherVal = Call->getArgSVal(0);
1668beac285SDevin Coughlin   SVal ThisVal = Call->getCXXThisVal();
1678beac285SDevin Coughlin 
1688beac285SDevin Coughlin   const CXXRecordDecl *AssertResultClassDecl = Call->getDecl()->getParent();
1698beac285SDevin Coughlin   ProgramStateRef State = C.getState();
1708beac285SDevin Coughlin 
1718beac285SDevin Coughlin   SVal ThisSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl,
1728beac285SDevin Coughlin                                                          ThisVal, State);
1738beac285SDevin Coughlin   SVal OtherSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl,
1748beac285SDevin Coughlin                                                           OtherVal, State);
1758beac285SDevin Coughlin 
1768beac285SDevin Coughlin   State = assumeValuesEqual(ThisSuccess, OtherSuccess, State, C);
1778beac285SDevin Coughlin   C.addTransition(State);
1788beac285SDevin Coughlin }
1798beac285SDevin Coughlin 
1808beac285SDevin Coughlin /// Model calls to AssertionResult constructors that are not inlined.
checkPostCall(const CallEvent & Call,CheckerContext & C) const1818beac285SDevin Coughlin void GTestChecker::checkPostCall(const CallEvent &Call,
1828beac285SDevin Coughlin                                  CheckerContext &C) const {
1838beac285SDevin Coughlin   /// If the constructor was inlined, there is no need model it.
1848beac285SDevin Coughlin   if (C.wasInlined)
1858beac285SDevin Coughlin     return;
1868beac285SDevin Coughlin 
1878beac285SDevin Coughlin   initIdentifierInfo(C.getASTContext());
1888beac285SDevin Coughlin 
1898beac285SDevin Coughlin   auto *CtorCall = dyn_cast<CXXConstructorCall>(&Call);
1908beac285SDevin Coughlin   if (!CtorCall)
1918beac285SDevin Coughlin     return;
1928beac285SDevin Coughlin 
1938beac285SDevin Coughlin   const CXXConstructorDecl *CtorDecl = CtorCall->getDecl();
1948beac285SDevin Coughlin   const CXXRecordDecl *CtorParent = CtorDecl->getParent();
1958beac285SDevin Coughlin   if (CtorParent->getIdentifier() != AssertionResultII)
1968beac285SDevin Coughlin     return;
1978beac285SDevin Coughlin 
198e17f6215SDevin Coughlin   unsigned ParamCount = CtorDecl->getNumParams();
1998beac285SDevin Coughlin 
200e17f6215SDevin Coughlin   // Call the appropriate modeling method based the parameters and their
201e17f6215SDevin Coughlin   // types.
2028beac285SDevin Coughlin 
203e17f6215SDevin Coughlin   // We have AssertionResult(const &AssertionResult)
204e17f6215SDevin Coughlin   if (CtorDecl->isCopyConstructor() && ParamCount == 1) {
2058beac285SDevin Coughlin     modelAssertionResultCopyConstructor(CtorCall, C);
206e17f6215SDevin Coughlin     return;
207e17f6215SDevin Coughlin   }
208e17f6215SDevin Coughlin 
209e17f6215SDevin Coughlin   // There are two possible boolean constructors, depending on which
210e17f6215SDevin Coughlin   // version of gtest is being used:
211e17f6215SDevin Coughlin   //
212e17f6215SDevin Coughlin   // v1.7 and earlier:
213e17f6215SDevin Coughlin   //      AssertionResult(bool success)
214e17f6215SDevin Coughlin   //
215e17f6215SDevin Coughlin   // v1.8 and greater:
216e17f6215SDevin Coughlin   //      template <typename T>
217e17f6215SDevin Coughlin   //      AssertionResult(const T& success,
218e17f6215SDevin Coughlin   //                      typename internal::EnableIf<
219e17f6215SDevin Coughlin   //                          !internal::ImplicitlyConvertible<T,
220e17f6215SDevin Coughlin   //                              AssertionResult>::value>::type*)
221e17f6215SDevin Coughlin   //
222e17f6215SDevin Coughlin   CanQualType BoolTy = C.getASTContext().BoolTy;
223e17f6215SDevin Coughlin   if (ParamCount == 1 && CtorDecl->getParamDecl(0)->getType() == BoolTy) {
224e17f6215SDevin Coughlin     // We have AssertionResult(bool)
225e17f6215SDevin Coughlin     modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/false, C);
226e17f6215SDevin Coughlin     return;
227e17f6215SDevin Coughlin   }
228e17f6215SDevin Coughlin   if (ParamCount == 2){
229e17f6215SDevin Coughlin     auto *RefTy = CtorDecl->getParamDecl(0)->getType()->getAs<ReferenceType>();
230e17f6215SDevin Coughlin     if (RefTy &&
231e17f6215SDevin Coughlin         RefTy->getPointeeType()->getCanonicalTypeUnqualified() == BoolTy) {
232e17f6215SDevin Coughlin       // We have AssertionResult(bool &, ...)
233e17f6215SDevin Coughlin       modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/true, C);
234e17f6215SDevin Coughlin       return;
235e17f6215SDevin Coughlin     }
2368beac285SDevin Coughlin   }
2378beac285SDevin Coughlin }
2388beac285SDevin Coughlin 
initIdentifierInfo(ASTContext & Ctx) const2398beac285SDevin Coughlin void GTestChecker::initIdentifierInfo(ASTContext &Ctx) const {
2408beac285SDevin Coughlin   if (AssertionResultII)
2418beac285SDevin Coughlin     return;
2428beac285SDevin Coughlin 
2438beac285SDevin Coughlin   AssertionResultII = &Ctx.Idents.get("AssertionResult");
2448beac285SDevin Coughlin   SuccessII = &Ctx.Idents.get("success_");
2458beac285SDevin Coughlin }
2468beac285SDevin Coughlin 
2478beac285SDevin Coughlin /// Returns the value stored in the 'success_' field of the passed-in
2488beac285SDevin Coughlin /// AssertionResult instance.
getAssertionResultSuccessFieldValue(const CXXRecordDecl * AssertionResultDecl,SVal Instance,ProgramStateRef State) const2498beac285SDevin Coughlin SVal GTestChecker::getAssertionResultSuccessFieldValue(
2508beac285SDevin Coughlin     const CXXRecordDecl *AssertionResultDecl, SVal Instance,
2518beac285SDevin Coughlin     ProgramStateRef State) const {
2528beac285SDevin Coughlin 
2538beac285SDevin Coughlin   DeclContext::lookup_result Result = AssertionResultDecl->lookup(SuccessII);
2548beac285SDevin Coughlin   if (Result.empty())
2558beac285SDevin Coughlin     return UnknownVal();
2568beac285SDevin Coughlin 
2578beac285SDevin Coughlin   auto *SuccessField = dyn_cast<FieldDecl>(Result.front());
2588beac285SDevin Coughlin   if (!SuccessField)
2598beac285SDevin Coughlin     return UnknownVal();
2608beac285SDevin Coughlin 
2618beac285SDevin Coughlin   Optional<Loc> FieldLoc =
2628beac285SDevin Coughlin       State->getLValue(SuccessField, Instance).getAs<Loc>();
263452db157SKazu Hirata   if (!FieldLoc)
2648beac285SDevin Coughlin     return UnknownVal();
2658beac285SDevin Coughlin 
2668beac285SDevin Coughlin   return State->getSVal(*FieldLoc);
2678beac285SDevin Coughlin }
2688beac285SDevin Coughlin 
2698beac285SDevin Coughlin /// Constrain the passed-in state to assume two values are equal.
assumeValuesEqual(SVal Val1,SVal Val2,ProgramStateRef State,CheckerContext & C)2708beac285SDevin Coughlin ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2,
2718beac285SDevin Coughlin                                                 ProgramStateRef State,
2728beac285SDevin Coughlin                                                 CheckerContext &C) {
27396ccb690SBalazs Benics   auto DVal1 = Val1.getAs<DefinedOrUnknownSVal>();
27496ccb690SBalazs Benics   auto DVal2 = Val2.getAs<DefinedOrUnknownSVal>();
275*97afce08SKazu Hirata   if (!DVal1 || !DVal2)
2768beac285SDevin Coughlin     return State;
2778beac285SDevin Coughlin 
2788beac285SDevin Coughlin   auto ValuesEqual =
27996ccb690SBalazs Benics       C.getSValBuilder().evalEQ(State, *DVal1, *DVal2).getAs<DefinedSVal>();
280*97afce08SKazu Hirata   if (!ValuesEqual)
2818beac285SDevin Coughlin     return State;
2828beac285SDevin Coughlin 
28396ccb690SBalazs Benics   State = C.getConstraintManager().assume(State, *ValuesEqual, true);
2848beac285SDevin Coughlin   return State;
2858beac285SDevin Coughlin }
2868beac285SDevin Coughlin 
registerGTestChecker(CheckerManager & Mgr)2878beac285SDevin Coughlin void ento::registerGTestChecker(CheckerManager &Mgr) {
288058a7a45SKristof Umann   Mgr.registerChecker<GTestChecker>();
289058a7a45SKristof Umann }
290058a7a45SKristof Umann 
shouldRegisterGTestChecker(const CheckerManager & mgr)291bda3dd0dSKirstóf Umann bool ento::shouldRegisterGTestChecker(const CheckerManager &mgr) {
2928beac285SDevin Coughlin   // gtest is a C++ API so there is no sense running the checker
2938beac285SDevin Coughlin   // if not compiling for C++.
294bda3dd0dSKirstóf Umann   const LangOptions &LO = mgr.getLangOpts();
295058a7a45SKristof Umann   return LO.CPlusPlus;
2968beac285SDevin Coughlin }
297