10b57cec5SDimitry Andric //==- GTestChecker.cpp - Model gtest API --*- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This checker models the behavior of un-inlined APIs from the gtest
100b57cec5SDimitry Andric // unit-testing library to avoid false positives when using assertions from
110b57cec5SDimitry Andric // that library.
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
160b57cec5SDimitry Andric #include "clang/AST/Expr.h"
170b57cec5SDimitry Andric #include "clang/Basic/LangOptions.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
220b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
23bdd1243dSDimitry Andric #include <optional>
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace clang;
260b57cec5SDimitry Andric using namespace ento;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric // Modeling of un-inlined AssertionResult constructors
290b57cec5SDimitry Andric //
300b57cec5SDimitry Andric // The gtest unit testing API provides macros for assertions that expand
310b57cec5SDimitry Andric // into an if statement that calls a series of constructors and returns
320b57cec5SDimitry Andric // when the "assertion" is false.
330b57cec5SDimitry Andric //
340b57cec5SDimitry Andric // For example,
350b57cec5SDimitry Andric //
360b57cec5SDimitry Andric //   ASSERT_TRUE(a == b)
370b57cec5SDimitry Andric //
380b57cec5SDimitry Andric // expands into:
390b57cec5SDimitry Andric //
400b57cec5SDimitry Andric //   switch (0)
410b57cec5SDimitry Andric //   case 0:
420b57cec5SDimitry Andric //   default:
430b57cec5SDimitry Andric //     if (const ::testing::AssertionResult gtest_ar_ =
440b57cec5SDimitry Andric //             ::testing::AssertionResult((a == b)))
450b57cec5SDimitry Andric //       ;
460b57cec5SDimitry Andric //     else
470b57cec5SDimitry Andric //       return ::testing::internal::AssertHelper(
480b57cec5SDimitry Andric //                  ::testing::TestPartResult::kFatalFailure,
490b57cec5SDimitry Andric //                  "<path to project>",
500b57cec5SDimitry Andric //                  <line number>,
510b57cec5SDimitry Andric //                  ::testing::internal::GetBoolAssertionFailureMessage(
520b57cec5SDimitry Andric //                      gtest_ar_, "a == b", "false", "true")
530b57cec5SDimitry Andric //                      .c_str()) = ::testing::Message();
540b57cec5SDimitry Andric //
550b57cec5SDimitry Andric // where AssertionResult is defined similarly to
560b57cec5SDimitry Andric //
570b57cec5SDimitry Andric //   class AssertionResult {
580b57cec5SDimitry Andric //   public:
590b57cec5SDimitry Andric //     AssertionResult(const AssertionResult& other);
600b57cec5SDimitry Andric //     explicit AssertionResult(bool success) : success_(success) {}
610b57cec5SDimitry Andric //     operator bool() const { return success_; }
620b57cec5SDimitry Andric //     ...
630b57cec5SDimitry Andric //     private:
640b57cec5SDimitry Andric //     bool success_;
650b57cec5SDimitry Andric //   };
660b57cec5SDimitry Andric //
670b57cec5SDimitry Andric // In order for the analyzer to correctly handle this assertion, it needs to
680b57cec5SDimitry Andric // know that the boolean value of the expression "a == b" is stored the
690b57cec5SDimitry Andric // 'success_' field of the original AssertionResult temporary and propagated
700b57cec5SDimitry Andric // (via the copy constructor) into the 'success_' field of the object stored
710b57cec5SDimitry Andric // in 'gtest_ar_'.  That boolean value will then be returned from the bool
720b57cec5SDimitry Andric // conversion method in the if statement. This guarantees that the assertion
730b57cec5SDimitry Andric // holds when the return path is not taken.
740b57cec5SDimitry Andric //
750b57cec5SDimitry Andric // If the success value is not properly propagated, then the eager case split
760b57cec5SDimitry Andric // on evaluating the expression can cause pernicious false positives
770b57cec5SDimitry Andric // on the non-return path:
780b57cec5SDimitry Andric //
790b57cec5SDimitry Andric //   ASSERT(ptr != NULL)
800b57cec5SDimitry Andric //   *ptr = 7; // False positive null pointer dereference here
810b57cec5SDimitry Andric //
820b57cec5SDimitry Andric // Unfortunately, the bool constructor cannot be inlined (because its
830b57cec5SDimitry Andric // implementation is not present in the headers) and the copy constructor is
840b57cec5SDimitry Andric // not inlined (because it is constructed into a temporary and the analyzer
850b57cec5SDimitry Andric // does not inline these since it does not yet reliably call temporary
860b57cec5SDimitry Andric // destructors).
870b57cec5SDimitry Andric //
880b57cec5SDimitry Andric // This checker compensates for the missing inlining by propagating the
890b57cec5SDimitry Andric // _success value across the bool and copy constructors so the assertion behaves
900b57cec5SDimitry Andric // as expected.
910b57cec5SDimitry Andric 
920b57cec5SDimitry Andric namespace {
930b57cec5SDimitry Andric class GTestChecker : public Checker<check::PostCall> {
940b57cec5SDimitry Andric 
955f757f3fSDimitry Andric   mutable IdentifierInfo *AssertionResultII = nullptr;
965f757f3fSDimitry Andric   mutable IdentifierInfo *SuccessII = nullptr;
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric public:
995f757f3fSDimitry Andric   GTestChecker() = default;
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric private:
1040b57cec5SDimitry Andric   void modelAssertionResultBoolConstructor(const CXXConstructorCall *Call,
1050b57cec5SDimitry Andric                                            bool IsRef, CheckerContext &C) const;
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric   void modelAssertionResultCopyConstructor(const CXXConstructorCall *Call,
1080b57cec5SDimitry Andric                                            CheckerContext &C) const;
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric   void initIdentifierInfo(ASTContext &Ctx) const;
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric   SVal
1130b57cec5SDimitry Andric   getAssertionResultSuccessFieldValue(const CXXRecordDecl *AssertionResultDecl,
1140b57cec5SDimitry Andric                                       SVal Instance,
1150b57cec5SDimitry Andric                                       ProgramStateRef State) const;
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric   static ProgramStateRef assumeValuesEqual(SVal Val1, SVal Val2,
1180b57cec5SDimitry Andric                                            ProgramStateRef State,
1190b57cec5SDimitry Andric                                            CheckerContext &C);
1200b57cec5SDimitry Andric };
1210b57cec5SDimitry Andric } // End anonymous namespace.
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric /// Model a call to an un-inlined AssertionResult(bool) or
1240b57cec5SDimitry Andric /// AssertionResult(bool &, ...).
1250b57cec5SDimitry Andric /// To do so, constrain the value of the newly-constructed instance's 'success_'
1260b57cec5SDimitry Andric /// field to be equal to the passed-in boolean value.
1270b57cec5SDimitry Andric ///
1280b57cec5SDimitry Andric /// \param IsRef Whether the boolean parameter is a reference or not.
modelAssertionResultBoolConstructor(const CXXConstructorCall * Call,bool IsRef,CheckerContext & C) const1290b57cec5SDimitry Andric void GTestChecker::modelAssertionResultBoolConstructor(
1300b57cec5SDimitry Andric     const CXXConstructorCall *Call, bool IsRef, CheckerContext &C) const {
1310b57cec5SDimitry Andric   assert(Call->getNumArgs() >= 1 && Call->getNumArgs() <= 2);
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
1340b57cec5SDimitry Andric   SVal BooleanArgVal = Call->getArgSVal(0);
1350b57cec5SDimitry Andric   if (IsRef) {
1360b57cec5SDimitry Andric     // The argument is a reference, so load from it to get the boolean value.
13781ad6265SDimitry Andric     if (!isa<Loc>(BooleanArgVal))
1380b57cec5SDimitry Andric       return;
1390b57cec5SDimitry Andric     BooleanArgVal = C.getState()->getSVal(BooleanArgVal.castAs<Loc>());
1400b57cec5SDimitry Andric   }
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric   SVal ThisVal = Call->getCXXThisVal();
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric   SVal ThisSuccess = getAssertionResultSuccessFieldValue(
1450b57cec5SDimitry Andric       Call->getDecl()->getParent(), ThisVal, State);
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric   State = assumeValuesEqual(ThisSuccess, BooleanArgVal, State, C);
1480b57cec5SDimitry Andric   C.addTransition(State);
1490b57cec5SDimitry Andric }
1500b57cec5SDimitry Andric 
1510b57cec5SDimitry Andric /// Model a call to an un-inlined AssertionResult copy constructor:
1520b57cec5SDimitry Andric ///
1530b57cec5SDimitry Andric ///   AssertionResult(const &AssertionResult other)
1540b57cec5SDimitry Andric ///
1550b57cec5SDimitry Andric /// To do so, constrain the value of the newly-constructed instance's
1560b57cec5SDimitry Andric /// 'success_' field to be equal to the value of the pass-in instance's
1570b57cec5SDimitry Andric /// 'success_' field.
modelAssertionResultCopyConstructor(const CXXConstructorCall * Call,CheckerContext & C) const1580b57cec5SDimitry Andric void GTestChecker::modelAssertionResultCopyConstructor(
1590b57cec5SDimitry Andric     const CXXConstructorCall *Call, CheckerContext &C) const {
1600b57cec5SDimitry Andric   assert(Call->getNumArgs() == 1);
1610b57cec5SDimitry Andric 
1620b57cec5SDimitry Andric   // The first parameter of the copy constructor must be the other
1630b57cec5SDimitry Andric   // instance to initialize this instances fields from.
1640b57cec5SDimitry Andric   SVal OtherVal = Call->getArgSVal(0);
1650b57cec5SDimitry Andric   SVal ThisVal = Call->getCXXThisVal();
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric   const CXXRecordDecl *AssertResultClassDecl = Call->getDecl()->getParent();
1680b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric   SVal ThisSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl,
1710b57cec5SDimitry Andric                                                          ThisVal, State);
1720b57cec5SDimitry Andric   SVal OtherSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl,
1730b57cec5SDimitry Andric                                                           OtherVal, State);
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric   State = assumeValuesEqual(ThisSuccess, OtherSuccess, State, C);
1760b57cec5SDimitry Andric   C.addTransition(State);
1770b57cec5SDimitry Andric }
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric /// Model calls to AssertionResult constructors that are not inlined.
checkPostCall(const CallEvent & Call,CheckerContext & C) const1800b57cec5SDimitry Andric void GTestChecker::checkPostCall(const CallEvent &Call,
1810b57cec5SDimitry Andric                                  CheckerContext &C) const {
1820b57cec5SDimitry Andric   /// If the constructor was inlined, there is no need model it.
1830b57cec5SDimitry Andric   if (C.wasInlined)
1840b57cec5SDimitry Andric     return;
1850b57cec5SDimitry Andric 
1860b57cec5SDimitry Andric   initIdentifierInfo(C.getASTContext());
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   auto *CtorCall = dyn_cast<CXXConstructorCall>(&Call);
1890b57cec5SDimitry Andric   if (!CtorCall)
1900b57cec5SDimitry Andric     return;
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric   const CXXConstructorDecl *CtorDecl = CtorCall->getDecl();
1930b57cec5SDimitry Andric   const CXXRecordDecl *CtorParent = CtorDecl->getParent();
1940b57cec5SDimitry Andric   if (CtorParent->getIdentifier() != AssertionResultII)
1950b57cec5SDimitry Andric     return;
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric   unsigned ParamCount = CtorDecl->getNumParams();
1980b57cec5SDimitry Andric 
1990b57cec5SDimitry Andric   // Call the appropriate modeling method based the parameters and their
2000b57cec5SDimitry Andric   // types.
2010b57cec5SDimitry Andric 
2020b57cec5SDimitry Andric   // We have AssertionResult(const &AssertionResult)
2030b57cec5SDimitry Andric   if (CtorDecl->isCopyConstructor() && ParamCount == 1) {
2040b57cec5SDimitry Andric     modelAssertionResultCopyConstructor(CtorCall, C);
2050b57cec5SDimitry Andric     return;
2060b57cec5SDimitry Andric   }
2070b57cec5SDimitry Andric 
2080b57cec5SDimitry Andric   // There are two possible boolean constructors, depending on which
2090b57cec5SDimitry Andric   // version of gtest is being used:
2100b57cec5SDimitry Andric   //
2110b57cec5SDimitry Andric   // v1.7 and earlier:
2120b57cec5SDimitry Andric   //      AssertionResult(bool success)
2130b57cec5SDimitry Andric   //
2140b57cec5SDimitry Andric   // v1.8 and greater:
2150b57cec5SDimitry Andric   //      template <typename T>
2160b57cec5SDimitry Andric   //      AssertionResult(const T& success,
2170b57cec5SDimitry Andric   //                      typename internal::EnableIf<
2180b57cec5SDimitry Andric   //                          !internal::ImplicitlyConvertible<T,
2190b57cec5SDimitry Andric   //                              AssertionResult>::value>::type*)
2200b57cec5SDimitry Andric   //
2210b57cec5SDimitry Andric   CanQualType BoolTy = C.getASTContext().BoolTy;
2220b57cec5SDimitry Andric   if (ParamCount == 1 && CtorDecl->getParamDecl(0)->getType() == BoolTy) {
2230b57cec5SDimitry Andric     // We have AssertionResult(bool)
2240b57cec5SDimitry Andric     modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/false, C);
2250b57cec5SDimitry Andric     return;
2260b57cec5SDimitry Andric   }
2270b57cec5SDimitry Andric   if (ParamCount == 2){
2280b57cec5SDimitry Andric     auto *RefTy = CtorDecl->getParamDecl(0)->getType()->getAs<ReferenceType>();
2290b57cec5SDimitry Andric     if (RefTy &&
2300b57cec5SDimitry Andric         RefTy->getPointeeType()->getCanonicalTypeUnqualified() == BoolTy) {
2310b57cec5SDimitry Andric       // We have AssertionResult(bool &, ...)
2320b57cec5SDimitry Andric       modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/true, C);
2330b57cec5SDimitry Andric       return;
2340b57cec5SDimitry Andric     }
2350b57cec5SDimitry Andric   }
2360b57cec5SDimitry Andric }
2370b57cec5SDimitry Andric 
initIdentifierInfo(ASTContext & Ctx) const2380b57cec5SDimitry Andric void GTestChecker::initIdentifierInfo(ASTContext &Ctx) const {
2390b57cec5SDimitry Andric   if (AssertionResultII)
2400b57cec5SDimitry Andric     return;
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric   AssertionResultII = &Ctx.Idents.get("AssertionResult");
2430b57cec5SDimitry Andric   SuccessII = &Ctx.Idents.get("success_");
2440b57cec5SDimitry Andric }
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric /// Returns the value stored in the 'success_' field of the passed-in
2470b57cec5SDimitry Andric /// AssertionResult instance.
getAssertionResultSuccessFieldValue(const CXXRecordDecl * AssertionResultDecl,SVal Instance,ProgramStateRef State) const2480b57cec5SDimitry Andric SVal GTestChecker::getAssertionResultSuccessFieldValue(
2490b57cec5SDimitry Andric     const CXXRecordDecl *AssertionResultDecl, SVal Instance,
2500b57cec5SDimitry Andric     ProgramStateRef State) const {
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric   DeclContext::lookup_result Result = AssertionResultDecl->lookup(SuccessII);
2530b57cec5SDimitry Andric   if (Result.empty())
2540b57cec5SDimitry Andric     return UnknownVal();
2550b57cec5SDimitry Andric 
2560b57cec5SDimitry Andric   auto *SuccessField = dyn_cast<FieldDecl>(Result.front());
2570b57cec5SDimitry Andric   if (!SuccessField)
2580b57cec5SDimitry Andric     return UnknownVal();
2590b57cec5SDimitry Andric 
260bdd1243dSDimitry Andric   std::optional<Loc> FieldLoc =
2610b57cec5SDimitry Andric       State->getLValue(SuccessField, Instance).getAs<Loc>();
26281ad6265SDimitry Andric   if (!FieldLoc)
2630b57cec5SDimitry Andric     return UnknownVal();
2640b57cec5SDimitry Andric 
2650b57cec5SDimitry Andric   return State->getSVal(*FieldLoc);
2660b57cec5SDimitry Andric }
2670b57cec5SDimitry Andric 
2680b57cec5SDimitry Andric /// Constrain the passed-in state to assume two values are equal.
assumeValuesEqual(SVal Val1,SVal Val2,ProgramStateRef State,CheckerContext & C)2690b57cec5SDimitry Andric ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2,
2700b57cec5SDimitry Andric                                                 ProgramStateRef State,
2710b57cec5SDimitry Andric                                                 CheckerContext &C) {
27281ad6265SDimitry Andric   auto DVal1 = Val1.getAs<DefinedOrUnknownSVal>();
27381ad6265SDimitry Andric   auto DVal2 = Val2.getAs<DefinedOrUnknownSVal>();
27481ad6265SDimitry Andric   if (!DVal1 || !DVal2)
2750b57cec5SDimitry Andric     return State;
2760b57cec5SDimitry Andric 
2770b57cec5SDimitry Andric   auto ValuesEqual =
27881ad6265SDimitry Andric       C.getSValBuilder().evalEQ(State, *DVal1, *DVal2).getAs<DefinedSVal>();
27981ad6265SDimitry Andric   if (!ValuesEqual)
2800b57cec5SDimitry Andric     return State;
2810b57cec5SDimitry Andric 
28281ad6265SDimitry Andric   State = C.getConstraintManager().assume(State, *ValuesEqual, true);
2830b57cec5SDimitry Andric   return State;
2840b57cec5SDimitry Andric }
2850b57cec5SDimitry Andric 
registerGTestChecker(CheckerManager & Mgr)2860b57cec5SDimitry Andric void ento::registerGTestChecker(CheckerManager &Mgr) {
2870b57cec5SDimitry Andric   Mgr.registerChecker<GTestChecker>();
2880b57cec5SDimitry Andric }
2890b57cec5SDimitry Andric 
shouldRegisterGTestChecker(const CheckerManager & mgr)2905ffd83dbSDimitry Andric bool ento::shouldRegisterGTestChecker(const CheckerManager &mgr) {
2910b57cec5SDimitry Andric   // gtest is a C++ API so there is no sense running the checker
2920b57cec5SDimitry Andric   // if not compiling for C++.
2935ffd83dbSDimitry Andric   const LangOptions &LO = mgr.getLangOpts();
2940b57cec5SDimitry Andric   return LO.CPlusPlus;
2950b57cec5SDimitry Andric }
296