GTestChecker.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. //==- GTestChecker.cpp - Model gtest API --*- C++ -*-==//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This checker models the behavior of un-inlined APIs from the gtest
  10. // unit-testing library to avoid false positives when using assertions from
  11. // that library.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  15. #include "clang/AST/Expr.h"
  16. #include "clang/Basic/LangOptions.h"
  17. #include "clang/StaticAnalyzer/Core/Checker.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
  21. #include "llvm/Support/raw_ostream.h"
  22. #include <optional>
  23. using namespace clang;
  24. using namespace ento;
  25. // Modeling of un-inlined AssertionResult constructors
  26. //
  27. // The gtest unit testing API provides macros for assertions that expand
  28. // into an if statement that calls a series of constructors and returns
  29. // when the "assertion" is false.
  30. //
  31. // For example,
  32. //
  33. // ASSERT_TRUE(a == b)
  34. //
  35. // expands into:
  36. //
  37. // switch (0)
  38. // case 0:
  39. // default:
  40. // if (const ::testing::AssertionResult gtest_ar_ =
  41. // ::testing::AssertionResult((a == b)))
  42. // ;
  43. // else
  44. // return ::testing::internal::AssertHelper(
  45. // ::testing::TestPartResult::kFatalFailure,
  46. // "<path to project>",
  47. // <line number>,
  48. // ::testing::internal::GetBoolAssertionFailureMessage(
  49. // gtest_ar_, "a == b", "false", "true")
  50. // .c_str()) = ::testing::Message();
  51. //
  52. // where AssertionResult is defined similarly to
  53. //
  54. // class AssertionResult {
  55. // public:
  56. // AssertionResult(const AssertionResult& other);
  57. // explicit AssertionResult(bool success) : success_(success) {}
  58. // operator bool() const { return success_; }
  59. // ...
  60. // private:
  61. // bool success_;
  62. // };
  63. //
  64. // In order for the analyzer to correctly handle this assertion, it needs to
  65. // know that the boolean value of the expression "a == b" is stored the
  66. // 'success_' field of the original AssertionResult temporary and propagated
  67. // (via the copy constructor) into the 'success_' field of the object stored
  68. // in 'gtest_ar_'. That boolean value will then be returned from the bool
  69. // conversion method in the if statement. This guarantees that the assertion
  70. // holds when the return path is not taken.
  71. //
  72. // If the success value is not properly propagated, then the eager case split
  73. // on evaluating the expression can cause pernicious false positives
  74. // on the non-return path:
  75. //
  76. // ASSERT(ptr != NULL)
  77. // *ptr = 7; // False positive null pointer dereference here
  78. //
  79. // Unfortunately, the bool constructor cannot be inlined (because its
  80. // implementation is not present in the headers) and the copy constructor is
  81. // not inlined (because it is constructed into a temporary and the analyzer
  82. // does not inline these since it does not yet reliably call temporary
  83. // destructors).
  84. //
  85. // This checker compensates for the missing inlining by propagating the
  86. // _success value across the bool and copy constructors so the assertion behaves
  87. // as expected.
  88. namespace {
  89. class GTestChecker : public Checker<check::PostCall> {
  90. mutable IdentifierInfo *AssertionResultII;
  91. mutable IdentifierInfo *SuccessII;
  92. public:
  93. GTestChecker();
  94. void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  95. private:
  96. void modelAssertionResultBoolConstructor(const CXXConstructorCall *Call,
  97. bool IsRef, CheckerContext &C) const;
  98. void modelAssertionResultCopyConstructor(const CXXConstructorCall *Call,
  99. CheckerContext &C) const;
  100. void initIdentifierInfo(ASTContext &Ctx) const;
  101. SVal
  102. getAssertionResultSuccessFieldValue(const CXXRecordDecl *AssertionResultDecl,
  103. SVal Instance,
  104. ProgramStateRef State) const;
  105. static ProgramStateRef assumeValuesEqual(SVal Val1, SVal Val2,
  106. ProgramStateRef State,
  107. CheckerContext &C);
  108. };
  109. } // End anonymous namespace.
  110. GTestChecker::GTestChecker() : AssertionResultII(nullptr), SuccessII(nullptr) {}
  111. /// Model a call to an un-inlined AssertionResult(bool) or
  112. /// AssertionResult(bool &, ...).
  113. /// To do so, constrain the value of the newly-constructed instance's 'success_'
  114. /// field to be equal to the passed-in boolean value.
  115. ///
  116. /// \param IsRef Whether the boolean parameter is a reference or not.
  117. void GTestChecker::modelAssertionResultBoolConstructor(
  118. const CXXConstructorCall *Call, bool IsRef, CheckerContext &C) const {
  119. assert(Call->getNumArgs() >= 1 && Call->getNumArgs() <= 2);
  120. ProgramStateRef State = C.getState();
  121. SVal BooleanArgVal = Call->getArgSVal(0);
  122. if (IsRef) {
  123. // The argument is a reference, so load from it to get the boolean value.
  124. if (!isa<Loc>(BooleanArgVal))
  125. return;
  126. BooleanArgVal = C.getState()->getSVal(BooleanArgVal.castAs<Loc>());
  127. }
  128. SVal ThisVal = Call->getCXXThisVal();
  129. SVal ThisSuccess = getAssertionResultSuccessFieldValue(
  130. Call->getDecl()->getParent(), ThisVal, State);
  131. State = assumeValuesEqual(ThisSuccess, BooleanArgVal, State, C);
  132. C.addTransition(State);
  133. }
  134. /// Model a call to an un-inlined AssertionResult copy constructor:
  135. ///
  136. /// AssertionResult(const &AssertionResult other)
  137. ///
  138. /// To do so, constrain the value of the newly-constructed instance's
  139. /// 'success_' field to be equal to the value of the pass-in instance's
  140. /// 'success_' field.
  141. void GTestChecker::modelAssertionResultCopyConstructor(
  142. const CXXConstructorCall *Call, CheckerContext &C) const {
  143. assert(Call->getNumArgs() == 1);
  144. // The first parameter of the copy constructor must be the other
  145. // instance to initialize this instances fields from.
  146. SVal OtherVal = Call->getArgSVal(0);
  147. SVal ThisVal = Call->getCXXThisVal();
  148. const CXXRecordDecl *AssertResultClassDecl = Call->getDecl()->getParent();
  149. ProgramStateRef State = C.getState();
  150. SVal ThisSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl,
  151. ThisVal, State);
  152. SVal OtherSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl,
  153. OtherVal, State);
  154. State = assumeValuesEqual(ThisSuccess, OtherSuccess, State, C);
  155. C.addTransition(State);
  156. }
  157. /// Model calls to AssertionResult constructors that are not inlined.
  158. void GTestChecker::checkPostCall(const CallEvent &Call,
  159. CheckerContext &C) const {
  160. /// If the constructor was inlined, there is no need model it.
  161. if (C.wasInlined)
  162. return;
  163. initIdentifierInfo(C.getASTContext());
  164. auto *CtorCall = dyn_cast<CXXConstructorCall>(&Call);
  165. if (!CtorCall)
  166. return;
  167. const CXXConstructorDecl *CtorDecl = CtorCall->getDecl();
  168. const CXXRecordDecl *CtorParent = CtorDecl->getParent();
  169. if (CtorParent->getIdentifier() != AssertionResultII)
  170. return;
  171. unsigned ParamCount = CtorDecl->getNumParams();
  172. // Call the appropriate modeling method based the parameters and their
  173. // types.
  174. // We have AssertionResult(const &AssertionResult)
  175. if (CtorDecl->isCopyConstructor() && ParamCount == 1) {
  176. modelAssertionResultCopyConstructor(CtorCall, C);
  177. return;
  178. }
  179. // There are two possible boolean constructors, depending on which
  180. // version of gtest is being used:
  181. //
  182. // v1.7 and earlier:
  183. // AssertionResult(bool success)
  184. //
  185. // v1.8 and greater:
  186. // template <typename T>
  187. // AssertionResult(const T& success,
  188. // typename internal::EnableIf<
  189. // !internal::ImplicitlyConvertible<T,
  190. // AssertionResult>::value>::type*)
  191. //
  192. CanQualType BoolTy = C.getASTContext().BoolTy;
  193. if (ParamCount == 1 && CtorDecl->getParamDecl(0)->getType() == BoolTy) {
  194. // We have AssertionResult(bool)
  195. modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/false, C);
  196. return;
  197. }
  198. if (ParamCount == 2){
  199. auto *RefTy = CtorDecl->getParamDecl(0)->getType()->getAs<ReferenceType>();
  200. if (RefTy &&
  201. RefTy->getPointeeType()->getCanonicalTypeUnqualified() == BoolTy) {
  202. // We have AssertionResult(bool &, ...)
  203. modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/true, C);
  204. return;
  205. }
  206. }
  207. }
  208. void GTestChecker::initIdentifierInfo(ASTContext &Ctx) const {
  209. if (AssertionResultII)
  210. return;
  211. AssertionResultII = &Ctx.Idents.get("AssertionResult");
  212. SuccessII = &Ctx.Idents.get("success_");
  213. }
  214. /// Returns the value stored in the 'success_' field of the passed-in
  215. /// AssertionResult instance.
  216. SVal GTestChecker::getAssertionResultSuccessFieldValue(
  217. const CXXRecordDecl *AssertionResultDecl, SVal Instance,
  218. ProgramStateRef State) const {
  219. DeclContext::lookup_result Result = AssertionResultDecl->lookup(SuccessII);
  220. if (Result.empty())
  221. return UnknownVal();
  222. auto *SuccessField = dyn_cast<FieldDecl>(Result.front());
  223. if (!SuccessField)
  224. return UnknownVal();
  225. std::optional<Loc> FieldLoc =
  226. State->getLValue(SuccessField, Instance).getAs<Loc>();
  227. if (!FieldLoc)
  228. return UnknownVal();
  229. return State->getSVal(*FieldLoc);
  230. }
  231. /// Constrain the passed-in state to assume two values are equal.
  232. ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2,
  233. ProgramStateRef State,
  234. CheckerContext &C) {
  235. auto DVal1 = Val1.getAs<DefinedOrUnknownSVal>();
  236. auto DVal2 = Val2.getAs<DefinedOrUnknownSVal>();
  237. if (!DVal1 || !DVal2)
  238. return State;
  239. auto ValuesEqual =
  240. C.getSValBuilder().evalEQ(State, *DVal1, *DVal2).getAs<DefinedSVal>();
  241. if (!ValuesEqual)
  242. return State;
  243. State = C.getConstraintManager().assume(State, *ValuesEqual, true);
  244. return State;
  245. }
  246. void ento::registerGTestChecker(CheckerManager &Mgr) {
  247. Mgr.registerChecker<GTestChecker>();
  248. }
  249. bool ento::shouldRegisterGTestChecker(const CheckerManager &mgr) {
  250. // gtest is a C++ API so there is no sense running the checker
  251. // if not compiling for C++.
  252. const LangOptions &LO = mgr.getLangOpts();
  253. return LO.CPlusPlus;
  254. }