GTestChecker.cpp 11 KB

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