Coverage Report

Created: 2019-07-24 05:18

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